aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark de Wever <koraq@xs4all.nl>2024-07-05 19:42:37 +0200
committerMark de Wever <koraq@xs4all.nl>2024-07-05 19:42:37 +0200
commitf40e7afa358082273df8f9f8b167cb12051b120a (patch)
tree5dd9c76ec42c020fe613fa7d7a4db65cf43cdd5d
parent130f0f526dc28ebbe23e5956857e85f7c9b754f5 (diff)
downloadllvm-users/mordante/version_header_generator.zip
llvm-users/mordante/version_header_generator.tar.gz
llvm-users/mordante/version_header_generator.tar.bz2
[libc++] Implements the new version header generator.users/mordante/version_header_generator
The generator makes a few changes to the output - removes the synopsis, it did not really show what was implemented correctly. - the output now is clang-format clean. This code uses the new FTM data structure. Since the contents of this structure are not up-to-date the code is only used in its tests.
-rw-r--r--libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py51
-rw-r--r--libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py2
-rw-r--r--libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py2
-rw-r--r--libcxx/test/libcxx/feature_test_macro/test_data.json7
-rw-r--r--libcxx/test/libcxx/feature_test_macro/version_header.sh.py73
-rw-r--r--libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py114
-rwxr-xr-xlibcxx/utils/generate_feature_test_macro_components.py143
7 files changed, 390 insertions, 2 deletions
diff --git a/libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py b/libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py
new file mode 100644
index 0000000..5d12a68
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py
@@ -0,0 +1,51 @@
+# ===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json
+
+import sys
+
+sys.path.append(sys.argv[1])
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+def test(output, expected):
+ assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
+
+
+ftm = FeatureTestMacros(sys.argv[2])
+test(
+ ftm.ftm_meta_data,
+ {
+ "__cpp_lib_any": {
+ "headers": ["any"],
+ "test_suite_guard": None,
+ "libcxx_guard": None,
+ },
+ "__cpp_lib_barrier": {
+ "headers": ["barrier"],
+ "test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)",
+ "libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
+ },
+ "__cpp_lib_format": {
+ "headers": ["format"],
+ "test_suite_guard": None,
+ "libcxx_guard": None,
+ },
+ "__cpp_lib_parallel_algorithm": {
+ "headers": ["algorithm", "numeric"],
+ "test_suite_guard": None,
+ "libcxx_guard": None,
+ },
+ "__cpp_lib_variant": {
+ "headers": ["variant"],
+ "test_suite_guard": None,
+ "libcxx_guard": None,
+ },
+ },
+)
diff --git a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
index 67353fc..62a3c46 100644
--- a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
@@ -31,7 +31,7 @@ test(
"__cpp_lib_barrier": {
"c++20": "201907L",
"c++23": "201907L",
- "c++26": "201907L",
+ "c++26": "299900L",
},
"__cpp_lib_format": {
"c++20": None,
diff --git a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
index 43c90b1..231e607 100644
--- a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
@@ -31,7 +31,7 @@ test(
"__cpp_lib_barrier": {
"c++20": "201907L",
"c++23": "201907L",
- "c++26": "201907L",
+ "c++26": "299900L",
},
"__cpp_lib_format": {
"c++20": "202110L",
diff --git a/libcxx/test/libcxx/feature_test_macro/test_data.json b/libcxx/test/libcxx/feature_test_macro/test_data.json
index 1f8bbe5..18c8836 100644
--- a/libcxx/test/libcxx/feature_test_macro/test_data.json
+++ b/libcxx/test/libcxx/feature_test_macro/test_data.json
@@ -23,6 +23,13 @@
"implemented": true
}
]
+ },
+ "c++26": {
+ "299900": [
+ {
+ "implemented": true
+ }
+ ]
}
},
"headers": [
diff --git a/libcxx/test/libcxx/feature_test_macro/version_header.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
new file mode 100644
index 0000000..bbb8633
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
@@ -0,0 +1,73 @@
+# ===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json
+
+import sys
+
+sys.path.append(sys.argv[1])
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+def test(output, expected):
+ assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
+
+
+ftm = FeatureTestMacros(sys.argv[2])
+test(
+ ftm.version_header,
+ """// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_VERSION
+#define _LIBCPP_VERSION
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 17
+# define __cpp_lib_any 201606L
+# define __cpp_lib_parallel_algorithm 201603L
+# define __cpp_lib_variant 202102L
+#endif // _LIBCPP_STD_VER >= 17
+
+#if _LIBCPP_STD_VER >= 20
+# if !defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC
+# define __cpp_lib_barrier 201907L
+# endif
+// define __cpp_lib_format 202110L
+# undef __cpp_lib_variant
+# define __cpp_lib_variant 202106L
+#endif // _LIBCPP_STD_VER >= 20
+
+#if _LIBCPP_STD_VER >= 23
+// define __cpp_lib_format 202207L
+#endif // _LIBCPP_STD_VER >= 23
+
+#if _LIBCPP_STD_VER >= 26
+# if !defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC
+# undef __cpp_lib_barrier
+# define __cpp_lib_barrier 299900L
+# endif
+// define __cpp_lib_format 202311L
+# undef __cpp_lib_variant
+# define __cpp_lib_variant 202306L
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP_VERSION
+""",
+)
diff --git a/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py
new file mode 100644
index 0000000..db90206
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py
@@ -0,0 +1,114 @@
+# ===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json
+
+import sys
+
+sys.path.append(sys.argv[1])
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+def test(output, expected):
+ assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
+
+
+ftm = FeatureTestMacros(sys.argv[2])
+test(
+ ftm.version_header_implementation,
+ {
+ "17": [
+ {
+ "__cpp_lib_any": {
+ "value": "201606L",
+ "implemented": True,
+ "need_undef": False,
+ "condition": None,
+ },
+ },
+ {
+ "__cpp_lib_parallel_algorithm": {
+ "value": "201603L",
+ "implemented": True,
+ "need_undef": False,
+ "condition": None,
+ },
+ },
+ {
+ "__cpp_lib_variant": {
+ "value": "202102L",
+ "implemented": True,
+ "need_undef": False,
+ "condition": None,
+ },
+ },
+ ],
+ "20": [
+ {
+ "__cpp_lib_barrier": {
+ "value": "201907L",
+ "implemented": True,
+ "need_undef": False,
+ "condition": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
+ },
+ },
+ {
+ "__cpp_lib_format": {
+ "value": "202110L",
+ "implemented": False,
+ "need_undef": False,
+ "condition": None,
+ },
+ },
+ {
+ "__cpp_lib_variant": {
+ "value": "202106L",
+ "implemented": True,
+ "need_undef": True,
+ "condition": None,
+ },
+ },
+ ],
+ "23": [
+ {
+ "__cpp_lib_format": {
+ "value": "202207L",
+ "implemented": False,
+ "need_undef": False,
+ "condition": None,
+ },
+ },
+ ],
+ "26": [
+ {
+ "__cpp_lib_barrier": {
+ "value": "299900L",
+ "implemented": True,
+ "need_undef": True,
+ "condition": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
+ },
+ },
+ {
+ "__cpp_lib_format": {
+ "value": "202311L",
+ "implemented": False,
+ "need_undef": False,
+ "condition": None,
+ },
+ },
+ {
+ "__cpp_lib_variant": {
+ "value": "202306L",
+ "implemented": True,
+ "need_undef": True,
+ "condition": None,
+ },
+ },
+ ],
+ },
+)
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 3f8ecc2..a683ac3 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1909,6 +1909,55 @@ def get_ftms(
return result
+def generate_version_header_dialect_block(data: Dict[str, Any]) -> str:
+ """Generates the contents of the version header for a dialect.
+
+ This generates the contents of a
+ #if _LIBCPP_STD_VER >= XY
+ #endif // _LIBCPP_STD_VER >= XY
+ block.
+ """
+ result = ""
+ for element in data:
+ for ftm, entry in element.items():
+ if not entry["implemented"]:
+ # When a FTM is not implemented don't add the guards
+ # or undefine the (possibly) defined macro.
+ result += f'// define {ftm} {entry["value"]}\n'
+ else:
+ need_undef = entry["need_undef"]
+ if entry["condition"]:
+ result += f'# if {entry["condition"]}\n'
+ if entry["need_undef"]:
+ result += f"# undef {ftm}\n"
+ result += f'# define {ftm} {entry["value"]}\n'
+ result += f"# endif\n"
+ else:
+ if entry["need_undef"]:
+ result += f"# undef {ftm}\n"
+ result += f'# define {ftm} {entry["value"]}\n'
+
+ return result
+
+
+def generate_version_header_implementation(data: Dict[str, Dict[str, Any]]) -> str:
+ """Generates the body of the version header."""
+
+ template = """#if _LIBCPP_STD_VER >= {dialect}
+{feature_test_macros}#endif // _LIBCPP_STD_VER >= {dialect}"""
+
+ result = []
+ for std, ftms in data.items():
+ result.append(
+ template.format(
+ dialect=std,
+ feature_test_macros=generate_version_header_dialect_block(ftms),
+ )
+ )
+
+ return "\n\n".join(result)
+
+
class FeatureTestMacros:
"""Provides all feature-test macro (FTM) output components.
@@ -2060,12 +2109,106 @@ class FeatureTestMacros:
return get_ftms(self.__data, self.std_dialects, True)
+ @functools.cached_property
+ def ftm_meta_data(self) -> Dict[str, Dict[str, Any]]:
+ """Returns the meta data of the FTMs defined in the Standard.
+
+ The meta data does not depend on the C++ dialect used.
+ The result is a dict with the following contents:
+ - key: Name of the feature test macro.
+ - value: A dict with the following content:
+ * headers: The list of headers that should provide the FTM
+ * test_suite_guard: The condtion for testing the FTM in the test suite.
+ * test_suite_guard: The condtion for testing the FTM in the version header.
+ """
+ result = dict()
+ for feature in self.__data:
+ entry = dict()
+ entry["headers"] = feature["headers"]
+ entry["test_suite_guard"] = feature.get("test_suite_guard", None)
+ entry["libcxx_guard"] = feature.get("libcxx_guard", None)
+ result[feature["name"]] = entry
+
+ return result
+
+ @property
+ def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]:
+ """Generates the body of the version header."""
+ result = dict()
+ for std in self.std_dialects:
+ result[get_std_number(std)] = list()
+
+ for ftm, values in self.standard_ftms.items():
+ need_undef = False
+ last_value = None
+ for std, value in values.items():
+ # When a newer Standard does not change the value of the macro
+ # there is no need to redefine it with the same value.
+ if last_value and value == last_value:
+ continue
+ last_value = value
+
+ entry = dict()
+ entry["value"] = value
+ entry["implemented"] = self.implemented_ftms[ftm][std] != None
+ entry["need_undef"] = need_undef
+ entry["condition"] = self.ftm_meta_data[ftm]["libcxx_guard"]
+
+ need_undef = entry["implemented"]
+
+ result[get_std_number(std)].append(dict({ftm: entry}))
+
+ return result
+
+ @property
+ def version_header(self) -> str:
+ """Generates the version header."""
+ template = """// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_VERSION
+#define _LIBCPP_VERSION
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+{feature_test_macros}
+
+#endif // _LIBCPP_VERSION
+"""
+ return template.format(
+ feature_test_macros=generate_version_header_implementation(
+ self.version_header_implementation
+ )
+ )
+
def main():
produce_version_header()
produce_tests()
produce_docs()
+ # Example how to use the new version header generation function to generate
+ # the file.
+ if False:
+ ftm = FeatureTestMacros(
+ os.path.join(
+ source_root, "test", "libcxx", "feature_test_macro", "test_data.json"
+ )
+ )
+ version_header_path = os.path.join(include_path, "version")
+ with open(version_header_path, "w", newline="\n") as f:
+ f.write(ftm.version_header)
+
if __name__ == "__main__":
main()