Files
yoga/enums.py
Nick Gerleman a43754266a <bit> and <concepts> (#1497)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1497

The lowest common denominator we have had for Yoga has been Clang 12 + MSVC 2017 stdlib. This has allowed Yoga to use C++ 20 language features, but not library features. React Native for mobile has not been bound to this restriction.

Builds using that toolchain are being updated to latest MSVC 2019 stdlib (which has good C++ 20 library support), along with Clang 17 (or maybe a stop at 15) pending projects using `-fcoroutines-ts` being migrated to C++ 20.

This tests out some C++ 20 standard library usages against the current Clang 12 + MSVC 2019 stdlib toolchain that didn't work before, and adds a couple concepts for better constraints/compiler error messages if misused.

This bumps min-tested XCode (and minimum required) version to 14.3, matching a similar change for React Native. This should probably be bumped to 15 sometime before Apple starts requiring 15+ to go out to the iOS app store.

We are approaching a practical support range of:
1. XCode >= 14.3
2. NDK >= 26
3. Clang/libc++ >= 14
4. GCC/libstdc++ >= 11
5. MSVC >= 16.11 (VS 2019)

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D51604487

fbshipit-source-id: d394d0d86672b69781b8ae071d87adcf944ddc72
2023-12-12 08:52:11 -08:00

293 lines
9.9 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import math
import os
ENUMS = {
"Direction": ["Inherit", "LTR", "RTL"],
"Unit": ["Undefined", "Point", "Percent", "Auto"],
"FlexDirection": ["Column", "ColumnReverse", "Row", "RowReverse"],
"Justify": [
"FlexStart",
"Center",
"FlexEnd",
"SpaceBetween",
"SpaceAround",
"SpaceEvenly",
],
"Overflow": ["Visible", "Hidden", "Scroll"],
"Align": [
"Auto",
"FlexStart",
"Center",
"FlexEnd",
"Stretch",
"Baseline",
"SpaceBetween",
"SpaceAround",
"SpaceEvenly",
],
"PositionType": ["Static", "Relative", "Absolute"],
"Display": ["Flex", "None"],
"Wrap": ["NoWrap", "Wrap", "WrapReverse"],
"MeasureMode": ["Undefined", "Exactly", "AtMost"],
"Dimension": ["Width", "Height"],
"Edge": [
"Left",
"Top",
"Right",
"Bottom",
"Start",
"End",
"Horizontal",
"Vertical",
"All",
],
"NodeType": ["Default", "Text"],
"LogLevel": ["Error", "Warn", "Info", "Debug", "Verbose", "Fatal"],
"ExperimentalFeature": [
# Mimic web flex-basis behavior (experiment may be broken)
"WebFlexBasis",
# Conformance fix: https://github.com/facebook/yoga/pull/1028
"AbsolutePercentageAgainstPaddingEdge",
],
"PrintOptions": [
("Layout", 1 << 0),
("Style", 1 << 1),
("Children", 1 << 2),
],
"Gutter": ["Column", "Row", "All"],
# Known incorrect behavior which can be enabled for compatibility
"Errata": [
# Default: Standards conformant mode
("None", 0),
# Allows main-axis flex basis to be stretched without flexGrow being
# set (previously referred to as "UseLegacyStretchBehaviour")
("StretchFlexBasis", 1 << 0),
# Solely uses the flex-direction to determine starting and ending edges
("StartingEndingEdgeFromFlexDirection", 1 << 1),
# Position: static behaves like position: relative within Yoga
("PositionStaticBehavesLikeRelative", 1 << 2),
# Positioning of absolute nodes will have various bugs related to
# justification, alignment, and insets
("AbsolutePositioning", 1 << 3),
# Enable all incorrect behavior (preserve compatibility)
("All", 0x7FFFFFFF),
# Enable all errata except for "StretchFlexBasis" (Defaults behavior
# before Yoga 2.0)
("Classic", 0x7FFFFFFF & (~(1 << 0))),
],
}
DO_NOT_STRIP = ["LogLevel"]
BITSET_ENUMS = ["PrintOptions", "Errata"]
def get_license(ext):
return f"""{"/**" if ext == "js" else "/*"}
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// @{"generated"} by enums.py
{"// clang-format off" if ext == "cpp" else ""}
"""
def _format_name(symbol, delimiter=None, transform=None):
symbol = str(symbol)
out = ""
for i in range(0, len(symbol)):
c = symbol[i]
if str.istitle(c) and i != 0 and not str.istitle(symbol[i - 1]):
out += delimiter or ""
if transform is None:
out += c
else:
out += getattr(c, transform)()
return out
def to_java_upper(symbol):
return _format_name(symbol, "_", "upper")
def to_hyphenated_lower(symbol):
return _format_name(symbol, "-", "lower")
root = os.path.dirname(os.path.abspath(__file__))
# write out C & Objective-C headers
with open(root + "/yoga/YGEnums.h", "w") as f:
f.write(get_license("cpp"))
f.write("#pragma once\n")
f.write("#include <yoga/YGMacros.h>\n\n")
f.write("YG_EXTERN_C_BEGIN\n\n")
items = sorted(ENUMS.items())
for name, values in items:
f.write("YG_ENUM_DECL(\n")
f.write(" YG%s,\n" % name)
for value in values:
if isinstance(value, tuple):
f.write(" YG%s%s = %d" % (name, value[0], value[1]))
else:
f.write(" YG%s%s" % (name, value))
if value == values[-1]:
f.write(")\n")
else:
f.write(",\n")
if name in BITSET_ENUMS:
f.write("YG_DEFINE_ENUM_FLAG_OPERATORS(YG%s)\n" % name)
f.write("\n")
f.write("YG_EXTERN_C_END\n")
# Write out C++ scoped enums
for name, values in sorted(ENUMS.items()):
with open(f"{root}/yoga/enums/{name}.h", "w") as f:
f.write(get_license("cpp"))
f.write("#pragma once\n\n")
f.write("#include <cstdint>\n")
f.write("#include <yoga/YGEnums.h>\n")
f.write("#include <yoga/enums/YogaEnums.h>\n\n")
f.write("namespace facebook::yoga {\n\n")
width = "uint32_t" if name in BITSET_ENUMS else "uint8_t"
f.write(f"enum class {name} : {width} {{\n")
for value in values:
ordinal = value[0] if isinstance(value, tuple) else value
f.write(f" {ordinal} = YG{name}{ordinal},\n")
f.write("};\n\n")
if name in BITSET_ENUMS:
f.write(f"YG_DEFINE_ENUM_FLAG_OPERATORS({name})\n\n")
else:
f.write("template <>\n")
f.write(f"constexpr int32_t ordinalCount<{name}>() {{\n")
f.write(f" return {len(values)};\n")
f.write("}\n\n")
f.write(f"constexpr {name} scopedEnum(YG{name} unscoped) {{\n")
f.write(f" return static_cast<{name}>(unscoped);\n")
f.write("}\n\n")
f.write(f"constexpr YG{name} unscopedEnum({name} scoped) {{\n")
f.write(f" return static_cast<YG{name}>(scoped);\n")
f.write("}\n\n")
f.write(f"inline const char* toString({name} e) {{\n")
f.write(f" return YG{name}ToString(unscopedEnum(e));\n")
f.write("}\n\n")
f.write("} // namespace facebook::yoga\n")
# write out C body for printing
with open(root + "/yoga/YGEnums.cpp", "w") as f:
f.write(get_license("cpp"))
f.write("#include <yoga/YGEnums.h>\n\n")
items = sorted(ENUMS.items())
for name, values in items:
f.write("const char* YG%sToString(const YG%s value) {\n" % (name, name))
f.write(" switch (value) {\n")
for value in values:
if isinstance(value, tuple):
f.write(" case YG%s%s:\n" % (name, value[0]))
f.write(' return "%s";\n' % to_hyphenated_lower(value[0]))
else:
f.write(" case YG%s%s:\n" % (name, value))
f.write(' return "%s";\n' % to_hyphenated_lower(value))
f.write(" }\n")
f.write(' return "unknown";\n')
f.write("}\n")
if name != items[-1][0]:
f.write("\n")
# write out java files
for name, values in sorted(ENUMS.items()):
with open(root + "/java/com/facebook/yoga/Yoga%s.java" % name, "w") as f:
f.write(get_license("java"))
f.write("package com.facebook.yoga;\n\n")
if name in DO_NOT_STRIP:
f.write("import com.facebook.yoga.annotations.DoNotStrip;\n\n")
f.write("@DoNotStrip\n")
f.write("public enum Yoga%s {\n" % name)
if len(values) > 0:
for value in values:
if isinstance(value, tuple):
f.write(" %s(%d)" % (to_java_upper(value[0]), value[1]))
else:
f.write(" %s(%d)" % (to_java_upper(value), values.index(value)))
if values.index(value) is len(values) - 1:
f.write(";\n")
else:
f.write(",\n")
else:
f.write("__EMPTY(-1);")
f.write("\n")
f.write(" private final int mIntValue;\n")
f.write("\n")
f.write(" Yoga%s(int intValue) {\n" % name)
f.write(" mIntValue = intValue;\n")
f.write(" }\n")
f.write("\n")
f.write(" public int intValue() {\n")
f.write(" return mIntValue;\n")
f.write(" }\n")
f.write("\n")
if name in DO_NOT_STRIP:
f.write(" @DoNotStrip\n")
f.write(" public static Yoga%s fromInt(int value) {\n" % name)
f.write(" switch (value) {\n")
for value in values:
if isinstance(value, tuple):
f.write(
" case %d: return %s;\n" % (value[1], to_java_upper(value[0]))
)
else:
f.write(
" case %d: return %s;\n"
% (values.index(value), to_java_upper(value))
)
f.write(
' default: throw new IllegalArgumentException("Unknown enum value: " + value);\n'
)
f.write(" }\n")
f.write(" }\n")
f.write("}\n")
# write out TypeScript file
with open(root + "/javascript/src/generated/YGEnums.ts", "w") as f:
f.write(get_license("js"))
enums = sorted(ENUMS.items())
for enum_name, ordinals in enums:
f.write(f"export enum {enum_name} {{\n")
for ordinal_index, ordinal in enumerate(ordinals):
ordinal_name = ordinal[0] if isinstance(ordinal, tuple) else ordinal
ordinal_value = ordinal[1] if isinstance(ordinal, tuple) else ordinal_index
f.write(f" {ordinal_name} = {ordinal_value},\n")
f.write("}\n\n")
f.write("const constants = {\n")
for enum_name, ordinals in enums:
for ordinal in ordinals:
ordinal_name = ordinal[0] if isinstance(ordinal, tuple) else ordinal
ordinal_value = ordinal[1] if isinstance(ordinal, tuple) else ordinal_index
f.write(
f" {to_java_upper(enum_name)}_{to_java_upper(ordinal_name)}: {enum_name}.{ordinal_name},\n"
)
f.write("}\n")
f.write("export default constants")