From 59fc9501f147232ae1675b4cc7b16c14eef5624a Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 22:32:35 +0200 Subject: [PATCH 01/41] fix: key PCM and project.so freshness on content, not installed mtimes std.pcm was rebuilt when it was older than std.cppm, and project.so when it was older than the crafter-build executable. Both inputs are installed files, and an OSTree deployment (the Flatpak) gives every installed file mtime 0: std.cppm and the executable always looked older than the cache, so a clang bump would never rebuild std.pcm and a crafter-build update would never recompile project.so. clang crashes on a PCM from another compiler instead of reporting it, so the failure would have been a backtrace on the user's first build after an update. Each artifact now has a one-line stamp beside it. std.pcm's covers the precompile command and the module source's bytes, which also catches a libc++ update under the same clang. project.so's covers the crafter-build version, the toolchain key, the module source stamp and the compile command; project.cpp's and the pre-dependency artifacts' mtimes remain inputs, since those live next to it with real timestamps. The module source stamp stands in for the executable: project.so resolves Crafter:: symbols from the running binary, so only declarations matter, and an implementation-only rebuild of crafter-build no longer recompiles it. The std.pcm stamp folds into the module cache stamp, so a rebuilt std.pcm rebuilds the PCMs that import it. The cache directory carries the toolchain key (---), so a compiler bump starts cold instead of handing the new clang PCMs it will crash on. LoadProject and Build() now derive the directory from the same StdPcmDir. Old - directories are left behind; `clean` never touched the cache and still does not. Co-Authored-By: Claude Fable 5.1 --- implementations/Crafter.Build-Clang.cpp | 13 +- implementations/Crafter.Build-Platform.cpp | 141 ++++++++++++------- project.cpp | 2 + tests/HostCacheToolchainKey/main.cpp | 154 +++++++++++++++++++++ 4 files changed, 255 insertions(+), 55 deletions(-) create mode 100644 tests/HostCacheToolchainKey/main.cpp diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index 7c7f5c0..da2ddc4 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -246,11 +246,14 @@ void Configuration::GetInterfacesAndImplementations(std::span interfac } fs::path Crafter::StdPcmDir(const Configuration& config) { - // The std PCM is cached per target+march, but a wasm variant pass compiles - // it with extra codegen flags (e.g. -mrelaxed-simd) — its BMI must not be - // shared with the baseline's, or the consuming TUs see a target-feature - // mismatch. Suffix the cache dir with the variant flags when present. - std::string stdPcmKey = std::format("{}-{}", config.target, config.march); + // The std PCM is cached per target+march+toolchain: a PCM is only valid + // for the exact clang that wrote it, so a compiler bump starts a cold + // directory instead of handing the new clang a PCM it will crash on. A + // wasm variant pass compiles it with extra codegen flags (e.g. + // -mrelaxed-simd) — its BMI must not be shared with the baseline's, or + // the consuming TUs see a target-feature mismatch. Suffix the cache dir + // with the variant flags when present. + std::string stdPcmKey = std::format("{}-{}-{}", config.target, config.march, GetToolchain().Key()); for (const std::string& f : config.wasmVariantFlags) { stdPcmKey += "+"; for (char c : f) stdPcmKey += (c == '/' || c == '\\') ? '_' : c; diff --git a/implementations/Crafter.Build-Platform.cpp b/implementations/Crafter.Build-Platform.cpp index 4181bb6..a6f8793 100644 --- a/implementations/Crafter.Build-Platform.cpp +++ b/implementations/Crafter.Build-Platform.cpp @@ -90,15 +90,35 @@ namespace { return cacheDir / "crafter-build-sources.stamp"; } - std::string ReadCacheStamp(const fs::path& cacheDir) { - std::ifstream in(CacheStampPath(cacheDir), std::ios::binary); + // A stamp is a one-line content hash kept next to the artifact it + // describes. Written last, after the artifact, so an interrupted build + // leaves a stamp that disagrees and the next run starts over. + std::string ReadStamp(const fs::path& file) { + std::ifstream in(file, std::ios::binary); std::string stamp; std::getline(in, stamp); return stamp; } - void WriteCacheStamp(const fs::path& cacheDir, const std::string& stamp) { - std::ofstream(CacheStampPath(cacheDir), std::ios::binary | std::ios::trunc) << stamp << '\n'; + void WriteStamp(const fs::path& file, std::string_view stamp) { + std::ofstream(file, std::ios::binary | std::ios::trunc) << stamp << '\n'; + } + + fs::path StampPath(const fs::path& artifact) { + fs::path stamp = artifact; + stamp += ".stamp"; + return stamp; + } + + std::string HashStamp(std::string_view content) { + return std::format("{:016x}", std::hash{}(content)); + } + + std::string FileBytes(const fs::path& file) { + std::ifstream in(file, std::ios::binary); + std::ostringstream buffer; + buffer << in.rdbuf(); + return buffer.str(); } } @@ -495,12 +515,17 @@ namespace { return out; } - // project.so predates its source, the crafter-build that will load it, or - // a pre-dependency artifact — any of them means recompiling. - bool ProjectFileStale(const fs::path& artifact, const fs::path& project, const fs::path& hostExe, const ProjectFileDependencies& deps) { + // project.so is stale when its stamp (crafter-build version, toolchain, + // module sources and compile command) disagrees, or when it predates its + // source or a pre-dependency artifact. The executable's mtime is not an + // input any more: an OSTree deployment dates every installed file at the + // epoch, so a comparison against it never fired after an update and the + // old project.so was loaded into a binary it did not match. + bool ProjectFileStale(const fs::path& artifact, const fs::path& project, std::string_view stamp, const ProjectFileDependencies& deps) { if (!fs::exists(artifact)) return true; + if (ReadStamp(StampPath(artifact)) != stamp) return true; const fs::file_time_type built = fs::last_write_time(artifact); - return built < fs::last_write_time(project) || built < fs::last_write_time(hostExe) || built < deps.newestArtifact; + return built < fs::last_write_time(project) || built < deps.newestArtifact; } } @@ -611,21 +636,22 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) { fs::path stdCc = GetToolchain().mingwSysroot / "include" / "c++" / mingWversion / "bits" / "std.cc"; - if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) { - // -femulated-tls keeps PCM TLS access matching libstdc++'s emutls - // definitions; mismatch surfaces as undefined std::__once_callable. - // The std.cc → std.cppm copy uses a plain filename: held under the - // cache lock, no other builder writes this path concurrently. - fs::path stdCppm = stdPcm.parent_path() / "std.cppm"; - std::error_code copyEc; - fs::copy_file(stdCc, stdCppm, fs::copy_options::overwrite_existing, copyEc); - if (copyEc) { - return std::format("copy {} -> {}: {}", stdCc.string(), stdCppm.string(), copyEc.message()); - } - return RunBuildCommand(std::format("{} --target={} -march={} -mtune={} -femulated-tls -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", GetBaseCommand(config), config.target, config.march, config.mtune, stdCppm.string(), stdPcm.string())); - } else { - return ""; + // -femulated-tls keeps PCM TLS access matching libstdc++'s emutls + // definitions; mismatch surfaces as undefined std::__once_callable. + // The std.cc → std.cppm copy uses a plain filename: held under the + // cache lock, no other builder writes this path concurrently. + fs::path stdCppm = stdPcm.parent_path() / "std.cppm"; + std::string command = std::format("{} --target={} -march={} -mtune={} -femulated-tls -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", GetBaseCommand(config), config.target, config.march, config.mtune, stdCppm.string(), stdPcm.string()); + std::string stamp = HashStamp(command + '\0' + FileBytes(stdCc)); + if (fs::exists(stdPcm) && ReadStamp(StampPath(stdPcm)) == stamp) return ""; + std::error_code copyEc; + fs::copy_file(stdCc, stdCppm, fs::copy_options::overwrite_existing, copyEc); + if (copyEc) { + return std::format("copy {} -> {}: {}", stdCc.string(), stdCppm.string(), copyEc.message()); } + std::string result = RunBuildCommand(command); + if (result.empty()) WriteStamp(StampPath(stdPcm), stamp); + return result; } else { const bool isWasm = config.target.starts_with("wasm32"); // wasi-sdk drops std.cppm at /share/libc++/v1/, the rest of @@ -669,11 +695,16 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) { if (!isWasm && !config.sysroot.empty()) { archFlags += std::format(" -nostdinc++ -isystem {}/usr/include/c++/v1", config.sysroot); } - if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCppm)) { - return RunBuildCommand(std::format("{} --target={} -std=c++26{}{} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", GetBaseCommand(config), config.target, sysrootFlag, archFlags, stdCppm, stdPcm.string())); - } else { - return ""; - } + // Keyed on the command and the module source's bytes, never on + // mtimes: an installed std.cppm can be dated at the epoch (OSTree), + // and a libc++ update under the same clang changes the bytes but + // not the path. + std::string command = std::format("{} --target={} -std=c++26{}{} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", GetBaseCommand(config), config.target, sysrootFlag, archFlags, stdCppm, stdPcm.string()); + std::string stamp = HashStamp(command + '\0' + FileBytes(stdCppm)); + if (fs::exists(stdPcm) && ReadStamp(StampPath(stdPcm)) == stamp) return ""; + std::string result = RunBuildCommand(command); + if (result.empty()) WriteStamp(StampPath(stdPcm), stamp); + return result; } } @@ -721,12 +752,17 @@ namespace { "Crafter.Build", }; - void EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir, const Configuration& host) { + // Returns the stamp of the module sources the cache was built from; it is + // one of project.so's inputs. + std::string EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir, const Configuration& host) { CacheLock lock(cacheDir); // Re-derived under the lock: another builder may have refreshed the // cache from its own sources while we waited. - std::string stamp = CrafterBuildSourceStamp(sourceDir, CrafterBuildModules); - const bool upToDate = ReadCacheStamp(cacheDir) == stamp; + // The std PCM is an input to every module PCM: its stamp is folded in + // so a rebuilt std.pcm (a libc++ update under the same clang) rebuilds + // them too, instead of leaving PCMs that import a std.pcm that is gone. + std::string stamp = HashStamp(CrafterBuildSourceStamp(sourceDir, CrafterBuildModules) + '\0' + ReadStamp(StampPath(cacheDir / "std.pcm"))); + const bool upToDate = ReadStamp(CacheStampPath(cacheDir)) == stamp; for (std::string_view name : CrafterBuildModules) { fs::path cppmPath = sourceDir / std::format("{}.cppm", name); fs::path pcmPath = cacheDir / std::format("{}.pcm", name); @@ -751,7 +787,8 @@ namespace { // Last, so an interrupted rebuild leaves the stamp disagreeing with the // sources and the next run starts over rather than trusting a half-built // set of PCMs. - if (!upToDate) WriteCacheStamp(cacheDir, stamp); + if (!upToDate) WriteStamp(CacheStampPath(cacheDir), stamp); + return stamp; } } @@ -765,8 +802,6 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span a // Same LoadProject wiring as ConcurrentCacheRace above. cfg.AddTest("HostCacheSourceStamp").Dependencies({ CrafterBuildLib.get() }) .LinkFlag("-Wl,--export-dynamic").LinkFlag("-ldl"); + cfg.AddTest("HostCacheToolchainKey").Dependencies({ CrafterBuildLib.get() }) + .LinkFlag("-Wl,--export-dynamic").LinkFlag("-ldl"); cfg.AddTest("ProjectDependency").Dependencies({ CrafterBuildLib.get() }) .LinkFlag("-Wl,--export-dynamic").LinkFlag("-ldl"); cfg.AddTest("ConcurrentDependencyReset").Dependencies({ CrafterBuildLib.get() }); diff --git a/tests/HostCacheToolchainKey/main.cpp b/tests/HostCacheToolchainKey/main.cpp new file mode 100644 index 0000000..58c35f8 --- /dev/null +++ b/tests/HostCacheToolchainKey/main.cpp @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +#include +import std; +import Crafter.Build; +namespace fs = std::filesystem; +using namespace Crafter; + +// Freshness of the std PCM and of project.so used to be an mtime comparison +// against files that come with the install: std.cppm and the crafter-build +// executable. A Flatpak (any OSTree deployment) gives every installed file +// mtime 0, so those inputs always looked older than the cache: a clang bump +// would never rebuild std.pcm, a crafter-build update would never recompile +// project.so, and clang crashes on the stale PCM instead of saying so. +// +// Both are now keyed on content stamps (the compile command, the std module +// source, the crafter-build version, the toolchain key and the module source +// stamp) and the cache directory carries the toolchain key in its name. This +// test drives the shapes an mtime rule gets wrong: a std.pcm dated before its +// source must still be reused, a warm load must leave project.so alone, and a +// stamp that disagrees must rebuild even when the artifact is dated after +// every input. +namespace { + std::int32_t Failures = 0; + + void Check(bool cond, std::string_view msg) { + if (!cond) { + std::println(std::cerr, "FAIL: {}", msg); + ++Failures; + } + } + + fs::path FindModuleCacheDir(const fs::path& cacheRoot) { + for (const fs::directory_entry& entry : fs::directory_iterator(cacheRoot / "crafter.build")) { + if (entry.is_directory()) return entry.path(); + } + return {}; + } + + bool Load(const fs::path& projectFile, std::string_view what) { + std::array noArgs = {}; + try { + (void)LoadProject(projectFile, noArgs); + return true; + } catch (const std::exception& e) { + std::println(std::cerr, "FAIL: LoadProject ({}) threw: {}", what, e.what()); + ++Failures; + return false; + } + } + + fs::path StampOf(const fs::path& artifact) { + fs::path stamp = artifact; + stamp += ".stamp"; + return stamp; + } +} + +int main() { + fs::path installedHome = GetCrafterBuildHome(); + if (!fs::exists(installedHome / "Crafter.Build.cppm")) { + std::println(std::cerr, "SKIP: no module sources at {}", installedHome.string()); + return 77; + } + + fs::path scratch = fs::temp_directory_path() / "crafter-build-host-cache-toolchain-key"; + std::error_code ec; + fs::remove_all(scratch, ec); + fs::create_directories(scratch); + + // A private copy of the module sources and a cold private cache, as in + // HostCacheSourceStamp. + fs::path home = scratch / "share" / "crafter-build"; + fs::create_directories(home.parent_path()); + fs::copy(installedHome, home, fs::copy_options::recursive); + setenv("CRAFTER_BUILD_HOME", home.string().c_str(), 1); + fs::path cacheRoot = scratch / "cache"; + fs::create_directories(cacheRoot); + setenv("XDG_CACHE_HOME", cacheRoot.string().c_str(), 1); + + fs::path projectDir = scratch / "project"; + fs::create_directories(projectDir); + fs::path projectFile = projectDir / "project.cpp"; + std::ofstream(projectFile) << + "import std;\n" + "import Crafter.Build;\n" + "using namespace Crafter;\n" + "extern \"C\" Configuration CrafterBuildProject(std::span) {\n" + " Configuration cfg;\n" + " cfg.path = \"./\";\n" + " cfg.name = \"keyed\";\n" + " cfg.outputName = \"keyed\";\n" + " cfg.type = ConfigurationType::Executable;\n" + " return cfg;\n" + "}\n"; + + if (!Load(projectFile, "cold")) return 1; + + const fs::path cacheDir = FindModuleCacheDir(cacheRoot); + Check(!cacheDir.empty(), "a module cache directory was created"); + if (cacheDir.empty()) return 1; + Check(cacheDir.filename().string().contains(GetToolchain().Key()), std::format("the cache directory is named for the toolchain (got '{}', key '{}')", cacheDir.filename().string(), GetToolchain().Key())); + + const fs::path stdPcm = cacheDir / "std.pcm"; + const fs::path projectSo = projectDir / "build" / "project.so"; + Check(fs::exists(stdPcm) && fs::exists(StampOf(stdPcm)), "std.pcm and its stamp exist after a cold load"); + Check(fs::exists(projectSo) && fs::exists(StampOf(projectSo)), "project.so and its stamp exist after a cold load"); + if (!fs::exists(StampOf(stdPcm)) || !fs::exists(StampOf(projectSo))) return 1; + + // The OSTree shape, inverted: std.pcm dated before its source. A stamp + // rule reuses it; an mtime rule would rebuild it on every run. project.so + // keeps two mtime inputs that live next to it (project.cpp and dependency + // artifacts) but the executable is no longer one of them, so a warm load + // with nothing changed leaves it alone. + const fs::file_time_type epoch{}; + fs::last_write_time(stdPcm, epoch); + const fs::file_time_type afterCold = fs::last_write_time(projectSo); + if (!Load(projectFile, "std.pcm dated at the epoch")) return 1; + Check(fs::last_write_time(stdPcm) == epoch, "std.pcm dated before its source is still reused"); + Check(fs::last_write_time(projectSo) == afterCold, "project.so is left alone when nothing changed"); + + // The other half: artifacts dated after every input, with a stamp that + // disagrees. An mtime rule keeps them; the stamp rule rebuilds. + const fs::file_time_type future = fs::file_time_type::clock::now() + std::chrono::hours(24); + std::ofstream(StampOf(stdPcm)) << "stale"; + fs::last_write_time(stdPcm, future); + if (!Load(projectFile, "stale std.pcm stamp")) return 1; + Check(fs::last_write_time(stdPcm) != future, "a disagreeing stamp rebuilds std.pcm even when it is dated after its source"); + { + std::ifstream in(StampOf(stdPcm)); + std::string stamp; + std::getline(in, stamp); + Check(stamp != "stale" && !stamp.empty(), "the rebuilt std.pcm gets its stamp back"); + } + + std::ofstream(StampOf(projectSo)) << "stale"; + fs::last_write_time(projectSo, future); + if (!Load(projectFile, "stale project.so stamp")) return 1; + Check(fs::last_write_time(projectSo) != future, "a disagreeing stamp recompiles project.so even when it is dated after its inputs"); + + // The one mtime input that legitimately remains: the project file itself. + const fs::file_time_type beforeEdit = fs::last_write_time(projectSo); + { + std::ofstream append(projectFile, std::ios::app); + append << "// edited\n"; + } + fs::last_write_time(projectFile, fs::file_time_type::clock::now() + std::chrono::seconds(2)); + if (!Load(projectFile, "edited project.cpp")) return 1; + Check(fs::last_write_time(projectSo) != beforeEdit, "an edited project.cpp recompiles project.so"); + + fs::remove_all(scratch, ec); + return Failures == 0 ? 0 : 1; +} From c3485b7ab1f15d240cc7774b12f4a00ba0e30ab6 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 22:32:35 +0200 Subject: [PATCH 02/41] docs: name the toolchain in the cache directory layout Co-Authored-By: Claude Fable 5.1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6af758c..400f9f1 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ import Crafter.LintRules; /bin/crafter-build # the executable /lib/libcrafter-build.a # static archive for downstream consumers /share/crafter-build/*.cppm # module sources (rebuilt on user machine on first run) -~/.cache/crafter.build/-/ # std.pcm + Crafter.Build*.pcm, built locally +~/.cache/crafter.build/---/ # std.pcm + Crafter.Build*.pcm, built locally per toolchain ``` ## Tests From add30b9a4f9dac4c2f49b5c47570df5be8421a50 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 22:34:57 +0200 Subject: [PATCH 03/41] feat(packaging): ship crafter-build as a Flatpak with its own toolchain packaging/flatpak/ holds the manifest for net.catcrafts.CrafterBuild and its side files. The runtime is the Freedesktop Sdk, not the Platform: a build tool needs glibc's headers and crt objects, gcc's libgcc_s, cmake, ninja, git and python at run time. Everything compiler-shaped is ours under /app, built from one LLVM 22.1.8 source tree: clang, lld, libclang, libc++ with its std module, compiler-rt for the host and for wasm32, the WASI sysroot (wasi-sdk 34 in sysroot-only mode, driven by the clang just built) and wasmtime for running the result. One tree means the modules crafter-build builds and the compiler that consumes them cannot drift apart, and the toolchain resolver finds all of it beside the executable with no configuration. The crafter-build module runs the bootstrap, the self-host build and the test suite inside the sandbox, so a Flatpak that installs has passed the suite with the toolchain it ships. CRAFTER_BUILD_MARCH is x86-64-v2 for the shipped binary; user projects keep -march=native. WasmHelloRuns compiles a fixture for wasm32-wasip1 and runs it under wasmtime. The existing wasm tests only inspect generated files, so this is the one that proves the sysroot, the std module for wasm and wasm-ld work together; it skips where the sysroot or wasmtime is missing. Co-Authored-By: Claude Fable 5.1 --- .gitignore | 4 +- packaging/flatpak/catcrafts.flatpakrepo | 8 + .../net.catcrafts.CrafterBuild.flatpakref | 13 ++ .../net.catcrafts.CrafterBuild.metainfo.xml | 34 ++++ .../flatpak/net.catcrafts.CrafterBuild.yaml | 182 ++++++++++++++++++ packaging/flatpak/wasi-host-toolchain.cmake | 23 +++ project.cpp | 1 + tests/WasmHelloRuns/fixture/main.cpp | 9 + tests/WasmHelloRuns/main.cpp | 57 ++++++ 9 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 packaging/flatpak/catcrafts.flatpakrepo create mode 100644 packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref create mode 100644 packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml create mode 100644 packaging/flatpak/net.catcrafts.CrafterBuild.yaml create mode 100644 packaging/flatpak/wasi-host-toolchain.cmake create mode 100644 tests/WasmHelloRuns/fixture/main.cpp create mode 100644 tests/WasmHelloRuns/main.cpp diff --git a/.gitignore b/.gitignore index a47aaf6..67d2a68 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ build/ bin/ lib/*.a share/crafter-build/ -.claude \ No newline at end of file +.claude +# written by CI next to the manifest so relative source paths still resolve +packaging/flatpak/ci-manifest.yaml diff --git a/packaging/flatpak/catcrafts.flatpakrepo b/packaging/flatpak/catcrafts.flatpakrepo new file mode 100644 index 0000000..8792ccd --- /dev/null +++ b/packaging/flatpak/catcrafts.flatpakrepo @@ -0,0 +1,8 @@ +[Flatpak Repo] +Title=Catcrafts +Url=https://flatpak.catcrafts.net/repo/ +Homepage=https://forgejo.catcrafts.net/Catcrafts +Comment=Catcrafts tools and applications +Description=crafter-build and other Catcrafts software, built and signed by Catcrafts CI +DefaultBranch=stable +GPGKey=GPG_KEY_BASE64_PLACEHOLDER diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref b/packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref new file mode 100644 index 0000000..16359ae --- /dev/null +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref @@ -0,0 +1,13 @@ +[Flatpak Ref] +Title=Crafter Build +Name=net.catcrafts.CrafterBuild +Branch=stable +Url=https://flatpak.catcrafts.net/repo/ +SuggestRemoteName=catcrafts +Homepage=https://forgejo.catcrafts.net/Catcrafts/Crafter.Build +Comment=C++26 modules build system +Description=crafter-build with its own LLVM toolchain and WASI sysroot +Icon= +RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo +IsRuntime=false +GPGKey=GPG_KEY_BASE64_PLACEHOLDER diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml b/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml new file mode 100644 index 0000000..7108966 --- /dev/null +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml @@ -0,0 +1,34 @@ + + + + net.catcrafts.CrafterBuild + Crafter Build + C++26 modules build system + CC0-1.0 + LGPL-3.0-only + + Catcrafts + + +

+ A build system for C++26 modules. Project descriptions are C++: a + project.cpp constructs a Configuration and returns it, and crafter-build + compiles, loads and executes it. The Flatpak carries its own LLVM, so + every machine builds with the same clang, lld, libc++ and WASI sysroot. +

+
+ https://forgejo.catcrafts.net/Catcrafts/Crafter.Build + https://forgejo.catcrafts.net/Catcrafts/Crafter.Build/issues + https://forgejo.catcrafts.net/Catcrafts/Crafter.Build + + crafter-build + + + + + +

First release distributed as a Flatpak, with a bundled LLVM 22 toolchain and WASI sysroot. Windows host support is removed; crafter-build gains --version and resolves its compiler from one place.

+
+
+
+
diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml new file mode 100644 index 0000000..01e286e --- /dev/null +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -0,0 +1,182 @@ +# SPDX-License-Identifier: LGPL-3.0-only +# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® +# +# crafter-build as a Flatpak. The runtime is the Freedesktop *Sdk*, not the +# Platform: a build tool needs glibc's headers and crt objects, gcc's +# libgcc_s, cmake, ninja, make, git and python at run time. Everything +# compiler-shaped is ours and lives under /app: clang, lld, libclang, libc++, +# compiler-rt (native and wasm32) and the WASI sysroot, all built from one +# LLVM source tree so the modules crafter-build builds and the compiler that +# consumes them can never drift apart. +app-id: net.catcrafts.CrafterBuild +runtime: org.freedesktop.Sdk +runtime-version: '26.08' +sdk: org.freedesktop.Sdk +command: crafter-build +finish-args: + # git dependencies, and the local web server `-r` starts for browser builds + - --share=network + - --socket=ssh-auth + # projects live anywhere; user sysroots too + - --filesystem=host + # so `-r` can launch what it built, games included + - --share=ipc + - --socket=wayland + - --socket=fallback-x11 + - --socket=pulseaudio + - --device=all + # gdb and friends from the Sdk + - --allow=devel + - --env=PATH=/app/bin:/usr/bin +build-options: + env: + CRAFTER_BUILD_LLVM_DIR: /app + # The shipped binary runs on any x86_64 from the last decade; user + # projects still build with -march=native by default. + CRAFTER_BUILD_MARCH: x86-64-v2 + CRAFTER_BUILD_MTUNE: generic + # CI replaces this with the tag; a local build is a development build. + CRAFTER_BUILD_VERSION: 0.0.0-dev +cleanup: + - /share/man + - /share/doc +modules: + - name: llvm + buildsystem: cmake-ninja + builddir: true + subdir: llvm + build-options: + # Debug info for all of LLVM would be a multi-gigabyte .Debug extension. + no-debuginfo: true + config-opts: + - -DCMAKE_BUILD_TYPE=Release + - -DLLVM_ENABLE_ASSERTIONS=OFF + - -DLLVM_ENABLE_PROJECTS=clang;lld + - -DLLVM_ENABLE_RUNTIMES=compiler-rt;libcxx;libcxxabi;libunwind + - -DLLVM_TARGETS_TO_BUILD=X86;AArch64;WebAssembly + # One shared LLVM and one shared clang library instead of a statically + # linked copy per tool: a fraction of the install size. + - -DLLVM_BUILD_LLVM_DYLIB=ON + - -DLLVM_LINK_LLVM_DYLIB=ON + - -DCLANG_LINK_CLANG_DYLIB=ON + - -DCLANG_DEFAULT_CXX_STDLIB=libc++ + - -DCLANG_DEFAULT_LINKER=lld + - -DLIBCXX_INSTALL_MODULES=ON + - -DLLVM_INCLUDE_TESTS=OFF + - -DLLVM_INCLUDE_EXAMPLES=OFF + - -DLLVM_INCLUDE_DOCS=OFF + - -DLLVM_INCLUDE_BENCHMARKS=OFF + - -DLLVM_INSTALL_UTILS=OFF + - -DCLANG_INCLUDE_TESTS=OFF + - -DCOMPILER_RT_INCLUDE_TESTS=OFF + # The Sdk links with binutils' ld; two libLLVM-sized links at a time + # fit the CX53's memory with room to spare. + - -DLLVM_PARALLEL_LINK_JOBS=2 + cleanup: + - /include/llvm + - /include/llvm-c + - /include/clang + - /include/clang-tidy + - /include/lld + - /lib/cmake + - /lib/*.a + - /share/opt-viewer + - /share/scan-build + - /share/scan-view + - /bin/clang-check + - /bin/clang-extdef-mapping + - /bin/clang-import-test + - /bin/clang-linker-wrapper + - /bin/clang-nvlink-wrapper + - /bin/clang-offload-bundler + - /bin/clang-offload-packager + - /bin/clang-refactor + - /bin/clang-rename + - /bin/clang-repl + - /bin/clang-sycl-linker + - /bin/diagtool + - /bin/git-clang-format + - /bin/hmaptool + - /bin/llvm-exegesis + - /bin/scan-build* + - /bin/scan-view + sources: + - type: archive + url: https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz + sha256: 922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888 + + - name: wasi-sysroot + # wasi-sdk's sysroot-only mode, driven by the clang built above: + # wasi-libc, compiler-rt builtins (installed into our clang's resource + # dir, where the wasm32 driver looks for them) and libc++ for wasip1 and + # wasip1-threads. The llvm-project tree is the same tarball as above so + # the wasm libc++ matches the native one. + buildsystem: cmake-ninja + builddir: true + build-options: + no-debuginfo: true + config-opts: + - -DCMAKE_TOOLCHAIN_FILE=/run/build/wasi-sysroot/wasi-host-toolchain.cmake + - -DWASI_SDK_BUILD_TOOLCHAIN=OFF + - -DWASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR=ON + - -DWASI_SDK_TARGETS=wasm32-wasip1;wasm32-wasip1-threads + - -DWASI_SDK_INCLUDE_TESTS=OFF + - -DCMAKE_INSTALL_PREFIX=/app + post-install: + # wasi-sdk installs into /share/wasi-sysroot; crafter-build + # looks there (Crafter.Build:Toolchain), so nothing to move. + - test -f /app/share/wasi-sysroot/share/libc++/v1/std.cppm + sources: + - type: git + url: https://github.com/WebAssembly/wasi-sdk.git + tag: wasi-sdk-34 + disable-submodules: true + - type: git + url: https://github.com/WebAssembly/wasi-libc.git + commit: 2e6fb9d8ee0cdf9e431fbcabe8af3115de000a13 + dest: src/wasi-libc + - type: archive + url: https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz + sha256: 922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888 + dest: src/llvm-project + - type: file + path: wasi-host-toolchain.cmake + + - name: wasmtime + # The runner for wasm32-wasi tests and `-r` on a non-browser wasm build. + buildsystem: simple + build-commands: + - install -Dm755 wasmtime /app/bin/wasmtime + sources: + - type: archive + url: https://github.com/bytecodealliance/wasmtime/releases/download/v49.0.2/wasmtime-v49.0.2-x86_64-linux.tar.xz + sha256: a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 + + - name: crafter-build + buildsystem: simple + build-options: + # The self-host build fetches Crafter.Math, Crafter.Asset, + # Crafter.LintRules and glslang from git; they are unpinned on purpose + # (sibling repos tracking their default branch, see AGENTS.md). + build-args: + - --share=network + build-commands: + # Bootstrap by hand, then let the result build itself: only the + # self-hosted binary has asset support and ThinLTO. + - ./build.sh + - ./bin/crafter-build + - CRAFTER_BUILD_HOME=$PWD/share/crafter-build ./bin/crafter-build test + - install -Dm755 bin/crafter.build-exe-*/crafter-build /app/bin/crafter-build + - install -Dm644 bin/crafter.build-lib-*/libcrafter-build.a /app/lib/libcrafter-build.a + - mkdir -p /app/share && cp -r share/crafter-build /app/share/ + - install -Dm644 LICENSE LICENSE.GPL -t /app/share/licenses/net.catcrafts.CrafterBuild/ + - install -Dm644 packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml -t /app/share/metainfo/ + sources: + - type: dir + path: ../.. + skip: + - .git + - bin + - build + - lib/libcrafter-build.a + - share/crafter-build diff --git a/packaging/flatpak/wasi-host-toolchain.cmake b/packaging/flatpak/wasi-host-toolchain.cmake new file mode 100644 index 0000000..662d822 --- /dev/null +++ b/packaging/flatpak/wasi-host-toolchain.cmake @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: LGPL-3.0-only +# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® +# +# Points wasi-sdk's sysroot build at the clang the Flatpak built, instead of +# the one wasi-sdk would build itself. Mirrors the wasi-sdk.cmake toolchain +# file an installed wasi-sdk ships, with our prefix. +set(WASI_SDK_PREFIX /app) +set(CMAKE_SYSTEM_NAME WASI) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(triple wasm32-wasip1) +set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang) +set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang) +set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar) +set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib) +set(CMAKE_NM ${WASI_SDK_PREFIX}/bin/llvm-nm) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) +# The sysroot does not exist yet when cmake probes the compiler. +set(CMAKE_C_COMPILER_WORKS ON) +set(CMAKE_CXX_COMPILER_WORKS ON) diff --git a/project.cpp b/project.cpp index f2a982b..d3364d3 100644 --- a/project.cpp +++ b/project.cpp @@ -132,6 +132,7 @@ extern "C" Configuration CrafterBuildProject(std::span a cfg.AddTest("VersionFlag").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("WasiBrowserRuntime").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("WasmVariants").Dependencies({ CrafterBuildLib.get() }); + cfg.AddTest("WasmHelloRuns").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("RunSingleTestExit").Dependencies({ CrafterBuildLib.get() }); // LoadProject dlopens the synthesized project.so, which references // Crafter:: symbols (HostTarget, Configuration ctors) that have to be diff --git a/tests/WasmHelloRuns/fixture/main.cpp b/tests/WasmHelloRuns/fixture/main.cpp new file mode 100644 index 0000000..59fb23d --- /dev/null +++ b/tests/WasmHelloRuns/fixture/main.cpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +import std; + +int main() { + std::println("hello from wasm"); + return 0; +} diff --git a/tests/WasmHelloRuns/main.cpp b/tests/WasmHelloRuns/main.cpp new file mode 100644 index 0000000..58a910f --- /dev/null +++ b/tests/WasmHelloRuns/main.cpp @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +import std; +import Crafter.Build; +namespace fs = std::filesystem; +using namespace Crafter; + +// A wasm build end to end: the fixture is compiled for wasm32-wasip1 against +// the resolved toolchain's WASI sysroot and the result is run under +// wasmtime. The other wasm tests only inspect generated files; this is the +// one that proves the sysroot, the std module for wasm and wasm-ld all work +// together. It skips when the sysroot or wasmtime is missing, so a +// development box without them is not a failure. Inside the Flatpak both are +// bundled, and there the skip would be the failure to look for. +int main() { + const Toolchain& tc = GetToolchain(); + if (!fs::exists(tc.wasiSysroot / "include")) { + std::println(std::cerr, "SKIP: no WASI sysroot at {}", tc.wasiSysroot.string()); + return 77; + } + if (std::system("which wasmtime > /dev/null 2>&1") != 0) { + std::println(std::cerr, "SKIP: wasmtime not on PATH"); + return 77; + } + + Configuration cfg; + cfg.path = fs::current_path() / "tests" / "WasmHelloRuns" / "fixture"; + cfg.name = "wasm-hello"; + cfg.outputName = "wasm-hello"; + cfg.target = "wasm32-wasip1"; + cfg.type = ConfigurationType::Executable; + std::array ifaces = {}; + std::array impls = { "main" }; + cfg.GetInterfacesAndImplementations(ifaces, impls); + + std::unordered_map> depResults; + std::mutex depMutex; + BuildResult r = Build(cfg, depResults, depMutex); + if (!r.result.empty()) { + std::println(std::cerr, "build failed: {}", r.result); + return 1; + } + + const fs::path wasm = ExpectedOutput(cfg); + if (!fs::exists(wasm)) { + std::println(std::cerr, "no .wasm produced at {}", wasm.string()); + return 1; + } + + const auto run = RunCommandWithTimeout(std::format("wasmtime {}", wasm.string()), std::chrono::seconds(60)); + if (run.exitCode != 0 || !run.output.contains("hello from wasm")) { + std::println(std::cerr, "wasmtime run failed (exit {}): {}", run.exitCode, run.output); + return 1; + } + return 0; +} From ff2e8c03a9c4f634c2ed829db50f0df5e261bf4a Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 23:02:44 +0200 Subject: [PATCH 04/41] fix(packaging): classic libc++ layout, no debug info, clean wasm flags Three findings from building the manifest locally. LLVM's runtimes build installs libc++ under lib// by default. clang finds it there, but the rpath crafter-build adds for a toolchain outside /usr points at /lib, and so does every distro layout the resolver was modelled on; the bootstrap failed to start with libc++.so.1 missing. LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF puts the runtimes in lib/ and include/c++/v1. flatpak-builder's default CFLAGS carry -g. For all of LLVM that is a 30 GB build tree and a multi-gigabyte .Debug extension; the module now builds without debug info and strips, keeping the hardening flags. The no-debuginfo key also moves under build-options, where flatpak-builder actually reads it. The same default CFLAGS and LDFLAGS (-fcf-protection, -z relro) reach wasi-sdk's nested wasm32 builds through the environment, and clang rejects them for that target. The wasi-sysroot module clears them. Co-Authored-By: Claude Fable 5.1 --- .../flatpak/net.catcrafts.CrafterBuild.yaml | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml index 01e286e..7226ad1 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -46,14 +46,27 @@ modules: builddir: true subdir: llvm build-options: - # Debug info for all of LLVM would be a multi-gigabyte .Debug extension. + # flatpak-builder's default CFLAGS carry -g. For all of LLVM that is a + # 30 GB build tree and a multi-gigabyte .Debug extension, so build + # without debug info, strip, and skip the split. The hardening flags + # from the defaults stay. no-debuginfo: true + strip: true + cflags: -O2 -pipe -fexceptions -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection + cflags-override: true + cxxflags: -O2 -pipe -fexceptions -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection + cxxflags-override: true config-opts: - -DCMAKE_BUILD_TYPE=Release - -DLLVM_ENABLE_ASSERTIONS=OFF - -DLLVM_ENABLE_PROJECTS=clang;lld - -DLLVM_ENABLE_RUNTIMES=compiler-rt;libcxx;libcxxabi;libunwind - -DLLVM_TARGETS_TO_BUILD=X86;AArch64;WebAssembly + # Classic layout: libc++ in lib/ and include/c++/v1, not under a + # per-target subdirectory, the same shape as a distro install. + # crafter-build adds -L and an rpath for /lib itself, since + # clang searches only its per-target directory and the system paths. + - -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF # One shared LLVM and one shared clang library instead of a statically # linked copy per tool: a fraction of the install size. - -DLLVM_BUILD_LLVM_DYLIB=ON @@ -115,6 +128,16 @@ modules: builddir: true build-options: no-debuginfo: true + # flatpak-builder's default CFLAGS/LDFLAGS are x86-64 hardening flags + # (-fcf-protection, -z relro) that the nested wasm32 builds inherit + # through the environment and clang rejects for that target. + cflags: "" + cflags-override: true + cxxflags: "" + cxxflags-override: true + ldflags: "" + ldflags-override: true + strip: true config-opts: - -DCMAKE_TOOLCHAIN_FILE=/run/build/wasi-sysroot/wasi-host-toolchain.cmake - -DWASI_SDK_BUILD_TOOLCHAIN=OFF From a902b32938f7e9bdd48086be76b6a612d1286093 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 23:29:42 +0200 Subject: [PATCH 05/41] fix(packaging): assemble compiler-rt's wasm stubs for wasm32 wasi-sdk's sysroot build hands its compiler-rt sub-build CMAKE_C_COMPILER_TARGET but nothing for the ASM language, so the two wasm/*.S stubs in the builtins (the setjmp/longjmp and C++ exception tags) were assembled for x86-64. wasm-ld warned on every wasm link that the archive members were neither Wasm objects nor bitcode, and anything that reaches those tags, which crafter-build's -wasm-enable-sjlj does, would fail to link. wasi-sdk's own toolchain never sees it because its clang defaults to wasm32-wasip1; the sysroot-only mode against a host clang does. A one-line patch adds CMAKE_ASM_COMPILER_TARGET to that sub-build; it belongs upstream. Co-Authored-By: Claude Fable 5.1 --- .../flatpak/net.catcrafts.CrafterBuild.yaml | 5 +++++ packaging/flatpak/wasi-sdk-asm-target.patch | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 packaging/flatpak/wasi-sdk-asm-target.patch diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml index 7226ad1..a8b769a 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -154,6 +154,11 @@ modules: url: https://github.com/WebAssembly/wasi-sdk.git tag: wasi-sdk-34 disable-submodules: true + # The compiler-rt sub-build gets the C target but not the ASM target, so + # its two wasm/*.S stubs come out as host objects when the driving clang + # does not default to wasm32. Reported upstream; drop once wasi-sdk has it. + - type: patch + path: wasi-sdk-asm-target.patch - type: git url: https://github.com/WebAssembly/wasi-libc.git commit: 2e6fb9d8ee0cdf9e431fbcabe8af3115de000a13 diff --git a/packaging/flatpak/wasi-sdk-asm-target.patch b/packaging/flatpak/wasi-sdk-asm-target.patch new file mode 100644 index 0000000..055c72f --- /dev/null +++ b/packaging/flatpak/wasi-sdk-asm-target.patch @@ -0,0 +1,20 @@ +wasi-sdk-sysroot.cmake: tell the compiler-rt sub-build the ASM target too + +The sub-build gets CMAKE_C_COMPILER_TARGET but nothing for the ASM +language, so compiler-rt's wasm/*.S stubs (the setjmp/longjmp and C++ +exception tags) are assembled for the host when the driving clang is not +a wasm-default build. wasm-ld then warns that the archive members are +neither Wasm objects nor bitcode, and code that needs the tags fails to +link. wasi-sdk's own toolchain hides this because its clang defaults to +wasm32-wasip1; the sysroot-only mode against a host clang does not. + +--- a/cmake/wasi-sdk-sysroot.cmake ++++ b/cmake/wasi-sdk-sysroot.cmake +@@ -107,6 +107,7 @@ + -DCOMPILER_RT_BUILD_ORC=OFF + -DCOMPILER_RT_BUILD_GWP_ASAN=OFF + -DCMAKE_C_COMPILER_TARGET=${target} ++ -DCMAKE_ASM_COMPILER_TARGET=${target} + -DCMAKE_C_FLAGS=${WASI_SDK_CPU_CFLAGS} + -DCMAKE_CXX_FLAGS=${WASI_SDK_CPU_CFLAGS} + -DCMAKE_ASM_FLAGS=${WASI_SDK_CPU_CFLAGS} From ccfd6078703fbe9c4ff56c4fd40f9cab4948d7e5 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 22:36:55 +0200 Subject: [PATCH 06/41] feat(ci): build and publish the Flatpak One job replaces the tarball pipeline. On a pull request it builds the Flatpak with flatpak-builder, which runs the bootstrap, the self-host build and the test suite inside the sandbox, and smoke-tests the result with `crafter-build --version` and the hello-world example. On a push to master it also bundles the build, promotes it into the live repo as the `beta` branch and refreshes the rolling `latest` prerelease. On a tag X.Y.Z it promotes as `stable`, refuses to proceed unless the metainfo's newest release is that version, and publishes a release whose notes are that entry's description. The job runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box, with the Sdk install, the flatpak-builder state and the repo's web root as volumes. The state volume is what keeps a normal run short: flatpak-builder rebuilds the LLVM and WASI modules only when their sources change. The live repo is canonical and never built into; builds are promoted with build-commit-from, then build-update-repo signs and prunes it and generates static deltas. The musl job, the per-march tarballs, the toolchain packages the runner used to install and the PKGBUILD go: the Flatpak is the one artifact. Co-Authored-By: Claude Fable 5.1 --- .forgejo/workflows/ci.yaml | 326 ++++++++++++++++--------------------- AGENTS.md | 16 +- PKGBUILD | 35 ---- 3 files changed, 152 insertions(+), 225 deletions(-) delete mode 100644 PKGBUILD diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 6d9d973..d011ec3 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -1,229 +1,185 @@ -name: CI +name: Flatpak +# One job, three outcomes: +# pull request -> build the Flatpak and run the suite inside it +# push master -> also publish the result as the `beta` branch of the +# Catcrafts repo and refresh the rolling `latest` release +# tag X.Y.Z -> publish as `stable` and create release X.Y.Z +# +# The `flatpak` runner label is the privileged runner on the Hetzner box +# (bubblewrap needs it) with three volumes allowed: the Sdk install, the +# flatpak-builder state (ccache and the LLVM module, so only crafter-build +# itself rebuilds on a normal run) and the published repo's web root. on: pull_request: branches: [master] push: branches: [master] + tags: ['[0-9]+.[0-9]+.[0-9]+'] workflow_dispatch: +env: + APP_ID: net.catcrafts.CrafterBuild + MANIFEST: packaging/flatpak/net.catcrafts.CrafterBuild.yaml + CI_MANIFEST: packaging/flatpak/ci-manifest.yaml + RUNTIME: org.freedesktop.Sdk//26.08 + REPO_DIR: /srv/flatpak/repo + WEB_DIR: /srv/flatpak + jobs: - build-test-release: - runs-on: arch-latest - env: - # x86-64-v2 = SSE4.2 baseline; runs on the CI SBC (Intel N5105 / Tremont, - # no AVX) and on essentially every x86_64 CPU since ~2011. The bootstrap - # MUST stay v2 — anything higher would emit instructions the SBC can't - # execute and the bootstrap binary itself would crash before it could - # rebuild for higher levels. v3/v4 variants are produced from this v2 - # bootstrap by re-running it with CRAFTER_BUILD_MARCH overridden. - CRAFTER_BUILD_MARCH: x86-64-v2 - CRAFTER_BUILD_MTUNE: generic + flatpak: + runs-on: flatpak + container: + image: archlinux:latest + options: >- + -v crafter-flatpak-system:/var/lib/flatpak + -v crafter-flatpak-builder:/root/.cache/flatpak-builder + -v /var/www/flatpak:/srv/flatpak steps: - - name: Install build dependencies + - name: Install tools run: | - # The slim archlinux:latest image arrives without a populated - # pacman keyring AND without a local pacman master key, so: - # 1. --init generates the local signing key (needed when pacman - # itself rewrites the keyring during package upgrades) - # 2. --populate archlinux imports the upstream master keys so - # currently-shipping signatures verify - # After that we can refresh archlinux-keyring to pick up keys for - # packagers added after the image's snapshot, then -Syu the rest. pacman-key --init pacman-key --populate archlinux pacman -Sy --noconfirm --needed archlinux-keyring - pacman -Syu --noconfirm --needed \ - base-devel git zip tar jq \ - clang llvm lld libc++ cmake \ - mingw-w64-gcc \ - wasi-libc wasi-libc++ wasi-libc++abi wasi-compiler-rt \ - nodejs - # The container runs as root and the workspace may be owned by a - # different uid; tell git not to refuse operations on it. + pacman -Syu --noconfirm --needed flatpak flatpak-builder ccache git nodejs jq gnupg go-yq ostree git config --global --add safe.directory '*' + - name: Sdk + # A no-op once the volume is warm; `update` follows point releases. + run: | + flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo + flatpak install -y --noninteractive flathub "$RUNTIME" + flatpak update -y --noninteractive + - name: Checkout uses: actions/checkout@v4 with: - # Persist the auth token so the 'Update rolling latest tag' step - # below can push the tag back via the implicit GITHUB_TOKEN. + fetch-depth: 0 persist-credentials: true - - name: Cache glslang clone+build - # Cache covers crafter-build's external dep cache. The first run builds - # glslang; the cache makes subsequent runs reuse that build. - uses: actions/cache@v4 - with: - path: | - ~/.cache/crafter.build/external - key: glslang-${{ runner.os }}-v2 - - - name: Bootstrap (build.sh) - run: ./build.sh - - # Full style gate: Report() findings (naming, enum-class, char*, …) AND - # would-reformat findings from the transform rules. A dirty tree means - # someone skipped `crafter-build format` — fail fast, before the - # (much slower) test suite. - # - name: Lint - # run: CRAFTER_BUILD_HOME=$PWD/share/crafter-build ./bin/crafter-build lint - - - name: Run tests - run: CRAFTER_BUILD_HOME=$PWD/share/crafter-build ./bin/crafter-build test - - # Everything below produces release artifacts and only runs on push to - # master. PRs stop after the test step above — the bootstrap binary is - # enough to validate the change; the per-march variant builds and the - # archive uploads are wasted work on a PR. - - name: Build Linux x86-64-v2 (matches bootstrap) - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' - run: CRAFTER_BUILD_HOME=$PWD/share/crafter-build ./bin/crafter-build - - - name: Build Linux x86-64-v3 - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' - run: CRAFTER_BUILD_HOME=$PWD/share/crafter-build CRAFTER_BUILD_MARCH=x86-64-v3 ./bin/crafter-build - - - name: Build Linux x86-64-v4 - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' - run: CRAFTER_BUILD_HOME=$PWD/share/crafter-build CRAFTER_BUILD_MARCH=x86-64-v4 ./bin/crafter-build - - - name: Package artifacts - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' + - name: Version and channel + id: meta + run: | + set -eu + if [ "${GITHUB_REF_TYPE}" = tag ]; then + version="$GITHUB_REF_NAME" + branch=stable + # The metainfo's newest release is the release notes and must be + # the version being tagged. + top=$(grep -m1 -oP '&2 + exit 1 + fi + else + version="0.0.0-dev+$(git rev-parse --short HEAD)" + branch=beta + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "branch=$branch" >> "$GITHUB_OUTPUT" + echo "version $version, flatpak branch $branch" + + - name: Build + # The manifest is rewritten beside the original so its relative + # source paths still resolve. The suite runs inside this build. + env: + VERSION: ${{ steps.meta.outputs.version }} + run: | + set -eu + yq '."build-options".env.CRAFTER_BUILD_VERSION = strenv(VERSION)' "$MANIFEST" > "$CI_MANIFEST" + grep -n 'CRAFTER_BUILD_VERSION' "$CI_MANIFEST" + flatpak-builder --force-clean --ccache --disable-rofiles-fuse \ + --state-dir=/root/.cache/flatpak-builder \ + --repo=repo --default-branch="${{ steps.meta.outputs.branch }}" \ + build-dir "$CI_MANIFEST" + + - name: Smoke test + run: | + flatpak-builder --run build-dir "$CI_MANIFEST" crafter-build --version + flatpak-builder --run build-dir "$CI_MANIFEST" sh -c 'cd examples/hello-world && crafter-build && ls bin/*/hello*' + + # Everything below publishes and only runs on master and on tags. + - name: Bundle + if: github.event_name != 'pull_request' run: | - set -eux mkdir -p dist + flatpak build-bundle repo "dist/crafter-build-${{ steps.meta.outputs.version }}.flatpak" "$APP_ID" "${{ steps.meta.outputs.branch }}" + cp packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref dist/ + (cd dist && sha256sum * > SHA256SUMS) + ls -la dist - # 3 archives, one per march level. Each carries the executable, the - # static library and share/; libc++ comes from the host. - for march in x86-64-v2 x86-64-v3 x86-64-v4; do - # Short suffix (v2/v3/v4) for archive filenames; the upload-artifact - # steps below reference these short names. - short=${march##*-} - - # VariantId is "----"; CI pins - # mtune=generic so each (target, march) resolves to a single subdir - # but the trailing hash isn't known to the workflow, hence the glob. - stage_lin=$(mktemp -d) - mkdir -p "$stage_lin/bin" "$stage_lin/lib" - cp bin/crafter.build-exe-x86_64-pc-linux-gnu-$march-*/crafter-build "$stage_lin/bin/" - cp bin/crafter.build-lib-x86_64-pc-linux-gnu-$march-*/libcrafter-build.a "$stage_lin/lib/" - cp -r share "$stage_lin/" - tar czf "dist/crafter-build-linux-x86_64-$short.tar.gz" -C "$stage_lin" . - done - - ls -la dist/ - - # Upload each archive as its own workflow artifact (one upload per file) - # so users browsing the PR / run page get small, individually-named - # downloads instead of one wrapper zip containing the lot. - - name: Upload Linux v2 artifact - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' + - name: Upload bundle + if: github.event_name != 'pull_request' uses: actions/upload-artifact@v3 - with: { name: crafter-build-linux-x86_64-v2, path: dist/crafter-build-linux-x86_64-v2.tar.gz, if-no-files-found: error } - - name: Upload Linux v3 artifact - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' - uses: actions/upload-artifact@v3 - with: { name: crafter-build-linux-x86_64-v3, path: dist/crafter-build-linux-x86_64-v3.tar.gz, if-no-files-found: error } - - name: Upload Linux v4 artifact - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' - uses: actions/upload-artifact@v3 - with: { name: crafter-build-linux-x86_64-v4, path: dist/crafter-build-linux-x86_64-v4.tar.gz, if-no-files-found: error } + with: + name: crafter-build-${{ steps.meta.outputs.version }} + path: dist/*.flatpak + if-no-files-found: error - - name: Update rolling 'latest' tag - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' + - name: Publish to the Catcrafts repo + if: github.event_name != 'pull_request' + env: + FLATPAK_GPG_PRIVATE_KEY: ${{ secrets.FLATPAK_GPG_PRIVATE_KEY }} + FLATPAK_GPG_KEY_ID: ${{ secrets.FLATPAK_GPG_KEY_ID }} + run: | + set -eu + export GNUPGHOME=$(mktemp -d) + printf '%s' "$FLATPAK_GPG_PRIVATE_KEY" | gpg --batch --quiet --import + # The live repo is the canonical copy; builds are promoted into it, + # never built into it. + [ -d "$REPO_DIR" ] || ostree init --repo="$REPO_DIR" --mode=archive + flatpak build-commit-from --src-repo=repo \ + --gpg-sign="$FLATPAK_GPG_KEY_ID" --gpg-homedir="$GNUPGHOME" \ + "$REPO_DIR" "app/$APP_ID/x86_64/${{ steps.meta.outputs.branch }}" + flatpak build-update-repo --gpg-sign="$FLATPAK_GPG_KEY_ID" --gpg-homedir="$GNUPGHOME" \ + --generate-static-deltas --prune "$REPO_DIR" + # The repo description and the app ref carry the public key. + key=$(gpg --export "$FLATPAK_GPG_KEY_ID" | base64 --wrap=0) + sed "s|GPGKey=.*|GPGKey=$key|" packaging/flatpak/catcrafts.flatpakrepo > "$WEB_DIR/catcrafts.flatpakrepo" + sed "s|GPGKey=.*|GPGKey=$key|" packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref > "$WEB_DIR/net.catcrafts.CrafterBuild.flatpakref" + + - name: Rolling 'latest' release + if: github.event_name != 'pull_request' && github.ref_type != 'tag' run: | git config user.email "ci@catcrafts.net" git config user.name "Crafter Build CI" git tag -f latest git push origin latest --force - - name: Publish rolling 'latest' release - # forgejo-release uploads each file in release-dir as a separate - # release asset, so the Releases page shows individually - # downloadable archives — pick the v-level matching your CPU. - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' + if: github.event_name != 'pull_request' && github.ref_type != 'tag' uses: https://code.forgejo.org/actions/forgejo-release@v2 with: direction: upload url: ${{ github.server_url }} repo: ${{ github.repository }} tag: latest - title: Latest main build + title: Latest master build prerelease: true override: true release-dir: dist token: ${{ secrets.GITHUB_TOKEN }} - # musl build for Alpine / postmarketOS hosts: the glibc launcher above cannot - # run there (musl has no glibc loader and gcompat lacks libgcc_s + the - # __isoc23_* symbols). Bootstrap natively on alpine:edge and ship the - # bootstrap output — the same thing the Arch PKGBUILD installs. Dynamic - # against musl libc++ (crafter-build dlopens project.so, so it cannot be - # fully static); consumers need `apk add clang lld libc++-dev llvm-runtimes`. - release-musl: - needs: build-test-release - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' - runs-on: arch-latest - container: - image: alpine:edge - env: - CRAFTER_BUILD_MARCH: x86-64-v2 - CRAFTER_BUILD_MTUNE: generic - steps: - - name: Install build dependencies - # nodejs: actions/checkout is a Node action. build-base: gcc's - # libgcc_s + crt objects, which clang's musl driver links against, plus - # make/binutils for the glslang cmake build (python3: its generators). - # clang-dev + llvm-dev: the libclang header Crafter.Build-Lint includes - # lives under llvm-config --includedir here, not /usr/include. llvm: - # llvm-ar for the LTO static archives the self-host/test builds write. + - name: Release notes + if: github.ref_type == 'tag' run: | - apk add -q nodejs git curl jq tar clang clang-dev lld libc++-dev \ - llvm-libunwind-dev llvm-runtimes llvm llvm-dev cmake build-base python3 - git config --global --add safe.directory '*' - - - name: Checkout - uses: actions/checkout@v4 - - - name: Bootstrap (build.sh) - run: ./build.sh - - - name: Run tests - run: CRAFTER_BUILD_HOME=$PWD/share/crafter-build ./bin/crafter-build test - - - name: Package artifact - run: | - set -eux - file bin/crafter-build || true - mkdir -p dist - stage=$(mktemp -d) - mkdir -p "$stage/bin" "$stage/lib" - cp bin/crafter-build "$stage/bin/" - cp lib/libcrafter-build.a "$stage/lib/" - cp -r share "$stage/" - tar czf dist/crafter-build-linux-x86_64-musl-v2.tar.gz -C "$stage" . - ls -la dist/ - - - name: Upload musl artifact - uses: actions/upload-artifact@v3 - with: { name: crafter-build-linux-x86_64-musl-v2, path: dist/crafter-build-linux-x86_64-musl-v2.tar.gz, if-no-files-found: error } - - # Add the asset to the rolling 'latest' release the first job just - # (re)published, via the API rather than forgejo-release: that action's - # override mode would drop the other job's assets. - - name: Attach to rolling 'latest' release - env: - TOKEN: ${{ secrets.GITHUB_TOKEN }} - API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} - run: | - set -eu - asset=crafter-build-linux-x86_64-musl-v2.tar.gz - rel=$(curl -fsS -H "Authorization: token $TOKEN" "$API/releases/tags/latest") - id=$(printf '%s' "$rel" | jq -r .id) - for old in $(printf '%s' "$rel" | jq -r --arg n "$asset" '.assets[] | select(.name==$n) | .id'); do - curl -fsS -X DELETE -H "Authorization: token $TOKEN" "$API/releases/$id/assets/$old" - done - curl -fsS -H "Authorization: token $TOKEN" -F "attachment=@dist/$asset" \ - "$API/releases/$id/assets?name=$asset" > /dev/null - echo "attached $asset to release $id" + # The newest block's description, as plain text. + python3 - > release-notes.md <<'EOF' + import re, html + text = open("packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml").read() + block = re.search(r"]*>(.*?)", text, re.S).group(1) + paras = re.findall(r"

(.*?)

", block, re.S) + print("\n\n".join(html.unescape(" ".join(p.split())) for p in paras)) + print("\nInstall:\n\n flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo\n flatpak install catcrafts net.catcrafts.CrafterBuild\n") + EOF + - name: Publish release + if: github.ref_type == 'tag' + uses: https://code.forgejo.org/actions/forgejo-release@v2 + with: + direction: upload + url: ${{ github.server_url }} + repo: ${{ github.repository }} + tag: ${{ github.ref_name }} + title: crafter-build ${{ github.ref_name }} + release-notes: release-notes.md + release-dir: dist + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md index b33dad4..8a1fc1d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ crafter-build is a C++26 modules build system. A project is described by a `proj | `wasi-runtime/` | `runtime.js` and `index.html.in`, shipped for browser targets. | | `lib/` | Vendored third-party headers (nlohmann/json, glslang's DirStackFileIncluder). Do not edit. | | `share/crafter-build/`, `bin/`, `build/`, `lib/*.a` | Generated and gitignored. | -| `PKGBUILD`, `.forgejo/workflows/ci.yaml` | Arch package and the Forgejo Actions CI. | +| `packaging/flatpak/`, `.forgejo/workflows/ci.yaml` | The Flatpak manifest with its metainfo, repo and ref files, and the Forgejo Actions CI that builds and publishes it. | Partitions and what they own: @@ -116,7 +116,7 @@ These were decided deliberately. Do not undo them without the maintainer's say-s 1. **Configuration is C++.** No JSON, YAML, TOML or DSL for project, runner or lint configuration. Lint rules are C++ callbacks; there is no clang-format or clang-tidy integration and no separate "format rule" type. A rule that calls `ctx.SetContent` is a transform, and that is the whole mechanism. 2. **Per-project artifacts go in `/build` and `/bin`. Only cross-project things go in `~/.cache/crafter.build/`:** the std PCM and Crafter.Build PCMs per target and march, and external clones keyed by their spec hash. -3. **Sources ship, PCMs build locally.** Packages install `*.cppm` under `share/crafter-build/`. Nothing target-baked ships except the executable itself, and that defaults to a portable `-march` in `PKGBUILD` and CI. +3. **Sources ship, PCMs build locally.** The Flatpak installs `*.cppm` under `/app/share/crafter-build/`. Nothing target-baked ships except the executable and the toolchain beside it, and the executable is built for a portable `-march`. 4. **crafter-build is a monolithic executable with `--export-dynamic`.** `project.so` resolves `Crafter::` symbols from the running binary at dlopen time. There is no Windows host; `x86_64-w64-mingw32` is a cross-compile target for user projects only. 5. **Convention over configuration.** Lead with the zero-config path. Env var, then CLI flag, then full `project.cpp` are the escalation, not the entry point. 6. **One skip mechanism** (runner probe plus exit 77) and **one `TestRunner` shape** (template fields with `{bin}`, `{bundle}` and similar placeholders; `Cmd(...)` covers any wrapper). Do not add a special-case runner class or a hardcoded "skip on this OS" branch. @@ -131,9 +131,9 @@ These were decided deliberately. Do not undo them without the maintainer's say-s - Subject line: `type(scope): imperative summary`, with `feat`, `fix`, `refactor`, `docs` or `test`; the scope (`lint`, `incremental`, ...) is optional. Body: what was wrong, why, and why the fix has this shape, wrapped at about 75 columns. Read `git log` for the voice. - Separate commits for the fix, its test and the README change when each stands on its own. - Update the README in the same PR when user-visible behaviour changes. -- `format`, `lint` and `test` green before pushing. CI runs the bootstrap and the test suite on PRs and publishes release archives on `master`. +- `format`, `lint` and `test` green before pushing. CI builds the Flatpak and runs the suite inside it on PRs, publishes the `beta` branch of the Catcrafts repo on every push to `master`, and the `stable` branch plus a release on an `X.Y.Z` tag. - Never commit `bin/`, `build/`, `share/crafter-build/`, `lib/*.a` or fixture build outputs (all gitignored), and never edit the vendored files in `lib/`. -- Keep CI's `CRAFTER_BUILD_MARCH=x86-64-v2` for the bootstrap: the runner's CPU cannot execute v3 code, and the v3 and v4 artifacts are produced from the v2 binary. +- Keep `CRAFTER_BUILD_MARCH=x86-64-v2` in the manifest for the shipped binary: it has to run on any x86_64 machine. User projects still default to `native`. ## Security considerations @@ -141,9 +141,15 @@ These were decided deliberately. Do not undo them without the maintainer's say-s - Unpinned dependencies `git pull` on every invocation, so builds and tests need network. A missing or private repo surfaces as a clone failure before anything is built. - `--runner=cmd:` executes the named wrapper around every test binary. Wrappers come from `--runner=` or `CRAFTER_BUILD_RUNNER_*`; do not hardcode hosts, wrappers or credentials in `project.cpp` or tests. - The linter loads `libclang` at runtime by name. `CRAFTER_BUILD_LIBCLANG` pins an exact path and must fail loudly when wrong, never fall back to another library. -- CI force-pushes the rolling `latest` tag and publishes a prerelease on every push to `master` with the workflow's own token. Do not add secrets to the workflow or echo the environment in it. +- CI force-pushes the rolling `latest` tag and publishes a prerelease on every push to `master` with the workflow's own token, and signs the Flatpak repo with the key in the `FLATPAK_GPG_PRIVATE_KEY` secret. Do not add other secrets to the workflow or echo the environment in it. - Tests write only under `fs::temp_directory_path()` and the fixture's own `bin/` and `build/`. Keep it that way. +## CI and publishing + +`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box (bubblewrap inside the job container needs it). Its `config.yml` allows three volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change) and `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`. Secrets: `FLATPAK_GPG_PRIVATE_KEY` (armored, no passphrase) and `FLATPAK_GPG_KEY_ID`. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it. + +To release: add a `` to `packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml` (its description becomes the release notes), merge, and push the tag `X.Y.Z`. CI refuses a tag that does not match the newest entry. Local builds: `flatpak-builder --user --force-clean --ccache --repo=repo build-dir packaging/flatpak/net.catcrafts.CrafterBuild.yaml`, then `flatpak-builder --run build-dir crafter-build --version`. + ## Gotchas - A `#pragma crafter dependency` target is rebuilt on every load on purpose: clang crashes rather than erroring when it imports a BMI whose own imported PCM was rebuilt underneath it, and the depfile check is what catches a regenerated host cache. Do not optimise the rebuild away. diff --git a/PKGBUILD b/PKGBUILD deleted file mode 100644 index 38299d1..0000000 --- a/PKGBUILD +++ /dev/null @@ -1,35 +0,0 @@ -#SPDX-License-Identifier: LGPL-3.0-only -#SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® - -pkgname=crafter-build -pkgver=0.1.0 -pkgrel=1 -pkgdesc='C++26 modules build system' -arch=('x86_64') -url='https://forgejo.catcrafts.net/Catcrafts/Crafter.Build' -license=('LGPL-3.0-only') -depends=('clang' 'libc++' 'lld') -optdepends=('wasi-libc: WASI/WebAssembly target support' - 'wasi-libc++: C++ standard library for WASI' - 'wasi-libc++abi: C++ ABI support for WASI') -makedepends=('cmake' 'git') -source=() -sha256sums=() -options=('!strip' '!debug' '!lto') - -build() { - cd "$startdir" - rm -rf bin build share/crafter-build - CRAFTER_BUILD_MARCH=x86-64-v3 CRAFTER_BUILD_MTUNE=generic ./build.sh -} - -package() { - cd "$startdir" - install -Dm755 bin/crafter-build "$pkgdir/usr/bin/crafter-build" - install -Dm644 lib/libcrafter-build.a "$pkgdir/usr/lib/libcrafter-build.a" - install -dm755 "$pkgdir/usr/share/crafter-build" - install -m644 share/crafter-build/*.cppm "$pkgdir/usr/share/crafter-build/" - install -dm755 "$pkgdir/usr/share/crafter-build/wasi-runtime" - install -m644 share/crafter-build/wasi-runtime/runtime.js "$pkgdir/usr/share/crafter-build/wasi-runtime/" - install -m644 share/crafter-build/wasi-runtime/index.html.in "$pkgdir/usr/share/crafter-build/wasi-runtime/" -} From 2b7c8f7f2dccbf17f95e20bc29c923539d29c43b Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 22:36:56 +0200 Subject: [PATCH 07/41] docs: install from the Catcrafts Flatpak repo The README leads with the Flatpak install, keeps the native loop as the way to build from source, and drops the tarball, musl and makepkg text that no longer describes anything CI produces. The install layout shows the /app tree and the per-app cache directory. Co-Authored-By: Claude Fable 5.1 --- AGENTS.md | 2 ++ README.md | 39 ++++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8a1fc1d..1ad41da 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,8 @@ Partitions and what they own: ## Dev environment +The shipped artifact is the Flatpak (see "CI and publishing"); the loop below is the native development loop. + Prerequisites on Linux: `clang` (with `libclang`, which the AST lint rules load at runtime), `lld`, `libc++` with its std module source (`share/libc++/v1/std.cppm`), `cmake`, `git`. `crafter-build --version` prints which compiler was resolved; `CRAFTER_BUILD_LLVM_DIR` points it at another LLVM prefix, and `build.sh` honours the same variable. Network access to `forgejo.catcrafts.net` and `github.com` is needed: loading `project.cpp` fetches Crafter.Math, Crafter.Asset and Crafter.LintRules into `~/.cache/crafter.build/external/`, and the bootstrap clones glslang. Those sources are unpinned, so every invocation runs `git pull` on them. ```bash diff --git a/README.md b/README.md index 400f9f1..5296f74 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,21 @@ A C++26 modules build system. Project descriptions are written in C++ with no JSON, no Lua, no embedded DSL. You write a `project.cpp` that constructs a `Configuration` and returns it; Crafter Build compiles, loads, and executes it. -## Quick start (Linux) +## Install -Bootstrap requires `clang`, `cmake`, `git`, `lld`, and `libc++`. +crafter-build ships as a Flatpak that carries its own clang, lld, libc++ and WASI sysroot, so every machine builds with the same compiler: + +```bash +flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo +flatpak install catcrafts net.catcrafts.CrafterBuild +alias crafter-build='flatpak run net.catcrafts.CrafterBuild' +``` + +Pre-release builds of `master` are the `beta` branch (`flatpak install catcrafts net.catcrafts.CrafterBuild//beta`), and every release also attaches a single-file bundle on the Forgejo releases page (`flatpak install --user crafter-build-.flatpak`). The sandbox sees the whole host filesystem, so projects can live anywhere. Binaries built for the host link the bundled `libc++.so.1` through an rpath into the Flatpak: they run inside it (`-r`, the test runner), and are not meant to be copied out onto the host. + +## Building from source (Linux) + +The development loop is native. It needs `clang`, `cmake`, `git`, `lld` and `libc++` with its std module source: ```bash ./build.sh # produces bin/crafter-build @@ -13,13 +25,7 @@ Bootstrap requires `clang`, `cmake`, `git`, `lld`, and `libc++`. The compiler is resolved once per run: `CRAFTER_BUILD_LLVM_DIR` if set, else a `clang++` installed beside crafter-build (`/bin`), else PATH. `crafter-build --version` prints the version and the compiler in use; `crafter-build --help` lists the other overrides (`CRAFTER_BUILD_LIBCXX_DIR`, `CRAFTER_BUILD_LIBCLANG`, `CRAFTER_BUILD_WASI_SYSROOT`, `CRAFTER_MINGW_DIR`). A `Configuration` targets the host triple unless `cfg.target` says otherwise. -The bootstrap targets the host toolchain's own triple (`clang++ -print-target-triple`): `x86_64-pc-linux-gnu` on glibc distros, `x86_64-alpine-linux-musl` on Alpine/postmarketOS (`apk add clang clang-dev lld libc++-dev llvm-libunwind-dev llvm-runtimes llvm llvm-dev cmake build-base python3 git`). Override with `CRAFTER_BUILD_TARGET=`. CI ships both: `crafter-build-linux-x86_64-v{2,3,4}.tar.gz` (glibc) and `crafter-build-linux-x86_64-musl-v2.tar.gz` (musl, needs `libc++` at runtime — the launcher dlopens `project.so`, so it cannot be static). - -To build the system as a distro package on Arch: - -```bash -makepkg -si # uses CRAFTER_BUILD_MARCH=x86-64-v3 by default for portability -``` +The bootstrap targets the toolchain's own triple (`clang++ -print-target-triple`); override with `CRAFTER_BUILD_TARGET=`. The Flatpak is built from `packaging/flatpak/net.catcrafts.CrafterBuild.yaml`, which runs this same bootstrap, the self-host build and the test suite inside the sandbox against the bundled toolchain (`CRAFTER_BUILD_LLVM_DIR=/app`). ## Writing a project.cpp @@ -103,13 +109,20 @@ import Crafter.LintRules; ## Install layout +Inside the Flatpak: + ``` -/bin/crafter-build # the executable -/lib/libcrafter-build.a # static archive for downstream consumers -/share/crafter-build/*.cppm # module sources (rebuilt on user machine on first run) -~/.cache/crafter.build/---/ # std.pcm + Crafter.Build*.pcm, built locally per toolchain +/app/bin/crafter-build # the executable +/app/bin/clang++ lld llvm-ar wasmtime # the bundled toolchain the executable resolves first +/app/lib/libcrafter-build.a # static archive for downstream consumers +/app/share/crafter-build/*.cppm # module sources (rebuilt on the user's machine on first run) +/app/share/wasi-sysroot/ # WASI sysroot for wasm32 targets +~/.var/app/net.catcrafts.CrafterBuild/cache/crafter.build/---/ + # std.pcm + Crafter.Build*.pcm, built locally per toolchain ``` +A source build uses the same shape under the checkout (`bin/`, `lib/`, `share/crafter-build/`) with the system compiler and `~/.cache/crafter.build/`. + ## Tests Tests live under `tests//`. The simplest case is a single C++ file: From 7eb837fd55db717fb5eb38361feb63515ce429d1 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 2 Oct 2026 23:51:23 +0200 Subject: [PATCH 08/41] fix(ci): sign with a key kept on the runner host The runner is our own machine, so the repo signing key does not need to pass through the forge as a secret. It is generated once under /etc/crafter-flatpak on the host and bind-mounted read-only into the job container, where each run imports it into a throwaway keyring. One less secret to manage, and the private key never leaves the box that uses it. Co-Authored-By: Claude Fable 5.1 --- .forgejo/workflows/ci.yaml | 19 +++++++++++++------ AGENTS.md | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index d011ec3..4b51b76 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -7,9 +7,10 @@ name: Flatpak # tag X.Y.Z -> publish as `stable` and create release X.Y.Z # # The `flatpak` runner label is the privileged runner on the Hetzner box -# (bubblewrap needs it) with three volumes allowed: the Sdk install, the +# (bubblewrap needs it) with four volumes allowed: the Sdk install, the # flatpak-builder state (ccache and the LLVM module, so only crafter-build -# itself rebuilds on a normal run) and the published repo's web root. +# itself rebuilds on a normal run), the published repo's web root, and the +# repo signing key, which lives on the runner host and never in the forge. on: pull_request: branches: [master] @@ -35,6 +36,7 @@ jobs: -v crafter-flatpak-system:/var/lib/flatpak -v crafter-flatpak-builder:/root/.cache/flatpak-builder -v /var/www/flatpak:/srv/flatpak + -v /etc/crafter-flatpak:/run/crafter-flatpak:ro steps: - name: Install tools run: | @@ -118,13 +120,18 @@ jobs: - name: Publish to the Catcrafts repo if: github.event_name != 'pull_request' - env: - FLATPAK_GPG_PRIVATE_KEY: ${{ secrets.FLATPAK_GPG_PRIVATE_KEY }} - FLATPAK_GPG_KEY_ID: ${{ secrets.FLATPAK_GPG_KEY_ID }} run: | set -eu + # The signing key is generated once on the runner host + # (/etc/crafter-flatpak: signing-key.asc and key-id) and mounted + # read-only; it is imported into a throwaway keyring per run. + if [ ! -s /run/crafter-flatpak/signing-key.asc ] || [ ! -s /run/crafter-flatpak/key-id ]; then + echo "no signing key under /etc/crafter-flatpak on the runner host; generate it as described in AGENTS.md (CI and publishing)" >&2 + exit 1 + fi export GNUPGHOME=$(mktemp -d) - printf '%s' "$FLATPAK_GPG_PRIVATE_KEY" | gpg --batch --quiet --import + gpg --batch --quiet --import /run/crafter-flatpak/signing-key.asc + FLATPAK_GPG_KEY_ID=$(cat /run/crafter-flatpak/key-id) # The live repo is the canonical copy; builds are promoted into it, # never built into it. [ -d "$REPO_DIR" ] || ostree init --repo="$REPO_DIR" --mode=archive diff --git a/AGENTS.md b/AGENTS.md index 1ad41da..63ee7e3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -143,12 +143,22 @@ These were decided deliberately. Do not undo them without the maintainer's say-s - Unpinned dependencies `git pull` on every invocation, so builds and tests need network. A missing or private repo surfaces as a clone failure before anything is built. - `--runner=cmd:` executes the named wrapper around every test binary. Wrappers come from `--runner=` or `CRAFTER_BUILD_RUNNER_*`; do not hardcode hosts, wrappers or credentials in `project.cpp` or tests. - The linter loads `libclang` at runtime by name. `CRAFTER_BUILD_LIBCLANG` pins an exact path and must fail loudly when wrong, never fall back to another library. -- CI force-pushes the rolling `latest` tag and publishes a prerelease on every push to `master` with the workflow's own token, and signs the Flatpak repo with the key in the `FLATPAK_GPG_PRIVATE_KEY` secret. Do not add other secrets to the workflow or echo the environment in it. +- CI force-pushes the rolling `latest` tag and publishes a prerelease on every push to `master` with the workflow's own token, and signs the Flatpak repo with the key mounted from the runner host. Do not add secrets to the workflow or echo the environment in it. - Tests write only under `fs::temp_directory_path()` and the fixture's own `bin/` and `build/`. Keep it that way. ## CI and publishing -`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box (bubblewrap inside the job container needs it). Its `config.yml` allows three volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change) and `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`. Secrets: `FLATPAK_GPG_PRIVATE_KEY` (armored, no passphrase) and `FLATPAK_GPG_KEY_ID`. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it. +`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box (bubblewrap inside the job container needs it). Its `config.yml` allows three volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change) and `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`, and `/etc/crafter-flatpak`, which holds the repo signing key (`signing-key.asc`, no passphrase, and `key-id`), generated once on the host and mounted read-only into the job; nothing is stored in the forge. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it. + +The key is made once, as root on the runner host, and the step fails with a message pointing here when it is missing: + +```bash +install -d -m 700 /etc/crafter-flatpak/gnupg && export GNUPGHOME=/etc/crafter-flatpak/gnupg +gpg --batch --pinentry-mode loopback --passphrase '' --quick-generate-key 'Catcrafts Flatpak Repository ' ed25519 sign never +KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}') +gpg --batch --pinentry-mode loopback --passphrase '' --export-secret-keys --armor "$KEYID" > /etc/crafter-flatpak/signing-key.asc +printf '%s\n' "$KEYID" > /etc/crafter-flatpak/key-id && chmod 600 /etc/crafter-flatpak/signing-key.asc /etc/crafter-flatpak/key-id +``` To release: add a `` to `packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml` (its description becomes the release notes), merge, and push the tag `X.Y.Z`. CI refuses a tag that does not match the newest entry. Local builds: `flatpak-builder --user --force-clean --ccache --repo=repo build-dir packaging/flatpak/net.catcrafts.CrafterBuild.yaml`, then `flatpak-builder --run build-dir crafter-build --version`. From b55fe31ddf673ede481e88c4ee4adc48579da027 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 14:17:24 +0200 Subject: [PATCH 09/41] docs: describe the dedicated flatpak runner The privileged runner that already existed is registered to another repository, so its labels cannot serve this one. CI runs on a separate privileged runner registered to Crafter.Build only, with four volumes rather than three now that the signing key is mounted from the host. Co-Authored-By: Claude Fable 5.1 --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 63ee7e3..9108767 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -148,7 +148,7 @@ These were decided deliberately. Do not undo them without the maintainer's say-s ## CI and publishing -`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box (bubblewrap inside the job container needs it). Its `config.yml` allows three volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change) and `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`, and `/etc/crafter-flatpak`, which holds the repo signing key (`signing-key.asc`, no passphrase, and `key-id`), generated once on the host and mounted read-only into the job; nothing is stored in the forge. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it. +`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: a dedicated privileged forgejo-runner on the Hetzner box (`forgejo-runner-flatpak.service`, config `/etc/forgejo-runner/config-flatpak.yaml`), registered to this repository only. It is privileged because bubblewrap inside the job container needs it, and separate from the other runners so that privilege is not shared with any other repository. Its config allows four volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change; a cold run takes about 80 minutes on 8 cores, a warm one a fraction of that), `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`, and `/etc/crafter-flatpak`, which holds the repo signing key (`signing-key.asc`, no passphrase, and `key-id`), generated once on the host and mounted read-only into the job; nothing is stored in the forge. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it. The key is made once, as root on the runner host, and the step fails with a message pointing here when it is missing: From 21746b188d1e13b8234e830c0396366c73f02562 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 14:40:09 +0200 Subject: [PATCH 10/41] fix(ci): initialise the published repo when its directory is empty The publish step created the OSTree repo only when the directory was missing. On the runner host the web root exists before the first publish, as an empty directory, so the init was skipped and build-commit-from failed with `opendir(objects)`. The check now looks for the repo's config file. Co-Authored-By: Claude Fable 5.1 --- .forgejo/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 4b51b76..bfe465b 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -134,7 +134,9 @@ jobs: FLATPAK_GPG_KEY_ID=$(cat /run/crafter-flatpak/key-id) # The live repo is the canonical copy; builds are promoted into it, # never built into it. - [ -d "$REPO_DIR" ] || ostree init --repo="$REPO_DIR" --mode=archive + # Keyed on the repo's config file, not the directory: the web root + # may exist as an empty directory before the first publish. + [ -f "$REPO_DIR/config" ] || ostree init --repo="$REPO_DIR" --mode=archive flatpak build-commit-from --src-repo=repo \ --gpg-sign="$FLATPAK_GPG_KEY_ID" --gpg-homedir="$GNUPGHOME" \ "$REPO_DIR" "app/$APP_ID/x86_64/${{ steps.meta.outputs.branch }}" From c7f984b95d6680681bb6255715e27d16fff20b02 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 15:41:57 +0200 Subject: [PATCH 11/41] fix(ci): name master builds after the release they follow Every non-tag build called itself 0.0.0-dev+, a placeholder that said nothing about which release it came after and put a `+` in the bundle's download URL. A master build now uses `git describe`, as a local checkout already does: 2.0.0-3-g21746b1 is three commits past 2.0.0. The placeholder remains only for history that has no tag yet. Three more things the tag path needed before its first run. The forgejo-release action takes the notes text in `release-notes` and a path in `release-notes-file`; the workflow passed a path to the former, so the release body would have been the file name. The tag filter is spelled with globs so it reads the same as a workflow pattern or a plain glob. Python, used to extract the notes from the metainfo, was only in the container as somebody else's dependency and is now listed. The 2.0.0 entry carries the date it is tagged on. Co-Authored-By: Claude Fable 5.1 --- .forgejo/workflows/ci.yaml | 19 +++++++++++++++---- .../net.catcrafts.CrafterBuild.metainfo.xml | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index bfe465b..daaafd6 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -16,7 +16,10 @@ on: branches: [master] push: branches: [master] - tags: ['[0-9]+.[0-9]+.[0-9]+'] + # Bare semver tags. Spelled with globs so it means the same whether the + # forge reads it as a workflow pattern or as a plain glob; `latest`, the + # rolling tag this workflow moves itself, does not start with a digit. + tags: ['[0-9]*.[0-9]*.[0-9]*'] workflow_dispatch: env: @@ -43,7 +46,7 @@ jobs: pacman-key --init pacman-key --populate archlinux pacman -Sy --noconfirm --needed archlinux-keyring - pacman -Syu --noconfirm --needed flatpak flatpak-builder ccache git nodejs jq gnupg go-yq ostree + pacman -Syu --noconfirm --needed flatpak flatpak-builder ccache git nodejs jq gnupg go-yq ostree python git config --global --add safe.directory '*' - name: Sdk @@ -74,7 +77,15 @@ jobs: exit 1 fi else - version="0.0.0-dev+$(git rev-parse --short HEAD)" + # A master build names itself after the release it follows, the + # way a local checkout does: 2.0.0-3-g21746b1 is three commits + # past 2.0.0. Before the first tag there is nothing to describe + # from and the bare hash gets a placeholder in front. + described=$(git describe --tags --always --exclude=latest) + case "$described" in + [0-9]*.[0-9]*.[0-9]*) version="$described" ;; + *) version="0.0.0-dev+$described" ;; + esac branch=beta fi echo "version=$version" >> "$GITHUB_OUTPUT" @@ -189,6 +200,6 @@ jobs: repo: ${{ github.repository }} tag: ${{ github.ref_name }} title: crafter-build ${{ github.ref_name }} - release-notes: release-notes.md + release-notes-file: release-notes.md release-dir: dist token: ${{ secrets.GITHUB_TOKEN }} diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml b/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml index 7108966..afe8428 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml @@ -25,7 +25,7 @@ - +

First release distributed as a Flatpak, with a bundled LLVM 22 toolchain and WASI sysroot. Windows host support is removed; crafter-build gains --version and resolves its compiler from one place.

From adc91ed9e2cf5be2bbc4164175256b66676adae8 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 16:04:43 +0200 Subject: [PATCH 12/41] fix(packaging): keep the per-commit version out of every module's cache key flatpak-builder printed "Building module llvm" on every CI run instead of "Cache hit". The build environment, CRAFTER_BUILD_VERSION included, sat in the manifest's top-level build options, and those are part of every module's cache key; the version changes with each commit, so no module ever hit. Runs still took twelve minutes rather than eighty only because ccache made the LLVM rebuild cheap. The variables only matter to crafter-build's own module, so they move there, and CI rewrites the version on that module. LLVM, the WASI sysroot and wasmtime are now keyed on nothing that changes between commits. Co-Authored-By: Claude Fable 5.1 --- .forgejo/workflows/ci.yaml | 2 +- .../flatpak/net.catcrafts.CrafterBuild.yaml | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index daaafd6..0c5d79a 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -99,7 +99,7 @@ jobs: VERSION: ${{ steps.meta.outputs.version }} run: | set -eu - yq '."build-options".env.CRAFTER_BUILD_VERSION = strenv(VERSION)' "$MANIFEST" > "$CI_MANIFEST" + yq '(.modules[] | select(.name == "crafter-build") | ."build-options".env.CRAFTER_BUILD_VERSION) = strenv(VERSION)' "$MANIFEST" > "$CI_MANIFEST" grep -n 'CRAFTER_BUILD_VERSION' "$CI_MANIFEST" flatpak-builder --force-clean --ccache --disable-rofiles-fuse \ --state-dir=/root/.cache/flatpak-builder \ diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml index a8b769a..9eeb64f 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -28,15 +28,6 @@ finish-args: # gdb and friends from the Sdk - --allow=devel - --env=PATH=/app/bin:/usr/bin -build-options: - env: - CRAFTER_BUILD_LLVM_DIR: /app - # The shipped binary runs on any x86_64 from the last decade; user - # projects still build with -march=native by default. - CRAFTER_BUILD_MARCH: x86-64-v2 - CRAFTER_BUILD_MTUNE: generic - # CI replaces this with the tag; a local build is a development build. - CRAFTER_BUILD_VERSION: 0.0.0-dev cleanup: - /share/man - /share/doc @@ -188,6 +179,18 @@ modules: # (sibling repos tracking their default branch, see AGENTS.md). build-args: - --share=network + # On this module, not at the top of the manifest: top-level build + # options are part of every module's cache key, and the version changes + # with every commit, which made flatpak-builder rebuild LLVM each run. + env: + CRAFTER_BUILD_LLVM_DIR: /app + # The shipped binary runs on any x86_64 from the last decade; user + # projects still build with -march=native by default. + CRAFTER_BUILD_MARCH: x86-64-v2 + CRAFTER_BUILD_MTUNE: generic + # CI replaces this with the version; a local build is a development + # build. + CRAFTER_BUILD_VERSION: 0.0.0-dev build-commands: # Bootstrap by hand, then let the result build itself: only the # self-hosted binary has asset support and ThinLTO. From e15cd3c14d280b4d212b03ef8c252e0dd0d1bfd7 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 16:37:00 +0200 Subject: [PATCH 13/41] fix(sysroot): take the std module source from the sysroot's libc++ release A cross build against a sysroot whose libc++ differs from the toolchain's failed while precompiling the std module, with a page of "no member named 'runtime_format' in namespace 'std'". The module source is a list of using-declarations for what one libc++ release declares, so it only compiles against that release's headers. BuildStdPcm wants the sysroot's own /usr/share/libc++/v1/std.cppm, but Alpine packages the headers without it, and the fallback was the toolchain's copy whatever its release. That held while the compiler came from the same distro as the sysroot; with the Flatpak's libc++ 22.1.8 and Alpine edge on 23.1.2 it no longer does. The release is now read from _LIBCPP_VERSION in the sysroot's and the toolchain's <__config>, and SysrootStdModule picks in this order: the sysroot's own source, the toolchain's when both are the same major release (a release branch only takes fixes), the copy crafter-build ships for that release under share/crafter-build/libc++-modules/. When none fits the build stops with one line naming both releases and the directory the matching source belongs in, before clang is run. If either release cannot be read the toolchain's source is tried as before, since there is nothing to compare. Matching sources live in crafter-build's own share directory rather than the LLVM prefix: they are data this tool ships for sysroots, not part of any one compiler install, and "sources ship" already describes that directory. `--version` prints the toolchain's libc++ release and the releases sources are shipped for, the first thing to look at when such a build fails. Co-Authored-By: Claude Fable 5.1 --- implementations/Crafter.Build-Clang.cpp | 14 +++++ implementations/Crafter.Build-Platform.cpp | 15 +++-- implementations/Crafter.Build-Toolchain.cpp | 66 +++++++++++++++++++-- interfaces/Crafter.Build-Toolchain.cppm | 31 ++++++++++ 4 files changed, 112 insertions(+), 14 deletions(-) diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index da2ddc4..d2aee22 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -1537,6 +1537,20 @@ static void PrintVersion() { return; } std::println("compiler {} ({})", tc.clangVersion, tc.clangxx.string()); + if (tc.libcxxVersion != 0) { + // A sysroot on another libc++ release builds only when its std + // module source is in the sysroot or among these; printed here + // because that is the first question when such a build fails. + std::vector shipped; + std::error_code ec; + for (const fs::directory_entry& entry : fs::directory_iterator(tc.stdModulesDir, ec)) { + if (fs::exists(entry.path() / "std.cppm")) shipped.push_back(entry.path().filename().string()); + } + std::ranges::sort(shipped); + std::string also; + for (const std::string& release : shipped) also += std::format(", {}", release); + std::println("libc++ {}{}", LibcxxVersionString(tc.libcxxVersion), also.empty() ? std::string() : std::format(" (std module sources for sysroots also for{})", also.substr(1))); + } std::println("host {}", tc.hostTriple); } diff --git a/implementations/Crafter.Build-Platform.cpp b/implementations/Crafter.Build-Platform.cpp index a6f8793..f8a7694 100644 --- a/implementations/Crafter.Build-Platform.cpp +++ b/implementations/Crafter.Build-Platform.cpp @@ -663,14 +663,13 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) { } else if (config.sysroot.empty()) { stdCppm = GetToolchain().stdCppm.string(); } else { - stdCppm = std::format("{}/usr/share/libc++/v1/std.cppm", config.sysroot); - // not every distro ships the std module sources (Alpine's - // libc++-dev doesn't); the host copy is interchangeable as long - // as the libc++ versions line up, which the compile of the PCM - // against the sysroot's headers verifies anyway - if (!fs::exists(stdCppm) && fs::exists(GetToolchain().stdCppm)) { - stdCppm = GetToolchain().stdCppm.string(); - } + // The source has to belong to the libc++ release whose headers + // the sysroot carries. Handing clang the toolchain's own copy + // regardless used to end in a page of "no member named ... in + // namespace 'std'" once the two releases differed. + StdModuleSource source = SysrootStdModule(GetToolchain(), config.sysroot); + if (!source.error.empty()) return source.error; + stdCppm = source.stdCppm.string(); } std::string sysrootFlag = config.sysroot.empty() ? std::string() diff --git a/implementations/Crafter.Build-Toolchain.cpp b/implementations/Crafter.Build-Toolchain.cpp index d7e4868..5c232ce 100644 --- a/implementations/Crafter.Build-Toolchain.cpp +++ b/implementations/Crafter.Build-Toolchain.cpp @@ -112,15 +112,19 @@ Toolchain Crafter::ResolveToolchain(const ToolchainInputs& in) { // 3. libc++ and its std module source. The compiler's own copy lives at // /share/libc++/v1 when the prefix carries libc++; the system - // copy is the last resort either way. - const fs::path systemStdCppm = "/usr/share/libc++/v1/std.cppm"; + // copy is the last resort either way. The headers of the same install + // say which release the source belongs to. + fs::path libcxxRoot = "/usr"; if (std::string_view dir = Env(in, "CRAFTER_BUILD_LIBCXX_DIR"); !dir.empty()) { tc.libcxxDir = fs::path(dir); - tc.stdCppm = tc.libcxxDir / "share" / "libc++" / "v1" / "std.cppm"; + libcxxRoot = tc.libcxxDir; } else if (!tc.prefix.empty() && fs::exists(tc.prefix / "share" / "libc++" / "v1" / "std.cppm")) { - tc.stdCppm = tc.prefix / "share" / "libc++" / "v1" / "std.cppm"; - } else { - tc.stdCppm = systemStdCppm; + libcxxRoot = tc.prefix; + } + tc.stdCppm = libcxxRoot / "share" / "libc++" / "v1" / "std.cppm"; + tc.libcxxVersion = LibcxxVersion(libcxxRoot / "include" / "c++" / "v1"); + if (!in.crafterPrefix.empty()) { + tc.stdModulesDir = in.crafterPrefix / "share" / "crafter-build" / "libc++-modules"; } // 4. Sysroots for the cross targets that have a conventional location. @@ -180,6 +184,56 @@ const Toolchain& Crafter::RequireToolchain() { return tc; } +std::int32_t Crafter::LibcxxVersion(const fs::path& includeDir) { + // "# define _LIBCPP_VERSION 230102", the one place libc++ states its + // release in a form the preprocessor does not have to be run for. + constexpr std::string_view Marker = "define _LIBCPP_VERSION "; + std::ifstream config(includeDir / "__config"); + std::string line; + while (std::getline(config, line)) { + const std::size_t at = line.find(Marker); + if (at == std::string::npos) continue; + const std::string_view digits = std::string_view(line).substr(at + Marker.size()); + std::int32_t version = 0; + if (std::from_chars(digits.data(), digits.data() + digits.size(), version).ec == std::errc{}) return version; + } + return 0; +} + +std::string Crafter::LibcxxVersionString(std::int32_t version) { + return std::format("{}.{}.{}", version / 10000, version / 100 % 100, version % 100); +} + +StdModuleSource Crafter::SysrootStdModule(const Toolchain& tc, const fs::path& sysroot) { + const fs::path own = sysroot / "usr" / "share" / "libc++" / "v1" / "std.cppm"; + if (fs::exists(own)) return {own, {}}; + + const fs::path includeDir = sysroot / "usr" / "include" / "c++" / "v1"; + if (!fs::exists(includeDir / "__config")) { + return {{}, std::format("sysroot {} has no libc++ headers at {}; install its libc++ development package", sysroot.string(), includeDir.string())}; + } + const std::int32_t wanted = LibcxxVersion(includeDir); + const bool comparable = wanted != 0 && tc.libcxxVersion != 0; + // Releases are matched on the major version: a release branch only takes + // fixes, so 22.1.3 headers and the 22.1.8 module source name the same + // declarations. + if (comparable && wanted / 10000 == tc.libcxxVersion / 10000 && fs::exists(tc.stdCppm)) return {tc.stdCppm, {}}; + if (wanted != 0 && !tc.stdModulesDir.empty()) { + const fs::path shipped = tc.stdModulesDir / std::to_string(wanted / 10000) / "std.cppm"; + if (fs::exists(shipped)) return {shipped, {}}; + } + // One of the two releases is unknown, so there is nothing to compare: + // hand over the toolchain's source and let the compile decide, which is + // what happened for every sysroot before releases were compared. + if (!comparable && fs::exists(tc.stdCppm)) return {tc.stdCppm, {}}; + + const std::string sysrootRelease = wanted != 0 ? LibcxxVersionString(wanted) : std::string("of an unknown release"); + const std::string toolchainSource = !fs::exists(tc.stdCppm) + ? std::string("the toolchain has no std module source at all") + : std::format("the toolchain's is for libc++ {}", LibcxxVersionString(tc.libcxxVersion)); + return {{}, std::format("sysroot {} carries libc++ {} without its std module source, and crafter-build has none for that release ({}); install the share/libc++/v1 directory of that libc++ release (std.cppm and std/) as {}", sysroot.string(), sysrootRelease, toolchainSource, own.parent_path().string())}; +} + std::string_view Crafter::CrafterBuildVersion() { return CRAFTER_BUILD_VERSION; } diff --git a/interfaces/Crafter.Build-Toolchain.cppm b/interfaces/Crafter.Build-Toolchain.cppm index 853b2b6..05d1d26 100644 --- a/interfaces/Crafter.Build-Toolchain.cppm +++ b/interfaces/Crafter.Build-Toolchain.cppm @@ -28,6 +28,14 @@ export namespace Crafter { fs::path libcxxDir; // libc++'s std module source for host builds. fs::path stdCppm; + // The _LIBCPP_VERSION of the libc++ stdCppm belongs to (220108 for + // 22.1.8); 0 when its <__config> could not be read. + std::int32_t libcxxVersion = 0; + // Std module sources for libc++ releases other than the toolchain's + // own, one /std.cppm each, for sysroots that carry libc++ + // without them: /share/crafter-build/libc++-modules. + // Empty when the crafter prefix is unknown; need not exist. + fs::path stdModulesDir; fs::path wasiSysroot; // Holds include/c++//bits/std.cc for the mingw target. Passed to // clang as --sysroot only when it was set explicitly: clang's own @@ -74,6 +82,29 @@ export namespace Crafter { const Toolchain& GetToolchain(); // GetToolchain(), or a std::runtime_error carrying its error. const Toolchain& RequireToolchain(); + // _LIBCPP_VERSION as the <__config> in `includeDir` (an include/c++/v1) + // defines it, XXYYZZ; 0 when the file is missing or has no such line. + std::int32_t LibcxxVersion(const fs::path& includeDir); + // "23.1.2" for 230102. + std::string LibcxxVersionString(std::int32_t version); + + struct StdModuleSource { + fs::path stdCppm; + // One line saying why no source fits; empty when stdCppm is set. + std::string error; + }; + // The std module source to precompile for a native (non-wasm) build + // against `sysroot`. The module source is a list of using-declarations + // for names the headers of the same libc++ release declare, so one from + // another release fails on every name that release added or dropped. In + // order: the sysroot's own /usr/share/libc++/v1/std.cppm, the + // toolchain's when both are the same major release, the copy for the + // sysroot's release under toolchain.stdModulesDir. Not every distro + // packages the module sources with its libc++ (Alpine does not), which + // is why the sysroot's own copy cannot be the only answer. When the + // sysroot's release cannot be read, the toolchain's source is returned + // and the compile is the check. + StdModuleSource SysrootStdModule(const Toolchain& toolchain, const fs::path& sysroot); // The version baked in when crafter-build was built; "0.0.0-dev" for a // build that was given none. std::string_view CrafterBuildVersion(); From cbf2b94f0642a96ab341d430273de55464c09f2d Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 16:37:00 +0200 Subject: [PATCH 14/41] test(sysroot): cover the std module source choice for a sysroot build SysrootStdModule runs over staged layouts that need no compiler: a sysroot with its own module source, one on a point release of the toolchain's libc++, one on the next major release with a shipped source, one nothing was shipped for, one without libc++ at all, and the two cases where a release cannot be read. The last block drives Build() against a sysroot claiming libc++ 99 and expects the one-line explanation back instead of compiler output, which is the failure the fix replaces. ToolchainResolve additionally checks that the release is read from the libc++ the module source comes from (the prefix's, an explicit libc++ install's, the system's) and where the shipped sources are looked up. Co-Authored-By: Claude Fable 5.1 --- project.cpp | 1 + tests/SysrootStdModule/main.cpp | 156 ++++++++++++++++++++++++++++++++ tests/ToolchainResolve/main.cpp | 11 ++- 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 tests/SysrootStdModule/main.cpp diff --git a/project.cpp b/project.cpp index d3364d3..a7c3272 100644 --- a/project.cpp +++ b/project.cpp @@ -129,6 +129,7 @@ extern "C" Configuration CrafterBuildProject(std::span a cfg.AddTest("TestRunnerSpec").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("VariantId").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("ToolchainResolve").Dependencies({ CrafterBuildLib.get() }); + cfg.AddTest("SysrootStdModule").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("VersionFlag").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("WasiBrowserRuntime").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("WasmVariants").Dependencies({ CrafterBuildLib.get() }); diff --git a/tests/SysrootStdModule/main.cpp b/tests/SysrootStdModule/main.cpp new file mode 100644 index 0000000..e5e984f --- /dev/null +++ b/tests/SysrootStdModule/main.cpp @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +import std; +import Crafter.Build; +namespace fs = std::filesystem; +using namespace Crafter; + +// A sysroot build precompiles the std module from a module source that has to +// belong to the libc++ release whose headers the sysroot carries. Not every +// distro packages that source with its libc++ (Alpine does not), and the +// fallback used to be the toolchain's own copy whatever its release: with a +// libc++ 22 toolchain and a libc++ 23 sysroot the build died on a page of "no +// member named 'runtime_format' in namespace 'std'" that named neither +// release nor what to do about it. +// +// Staged, compiler-free layouts cover the order SysrootStdModule picks in: the +// sysroot's own source, the toolchain's for the same major release, the copy +// crafter-build ships for another release, and otherwise one line naming both +// releases and where the matching source belongs. The last block drives +// Build() against a sysroot nothing matches and expects that line back in +// place of compiler output. +namespace { + std::int32_t Failures = 0; + + void Check(bool cond, std::string_view msg) { + if (!cond) { + std::println(std::cerr, "FAIL: {}", msg); + ++Failures; + } + } + + void Write(const fs::path& file, std::string_view content) { + fs::create_directories(file.parent_path()); + std::ofstream(file) << content; + } + + // What libc++ installs as include/c++/v1/__config, reduced to the line + // that states the release. + void StageHeaders(const fs::path& includeDir, std::int32_t version) { + Write(includeDir / "__config", std::format("#ifndef _LIBCPP___CONFIG\n# define _LIBCPP___CONFIG\n# define _LIBCPP_VERSION {}\n#endif\n", version)); + } + + fs::path StageSysroot(const fs::path& sysroot, std::int32_t version) { + StageHeaders(sysroot / "usr" / "include" / "c++" / "v1", version); + return sysroot; + } +} + +int main() { + const fs::path scratch = fs::temp_directory_path() / "crafter-build-sysroot-std-module"; + std::error_code ec; + fs::remove_all(scratch, ec); + fs::create_directories(scratch); + + { + StageHeaders(scratch / "headers", 230102); + Check(LibcxxVersion(scratch / "headers") == 230102, "the release is read from __config"); + Check(LibcxxVersion(scratch / "no-such-dir") == 0, "missing headers read as an unknown release"); + Write(scratch / "other" / "__config", "#define _LIBCPP_ABI_VERSION 1\n"); + Check(LibcxxVersion(scratch / "other") == 0, "a __config without the version line reads as an unknown release"); + Check(LibcxxVersionString(230102) == "23.1.2" && LibcxxVersionString(220108) == "22.1.8", "XXYYZZ is spelled as a dotted release"); + } + + // A toolchain on libc++ 22.1.8 that also ships the sources for 23. + Toolchain tc; + tc.stdCppm = scratch / "toolchain" / "share" / "libc++" / "v1" / "std.cppm"; + tc.libcxxVersion = 220108; + tc.stdModulesDir = scratch / "shipped"; + Write(tc.stdCppm, ""); + Write(tc.stdModulesDir / "23" / "std.cppm", ""); + + { + // The sysroot's own source matches its headers by construction, so + // it wins whatever the releases are. + const fs::path sysroot = StageSysroot(scratch / "sysroot-own", 240100); + const fs::path own = sysroot / "usr" / "share" / "libc++" / "v1" / "std.cppm"; + Write(own, ""); + StdModuleSource source = SysrootStdModule(tc, sysroot); + Check(source.error.empty() && source.stdCppm == own, "a sysroot that ships the module source uses its own"); + } + { + const fs::path sysroot = StageSysroot(scratch / "sysroot-same-major", 220103); + StdModuleSource source = SysrootStdModule(tc, sysroot); + Check(source.error.empty() && source.stdCppm == tc.stdCppm, "a point release of the toolchain's libc++ uses the toolchain's source"); + } + { + // The reported case: libc++ 23 headers, a libc++ 22 toolchain. + const fs::path sysroot = StageSysroot(scratch / "sysroot-next-major", 230102); + StdModuleSource source = SysrootStdModule(tc, sysroot); + Check(source.error.empty() && source.stdCppm == tc.stdModulesDir / "23" / "std.cppm", "another release uses the source shipped for it, not the toolchain's"); + } + { + const fs::path sysroot = StageSysroot(scratch / "sysroot-unshipped", 210108); + StdModuleSource source = SysrootStdModule(tc, sysroot); + Check(source.stdCppm.empty() && !source.error.empty(), "a release nothing was shipped for is an error, not a compile against the wrong source"); + Check(source.error.contains("21.1.8") && source.error.contains("22.1.8"), std::format("the error names both releases, got '{}'", source.error)); + Check(source.error.contains((sysroot / "usr" / "share" / "libc++" / "v1").string()), std::format("the error says where the matching source belongs, got '{}'", source.error)); + Check(!source.error.contains('\n'), "the error is one line"); + } + { + const fs::path sysroot = scratch / "sysroot-no-libcxx"; + fs::create_directories(sysroot / "usr" / "include"); + StdModuleSource source = SysrootStdModule(tc, sysroot); + Check(source.stdCppm.empty() && source.error.contains("no libc++ headers"), std::format("a sysroot without libc++ says so, got '{}'", source.error)); + } + { + // Nothing to compare when either release is unknown: the toolchain's + // source goes to the compiler, as it did before releases were read. + const fs::path sysroot = scratch / "sysroot-unreadable"; + Write(sysroot / "usr" / "include" / "c++" / "v1" / "__config", "#define _LIBCPP_ABI_VERSION 1\n"); + Check(SysrootStdModule(tc, sysroot).stdCppm == tc.stdCppm, "an unreadable sysroot release falls back to the toolchain's source"); + Toolchain unknown = tc; + unknown.libcxxVersion = 0; + unknown.stdModulesDir.clear(); + Check(SysrootStdModule(unknown, StageSysroot(scratch / "sysroot-vs-unknown", 230102)).stdCppm == tc.stdCppm, "an unreadable toolchain release falls back to the toolchain's source"); + } + { + Toolchain bare; + bare.stdCppm = scratch / "nowhere" / "std.cppm"; + bare.libcxxVersion = 220108; + StdModuleSource source = SysrootStdModule(bare, StageSysroot(scratch / "sysroot-vs-bare", 220108)); + Check(source.stdCppm.empty() && source.error.contains("no std module source"), std::format("a toolchain without a module source is an error too, got '{}'", source.error)); + } + + { + // End to end with the real toolchain: a sysroot on a libc++ release + // that does not exist fails the build with the one-line explanation + // before clang is run. The target is the host's so the std PCM cache + // directory is the one every other test already uses. + const Toolchain& real = GetToolchain(); + Check(real.libcxxVersion != 0, "the running toolchain's libc++ release is read from its headers"); + + const fs::path project = scratch / "project"; + Write(project / "main.cpp", "int main() { return 0; }\n"); + Configuration app; + app.path = project; + app.name = "sysroot-std-module"; + app.outputName = "sysroot-std-module"; + app.target = HostTarget(); + app.type = ConfigurationType::Executable; + app.sysroot = StageSysroot(scratch / "sysroot-future", 990000).string(); + std::array ifaces = {}; + std::array impls = { "main" }; + app.GetInterfacesAndImplementations(ifaces, impls); + + std::unordered_map> depResults; + std::mutex depMutex; + BuildResult r = Build(app, depResults, depMutex); + Check(r.result.contains("libc++ 99.0.0"), std::format("the build fails naming the sysroot's release, got '{}'", r.result)); + Check(r.result.contains(LibcxxVersionString(real.libcxxVersion)), std::format("the build fails naming the toolchain's release, got '{}'", r.result)); + Check(!r.result.contains('\n') && !r.result.contains("error:"), std::format("the failure is one line, not compiler output, got '{}'", r.result)); + } + + return Failures == 0 ? 0 : 1; +} diff --git a/tests/ToolchainResolve/main.cpp b/tests/ToolchainResolve/main.cpp index 51d8af6..d535c6b 100644 --- a/tests/ToolchainResolve/main.cpp +++ b/tests/ToolchainResolve/main.cpp @@ -40,9 +40,11 @@ int main() { // A prefix with everything crafter-build looks for beside the compiler, // and one with the compiler alone. const fs::path full = scratch / "full"; - for (const char* rel : {"bin/clang++", "bin/clang", "bin/llvm-ar", "lib/libclang.so", "include/clang-c/Index.h", "share/libc++/v1/std.cppm", "share/wasi-sysroot/include/.keep"}) { + for (const char* rel : {"bin/clang++", "bin/clang", "bin/llvm-ar", "lib/libclang.so", "include/clang-c/Index.h", "include/c++/v1/__config", "share/libc++/v1/std.cppm", "share/wasi-sysroot/include/.keep"}) { Touch(full / rel); } + // The prefix's libc++ headers state its release. + std::ofstream(full / "include/c++/v1/__config") << "# define _LIBCPP_VERSION 230102\n"; const fs::path bare = scratch / "bare"; Touch(bare / "bin/clang++"); @@ -56,6 +58,8 @@ int main() { Check(!tc.libclang.has_value(), "the PATH fallback searches libclang by name"); Check(tc.llvmIncludeDir.empty(), "the PATH fallback adds no include dir"); Check(tc.stdCppm == "/usr/share/libc++/v1/std.cppm", "the system std module source is the default"); + Check(tc.libcxxVersion == LibcxxVersion("/usr/include/c++/v1"), "the system libc++ headers state the release of the system module source"); + Check(tc.stdModulesDir.empty(), "no crafter-build prefix, no shipped module sources"); Check(tc.wasiSysroot == "/usr/share/wasi-sysroot", "the system wasi sysroot is the default"); Check(tc.mingwSysroot == "/usr/x86_64-w64-mingw32" && !tc.mingwSysrootExplicit, "the default mingw dir is not passed as --sysroot"); Check(tc.LibcxxCompileFlags().empty() && tc.LibcxxLinkFlags().empty(), "the PATH fallback adds no libc++ flags"); @@ -71,6 +75,8 @@ int main() { Check(tc.libclang == full / "lib/libclang.so", "libclang beside the compiler is pinned"); Check(tc.llvmIncludeDir == full / "include", "clang-c headers beside the compiler are added to the include path"); Check(tc.stdCppm == full / "share/libc++/v1/std.cppm", "the prefix's std module source is used"); + Check(tc.libcxxVersion == 230102, "the prefix's libc++ headers state its release"); + Check(tc.stdModulesDir == full / "share/crafter-build/libc++-modules", "module sources for other releases are looked up in crafter-build's own share directory"); Check(tc.wasiSysroot == full / "share/wasi-sysroot", "the prefix's wasi sysroot is used"); Check(tc.LibcxxCompileFlags().empty(), "the compiler finds its own libc++ headers without flags"); Check(Contains(tc.LibcxxLinkFlags(), std::format("-L{}", (full / "lib").string())) && Contains(tc.LibcxxLinkFlags(), std::format("-Wl,-rpath,{}", (full / "lib").string())), "binaries get -L and an rpath for the prefix's lib dir"); @@ -120,12 +126,15 @@ int main() { // An explicit libc++ install: headers, library and std module all // come from it, whatever the compiler would have used. const fs::path libcxx = scratch / "libcxx"; + fs::create_directories(libcxx / "include/c++/v1"); + std::ofstream(libcxx / "include/c++/v1/__config") << "# define _LIBCPP_VERSION 210108\n"; ToolchainInputs in; in.crafterPrefix = full; in.env["CRAFTER_BUILD_LIBCXX_DIR"] = libcxx.string(); Toolchain tc = ResolveToolchain(in); Check(tc.libcxxDir == libcxx, "CRAFTER_BUILD_LIBCXX_DIR is recorded"); Check(tc.stdCppm == libcxx / "share/libc++/v1/std.cppm", "the std module source follows the explicit libc++"); + Check(tc.libcxxVersion == 210108, "the release is read from the explicit libc++, not the compiler's"); Check(Contains(tc.LibcxxCompileFlags(), std::format("-nostdinc++ -isystem {}", (libcxx / "include/c++/v1").string())), "compiles use the explicit libc++ headers"); Check(Contains(tc.LibcxxLinkFlags(), std::format("-L{}", (libcxx / "lib").string())) && Contains(tc.LibcxxLinkFlags(), std::format("-Wl,-rpath,{}", (libcxx / "lib").string())), "links use the explicit libc++ library dir, with an rpath"); } From 8dccb88b78b3522eefbd4ba981936bf19c764f8c Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 17:01:21 +0200 Subject: [PATCH 15/41] feat(packaging): bundle LLVM 23.1.2 LLVM 23 has been the stable series since September, and the distros crafter-build cross-builds for have moved to it: Alpine edge ships libc++ 23.1.2, which is what surfaced the std module mismatch fixed in the previous commits. wasi-sdk 34 already builds against a newer LLVM than 22 upstream, so the WASI sysroot loses nothing by following. The bump exposed a patch step that never ran. wasi-sdk patches its LLVM tree with `git apply` in src/llvm-project, which upstream is a submodule. Here that directory is a tarball unpacked inside the wasi-sdk checkout, and git, finding the outer repository, treats every patched path as outside the current directory, prints "Skipped patch" and exits 0. LLVM 22 built without the three patches; libc++ 23 does not compile for WASI without the one that keeps the z/OS locale shim out of other platforms' builds (strtod_l redeclared with an abi_tag). `git init` on the unpacked tree makes it a repository of its own, so wasi-sdk's step applies whatever patch list the pinned tag carries, as it does upstream, and fails loudly when a patch no longer fits. Built locally: the whole manifest, the suite inside the sandbox (26 passed, WasmHelloRuns under the bundled wasmtime included), and aarch64 musl cross builds against Alpine edge and v3.24 sysroots that run under qemu. The native development loop is unaffected and stays on whatever clang the machine has. Co-Authored-By: Claude Fable 5.1 --- .../flatpak/net.catcrafts.CrafterBuild.yaml | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml index 9eeb64f..e95288d 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -106,8 +106,8 @@ modules: - /bin/scan-view sources: - type: archive - url: https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz - sha256: 922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888 + url: https://github.com/llvm/llvm-project/releases/download/llvmorg-23.1.2/llvm-project-23.1.2.src.tar.xz + sha256: c98bbef08a2b4c2613cd50e9aa9ae7b69b1fe6c16b2c40373bc0ab6116fdf78a - name: wasi-sysroot # wasi-sdk's sysroot-only mode, driven by the clang built above: @@ -155,9 +155,21 @@ modules: commit: 2e6fb9d8ee0cdf9e431fbcabe8af3115de000a13 dest: src/wasi-libc - type: archive - url: https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz - sha256: 922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888 + url: https://github.com/llvm/llvm-project/releases/download/llvmorg-23.1.2/llvm-project-23.1.2.src.tar.xz + sha256: c98bbef08a2b4c2613cd50e9aa9ae7b69b1fe6c16b2c40373bc0ab6116fdf78a dest: src/llvm-project + # wasi-sdk patches its LLVM tree before building libc++ (`git apply` in + # src/llvm-project), and upstream that directory is a submodule, a + # repository of its own. Unpacked from a tarball it is a plain + # subdirectory of the wasi-sdk checkout, where git treats the patched + # paths as outside the current directory, skips every one of them and + # exits 0. LLVM 22 happened to build without the patches; LLVM 23's + # libc++ does not compile for WASI without one of them. Making the tree + # a repository lets wasi-sdk's own patch step do what it does upstream, + # with whatever patch list the pinned tag carries. + - type: shell + commands: + - git init -q src/llvm-project - type: file path: wasi-host-toolchain.cmake From 4159ee4117f1c8ba8258ad9f065eedc1dc547acb Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 17:01:21 +0200 Subject: [PATCH 16/41] feat(packaging): ship libc++ 22's std module source for sysroots With LLVM 23 bundled, a sysroot still on libc++ 22 without its own module source (Alpine v3.24) is the mirror image of the case that started this: the toolchain's source is a release ahead of the sysroot's headers. The new libcxx-22-modules module installs libc++ 22.1.8's module source under share/crafter-build/libc++-modules/22, where SysrootStdModule looks for the release a sysroot is on. It only configures libc++'s runtimes build and installs the cxx-modules component: std.cppm as configure generates it and the .inc files from the source tree, nothing compiled, about 600 KB installed. The component is staged and copied because it also writes lib/libc++.modules.json, which belongs to the bundled libc++. Verified in the built Flatpak: `crafter-build --version` lists release 22, and a cross build against an Alpine v3.24 aarch64 sysroot (libc++ 22.1.3) precompiles that source with clang 23, links and runs under qemu. Co-Authored-By: Claude Fable 5.1 --- .../flatpak/net.catcrafts.CrafterBuild.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml index e95288d..7eb8644 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -183,6 +183,33 @@ modules: url: https://github.com/bytecodealliance/wasmtime/releases/download/v49.0.2/wasmtime-v49.0.2-x86_64-linux.tar.xz sha256: a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 + - name: libcxx-22-modules + # The std module source of the libc++ release before the bundled one, for + # cross sysroots that carry that libc++ without it: Alpine packages the + # headers and the library but not share/libc++/v1, and a module source + # only compiles against the headers of its own release. crafter-build + # picks /std.cppm here by the sysroot's _LIBCPP_VERSION + # (SysrootStdModule in Crafter.Build:Toolchain). Add a module like this + # one per release worth supporting; the bundled clang has to be able to + # compile that release's headers. + buildsystem: simple + build-commands: + # Configure only. The cxx-modules component is std.cppm as configure + # generates it plus the .inc files straight from the source tree, so + # nothing is compiled, and libc++'s own install rule stays the one + # place that knows what a module source install consists of. + - cmake -S runtimes -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/app/bin/clang -DCMAKE_CXX_COMPILER=/app/bin/clang++ "-DLLVM_ENABLE_RUNTIMES=libcxx;libcxxabi" -DLIBCXXABI_USE_LLVM_UNWINDER=OFF -DLIBCXX_INSTALL_MODULES=ON + # Staged, not installed to /app: the component also writes + # lib/libc++.modules.json, which would replace the bundled libc++'s. + - cmake --install build --component cxx-modules --prefix "$PWD/stage" + - grep -q "_LIBCPP_VERSION 22" libcxx/include/__config + - mkdir -p /app/share/crafter-build/libc++-modules + - cp -r stage/share/libc++/v1 /app/share/crafter-build/libc++-modules/22 + sources: + - type: archive + url: https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz + sha256: 922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888 + - name: crafter-build buildsystem: simple build-options: From 4e5d17b9e9fb7e0d4638b4a23ec49436471cbd11 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 17:01:21 +0200 Subject: [PATCH 17/41] docs: describe how a sysroot build finds its std module source The README said a cross build precompiles the sysroot's own std.cppm, which was never the whole story for sysroots that do not ship one. It now states the order (the sysroot's, the toolchain's for the same major release, the sources crafter-build carries for other releases), what the failure looks like when nothing matches and how to resolve it, and lists the new directory in the install layout. AGENTS.md records the rule for the next LLVM bump: the release the bundled toolchain leaves behind stays available as a module-source module, because stable distros remain on it long after. Co-Authored-By: Claude Fable 5.1 --- AGENTS.md | 3 ++- README.md | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9108767..091807a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ crafter-build is a C++26 modules build system. A project is described by a `proj Partitions and what they own: -- `:Toolchain` resolves the compiler once per process (`GetToolchain`): clang++, clang, llvm-ar, libclang, libc++'s std module source and the default sysroots, from `CRAFTER_BUILD_LLVM_DIR`, else a toolchain installed beside crafter-build, else PATH. It also holds the version baked in at build time. +- `:Toolchain` resolves the compiler once per process (`GetToolchain`): clang++, clang, llvm-ar, libclang, libc++'s std module source and the default sysroots, from `CRAFTER_BUILD_LLVM_DIR`, else a toolchain installed beside crafter-build, else PATH. It also holds the version baked in at build time. `SysrootStdModule` picks the std module source for a sysroot build by the sysroot's libc++ release. - `:Clang` holds `Configuration`, `Build()`, `VariantId()`, `ApplyStandardArgs`, `HostTarget`, the CLI entry point `Crafter::Run`, and the lint and test data types. - `:Platform` runs commands, implements `LoadProject` (compile and load `project.cpp`), the host PCM cache, `#pragma crafter dependency`, and all OS-specific code. - `:Interface` and `:Implementation` model module units, their staleness checks and their compiles. @@ -169,4 +169,5 @@ To release: add a `` to `packaging/flatpak/net.catcrafts.CrafterBuild.m - `crafter-build clean` leaves a pragma dependency's build alone; it lives with the clone in the cache. - The bootstrap binary is compiled without `CRAFTER_BUILD_HAS_ASSET`, so `cfg.assets` only works in the self-hosted binary. Changing asset extensions means updating two maps in `Crafter.Build-Clang.cpp`: `assetOutput` and the transitive copy loop. - A warning is printed only when its unit actually recompiles. A build with nothing to do is silent, which is not evidence that a warning is gone. +- A std module source only compiles against the headers of its own libc++ release, and a sysroot does not always ship one (Alpine). The Flatpak therefore carries the sources of the release next to the bundled one under `share/crafter-build/libc++-modules//`, one `libcxx--modules` manifest module each. When the bundled LLVM is bumped, keep the release it leaves behind as such a module: sysroots on the stable distro stay on it for months. The native loop has no such directory; stage one by hand under `share/crafter-build/` to test against a foreign sysroot. - `VariantId()` names the output directory, so CI globs `bin/crafter.build-exe---*/` rather than predicting the hash. diff --git a/README.md b/README.md index 5296f74..c195fb2 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,8 @@ Inside the Flatpak: /app/bin/clang++ lld llvm-ar wasmtime # the bundled toolchain the executable resolves first /app/lib/libcrafter-build.a # static archive for downstream consumers /app/share/crafter-build/*.cppm # module sources (rebuilt on the user's machine on first run) +/app/share/crafter-build/libc++-modules// + # std module sources of other libc++ releases, for sysroots without their own /app/share/wasi-sysroot/ # WASI sysroot for wasm32 targets ~/.var/app/net.catcrafts.CrafterBuild/cache/crafter.build/---/ # std.pcm + Crafter.Build*.pcm, built locally per toolchain @@ -174,7 +176,7 @@ TestRunner::Cmd("wine") // local exec via prefix co For elaborate setups (rsync, ssh ControlMaster, env vars on the spawn, `QEMU_LD_PREFIX` for sysroots), construct `TestRunner` directly — `copy`/`exec`/`cleanup`/`probe` are public string fields with `{bundle}`, `{remote_bundle}`, `{bin}`, `{bin_name}`, `{args}` placeholders. -For cross-arch builds, set `cfg.sysroot` on the `Configuration` to point at a sysroot for the target. `BuildStdPcm` and the compile/link commands both consume it: `--sysroot=` is passed to clang, and `/usr/share/libc++/v1/std.cppm` is precompiled instead of the host's. The cheapest aarch64 sysroot is an extracted [Arch Linux ARM rootfs](http://os.archlinuxarm.org/) plus `libc++` + `libc++abi` + `gcc` installed into it via `pacman --root --config --dbpath /var/lib/pacman -Sy …` (point pacman at ALARM's mirrors via explicit config to avoid pulling host-arch packages). +For cross-arch builds, set `cfg.sysroot` on the `Configuration` to point at a sysroot for the target. `BuildStdPcm` and the compile/link commands both consume it: `--sysroot=` is passed to clang, and the std module is precompiled against the sysroot's libc++ headers. The module source has to come from the same libc++ release as those headers, so it is taken from `/usr/share/libc++/v1/std.cppm` when the sysroot ships it, else from the toolchain when both are the same major release, else from the sources crafter-build carries for other releases (`share/crafter-build/libc++-modules//`; `crafter-build --version` lists them). Alpine is the common case for the last two: its `libc++-dev` has the headers and no module source. When nothing matches, the build stops with one line naming both releases; installing that release's `share/libc++/v1` directory into the sysroot resolves it. The cheapest aarch64 sysroot is an extracted [Arch Linux ARM rootfs](http://os.archlinuxarm.org/) plus `libc++` + `libc++abi` + `gcc` installed into it via `pacman --root --config --dbpath /var/lib/pacman -Sy …` (point pacman at ALARM's mirrors via explicit config to avoid pulling host-arch packages). For `--target=x86_64-w64-mingw32` (Windows from Linux), the pipeline cross-compiles as long as `mingw-w64` is installed. Pair it with `--runner=cmd:wine` to run the result. Only the MinGW ABI is supported; there is no MSVC target and no Windows host. `-fuse-ld=lld` is auto-injected on every link, and `-lstdc++exp` is auto-linked for the mingw target so user projects don't need to declare those flags. From 6e9eedfd2179f1b22ac918f4ed881d7553d46d18 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 19:04:47 +0200 Subject: [PATCH 18/41] fix(packaging): check out the WASI sysroot's LLVM tree from git The previous commit made the LLVM tree inside the wasi-sdk checkout a git repository with `git init`, so that wasi-sdk's patch step would apply. That was half the fix: wasi-sdk also runs `git rev-parse HEAD` in that tree to write the sysroot's VERSION file, which on an empty repository raises a traceback during configure and leaves VERSION without its llvm lines. The build went on regardless, so the suite passed and the published beta carries the truncated file. Fetching the tree as a git source pinned to the release tag gives wasi-sdk what it has upstream, a submodule checkout at a commit, and removes the shell step instead of stacking a second workaround on it. The llvm module keeps the release tarball: it is the same commit, and a tarball is the cheaper download for a tree that is only compiled. flatpak-builder keeps a mirror of the clone in its state directory, so the fetch costs the cold run about 300 MB once. Verified locally: no traceback, VERSION lists llvm 85ac56026243 and llvm-version 23.1.2, the eight patch steps apply, the suite passes in the sandbox and a wasm32-wasip1 program runs under the bundled wasmtime. Co-Authored-By: Claude Fable 5.1 --- .../flatpak/net.catcrafts.CrafterBuild.yaml | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml index 7eb8644..3584063 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.yaml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.yaml @@ -113,7 +113,7 @@ modules: # wasi-sdk's sysroot-only mode, driven by the clang built above: # wasi-libc, compiler-rt builtins (installed into our clang's resource # dir, where the wasm32 driver looks for them) and libc++ for wasip1 and - # wasip1-threads. The llvm-project tree is the same tarball as above so + # wasip1-threads. The llvm-project tree is the same release as above so # the wasm libc++ matches the native one. buildsystem: cmake-ninja builddir: true @@ -154,22 +154,20 @@ modules: url: https://github.com/WebAssembly/wasi-libc.git commit: 2e6fb9d8ee0cdf9e431fbcabe8af3115de000a13 dest: src/wasi-libc - - type: archive - url: https://github.com/llvm/llvm-project/releases/download/llvmorg-23.1.2/llvm-project-23.1.2.src.tar.xz - sha256: c98bbef08a2b4c2613cd50e9aa9ae7b69b1fe6c16b2c40373bc0ab6116fdf78a - dest: src/llvm-project - # wasi-sdk patches its LLVM tree before building libc++ (`git apply` in - # src/llvm-project), and upstream that directory is a submodule, a - # repository of its own. Unpacked from a tarball it is a plain + # A git checkout, where the llvm module above unpacks the release + # tarball of the same tag: wasi-sdk treats src/llvm-project as the + # submodule it is upstream. It patches the tree with `git apply` before + # building libc++, and records `git rev-parse HEAD` in the sysroot's + # VERSION file. Unpacked from a tarball the directory is a plain # subdirectory of the wasi-sdk checkout, where git treats the patched # paths as outside the current directory, skips every one of them and # exits 0. LLVM 22 happened to build without the patches; LLVM 23's - # libc++ does not compile for WASI without one of them. Making the tree - # a repository lets wasi-sdk's own patch step do what it does upstream, - # with whatever patch list the pinned tag carries. - - type: shell - commands: - - git init -q src/llvm-project + # libc++ does not compile for WASI without one of them. + - type: git + url: https://github.com/llvm/llvm-project.git + tag: llvmorg-23.1.2 + commit: 85ac560262434c9ccfc0c183ec22d4138ed647fb + dest: src/llvm-project - type: file path: wasi-host-toolchain.cmake From b5a366d060e26d2f88aad4c976f39f598718f0e8 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 20:09:22 +0200 Subject: [PATCH 19/41] fix(incremental): key build outputs on the compiler A project built with one clang kept its outputs when crafter-build started using another. The output directory is named by VariantId(), which hashed every input that changes what is compiled except the compiler itself, and staleness is decided by mtimes, which a compiler update leaves alone. A PCM can only be read by the clang release that wrote it, so after the bundled LLVM moved from 22 to 23 the first build found nothing stale, and the first edit failed with "module file Greeter.pcm uses an older format that is no longer supported" until the project was cleaned by hand. A project with a `#pragma crafter dependency` did not get that far: its project file imports the dependency's PCM, so it failed to load, and for a git dependency that PCM lives in the cache, where `crafter-build clean` does not reach. The same happens natively when the distro updates clang. The toolchain's key now goes into the hash, as the host PCM cache was already keyed on it. An update starts an empty variant directory and rebuilds into it; the previous one stays until `clean`, as after any other change of variant, and two crafter-build versions can work in one checkout without disturbing each other. VariantId() gains an overload taking the toolchain so the dependence can be tested in-process, the way ResolveToolchain and SysrootStdModule take theirs. External cmake builds are not covered: their directory is fixed per clone and keyed on the compiler's path, not its release. Objects of two clang releases link together on the host, so nothing fails there, but they are not rebuilt either. Verified by replaying the update: three projects built with the 2.0.0 Flatpak, then built, edited and run with this one without cleaning, then built with 2.0.0 again. Co-Authored-By: Claude Fable 5.1 --- interfaces/Crafter.Build-Clang.cppm | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/interfaces/Crafter.Build-Clang.cppm b/interfaces/Crafter.Build-Clang.cppm index 1149661..8a306be 100644 --- a/interfaces/Crafter.Build-Clang.cppm +++ b/interfaces/Crafter.Build-Clang.cppm @@ -7,6 +7,7 @@ import :Shader; import :Interface; import :Implementation; import :External; +import :Toolchain; namespace fs = std::filesystem; export namespace Crafter { @@ -488,10 +489,21 @@ export namespace Crafter { void AddAstLintRule(std::string name, std::function check); // Suffix that uniquely identifies this Configuration's compile state. // target+march+mtune are spelled out for readability; the rest - // (type, debug, sysroot, defines, compileFlags) collapse into a short - // hash so two Configurations sharing a path but with different - // compile state can't clobber each other's outputs. - std::string VariantId() const { + // (type, debug, sysroot, defines, compileFlags, the toolchain) + // collapse into a short hash so two Configurations sharing a path but + // with different compile state can't clobber each other's outputs. + std::string VariantId() const { return VariantId(GetToolchain()); } + // The variant under an explicit toolchain; VariantId() is this for + // the running one. The compiler is compile state like any flag: a + // PCM can only be read by the clang release that wrote it, and + // staleness is decided by mtimes, which a compiler update does not + // touch. Before the toolchain was hashed, a project built with clang + // 22 kept its PCMs when crafter-build moved to clang 23, and the + // first edit afterwards failed with "module file ... uses an older + // format that is no longer supported" until the project was cleaned. + // Keyed here, an update starts an empty directory and rebuilds; the + // old one stays until `crafter-build clean`. + std::string VariantId(const Toolchain& toolchain) const { std::string compileKey; compileKey += std::to_string(static_cast(type)); compileKey += '|'; @@ -521,6 +533,8 @@ export namespace Crafter { compileKey += "|A:"; compileKey += a; } + compileKey += "|T:"; + compileKey += toolchain.Key(); std::size_t configHash = std::hash{}(compileKey); return std::format("{}-{}-{}-{}-{:08x}", name, target, march, mtune, configHash); } From 995a40e7dd627b9920c1b00214844d0f039bc0a1 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 20:09:22 +0200 Subject: [PATCH 20/41] test(incremental): a compiler update perturbs VariantId Two toolchains that differ only in the clang release, as the Flatpak's do across an update, must give one Configuration two variants; so must the same release installed under another prefix. The no-argument form is checked to be the variant under the running toolchain, and BuildDir to be named for it. Co-Authored-By: Claude Fable 5.1 --- tests/VariantId/main.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/VariantId/main.cpp b/tests/VariantId/main.cpp index 022b0d6..e4d2a36 100644 --- a/tests/VariantId/main.cpp +++ b/tests/VariantId/main.cpp @@ -133,6 +133,33 @@ int main() { Check(a.BuildDir() != b.BuildDir(), "different VariantId yields different BuildDir"); Check(a.BinDir() != b.BinDir(), "different VariantId yields different BinDir"); } + { + // The compiler is compile state as much as any flag: a PCM can only + // be read by the clang release that wrote it, and nothing in a + // project's sources changes when the compiler does. With the + // toolchain left out of the id, a project built with clang 22 kept + // its PCMs after crafter-build moved to clang 23; the build after + // the update found nothing stale, and the first edit failed with + // "module file ... uses an older format that is no longer supported" + // until the project was cleaned by hand. + Configuration cfg = MakeBase(); + Toolchain older; + older.clangxx = "/app/bin/clang++"; + older.clangVersion = "clang version 22.1.8"; + Toolchain newer = older; + newer.clangVersion = "clang version 23.1.2"; + Check(cfg.VariantId(older) != cfg.VariantId(newer), "a compiler update at the same path perturbs VariantId"); + Check(cfg.VariantId(older) == cfg.VariantId(older), "VariantId under one toolchain is deterministic"); + + // Two installs of one release are still two toolchains: their + // libc++ need not be the same build. + Toolchain elsewhere = older; + elsewhere.clangxx = "/usr/bin/clang++"; + Check(cfg.VariantId(older) != cfg.VariantId(elsewhere), "the same release installed elsewhere perturbs VariantId"); + + Check(cfg.VariantId() == cfg.VariantId(GetToolchain()), "VariantId() is the variant under the running toolchain"); + Check(cfg.BuildDir().filename() == cfg.VariantId(GetToolchain()), "BuildDir is named for the running toolchain's variant"); + } if (Failures > 0) { std::println(std::cerr, "{} assertions failed", Failures); From 03728fd4a7b1372628a829f12e7a16922bd95b15 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 20:09:22 +0200 Subject: [PATCH 21/41] docs: describe the output directory name and what a compiler update does The README still gave the output path as bin/--/, which has not been its shape since the variant hash was introduced. It now shows the real one and says what the hash stands for, including that a compiler update starts a new directory and `clean` removes the old. AGENTS.md lists the toolchain among VariantId()'s inputs and notes that a glob over variant directories matches two after an update. Co-Authored-By: Claude Fable 5.1 --- AGENTS.md | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 091807a..574abeb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -110,7 +110,7 @@ Beyond the rules: - There is no host-platform preprocessor split: the host is Linux. Target differences (mingw, wasm) branch on `cfg.target` strings at runtime, in the library as well as in `project.cpp` and tests. - A build step fails when its command exits nonzero, never because it printed. Use `RunBuildCommand` or `RunCommandChecked`; warnings go to `Progress::Diagnostic`. - Every output path goes through `Configuration::BinDir()`, `BuildDir()` and `PcmDir()`. Hand-rolled `path / "bin" / ...` clobbered variants before; do not add new ones. -- Anything that changes what gets compiled or bundled belongs in `VariantId()`. It already hashes type, debug, sysroot, defines, compile flags, wasm variant flags and uninterpreted project args. +- Anything that changes what gets compiled or bundled belongs in `VariantId()`. It already hashes type, debug, sysroot, defines, compile flags, wasm variant flags, uninterpreted project args and the toolchain (`Toolchain::Key()`). ## Design rules @@ -170,4 +170,4 @@ To release: add a `` to `packaging/flatpak/net.catcrafts.CrafterBuild.m - The bootstrap binary is compiled without `CRAFTER_BUILD_HAS_ASSET`, so `cfg.assets` only works in the self-hosted binary. Changing asset extensions means updating two maps in `Crafter.Build-Clang.cpp`: `assetOutput` and the transitive copy loop. - A warning is printed only when its unit actually recompiles. A build with nothing to do is silent, which is not evidence that a warning is gone. - A std module source only compiles against the headers of its own libc++ release, and a sysroot does not always ship one (Alpine). The Flatpak therefore carries the sources of the release next to the bundled one under `share/crafter-build/libc++-modules//`, one `libcxx--modules` manifest module each. When the bundled LLVM is bumped, keep the release it leaves behind as such a module: sysroots on the stable distro stay on it for months. The native loop has no such directory; stage one by hand under `share/crafter-build/` to test against a foreign sysroot. -- `VariantId()` names the output directory, so CI globs `bin/crafter.build-exe---*/` rather than predicting the hash. +- `VariantId()` names the output directory, so CI globs `bin/crafter.build-exe---*/` rather than predicting the hash. The hash includes the compiler, so after a toolchain update the previous variant's directories stay in `bin/` and `build/` until `clean`, and such a glob matches both. diff --git a/README.md b/README.md index c195fb2..07ae901 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ extern "C" Configuration CrafterBuildProject(std::span a } ``` -Run `crafter-build` in that directory; outputs land at `bin/myapp--/myapp` and intermediates at `build/myapp--/`. Add `-r` to run the produced executable straight after a successful build. +Run `crafter-build` in that directory; outputs land at `bin/myapp----/myapp` and intermediates at `build/myapp----/`. The hash covers everything that changes what is compiled (build type, defines, flags, sysroot, the compiler itself), so two such builds never share a directory. After a compiler update, a new crafter-build release included, the first build starts a fresh directory and recompiles; `crafter-build clean` removes the ones left behind. Add `-r` to run the produced executable straight after a successful build. ## Dependencies From 486935bce862623279737db5b14da70d0d07104e Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 20:09:22 +0200 Subject: [PATCH 22/41] docs(packaging): add the 2.1.0 release entry CI takes the newest as the notes of a tag and refuses a tag that does not match it. 2.1.0 covers what changed since 2.0.0: the bundled LLVM 23.1.2, build outputs kept per compiler, and the std module source chosen by the sysroot's libc++ release. Co-Authored-By: Claude Fable 5.1 --- packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml b/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml index afe8428..e63ee54 100644 --- a/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml +++ b/packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml @@ -25,6 +25,12 @@ + + +

The bundled toolchain is now LLVM 23.1.2. Build outputs are kept per compiler, so the first build after updating recompiles each project into a fresh directory and nothing has to be cleaned by hand.

+

Cross builds pick the std module source that matches the sysroot's libc++ release. The source for libc++ 22 ships beside the bundled 23 for sysroots that carry libc++ without one, such as Alpine, and a sysroot nothing matches fails with one line naming both releases. crafter-build --version prints the libc++ release in use.

+
+

First release distributed as a Flatpak, with a bundled LLVM 22 toolchain and WASI sysroot. Windows host support is removed; crafter-build gains --version and resolves its compiler from one place.

From 7abbdf503bdd23f7fa6c2b9caf459ad51b131101 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 21:14:30 +0200 Subject: [PATCH 23/41] readme change --- README.md | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/README.md b/README.md index 07ae901..ebfc905 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,6 @@ alias crafter-build='flatpak run net.catcrafts.CrafterBuild' Pre-release builds of `master` are the `beta` branch (`flatpak install catcrafts net.catcrafts.CrafterBuild//beta`), and every release also attaches a single-file bundle on the Forgejo releases page (`flatpak install --user crafter-build-.flatpak`). The sandbox sees the whole host filesystem, so projects can live anywhere. Binaries built for the host link the bundled `libc++.so.1` through an rpath into the Flatpak: they run inside it (`-r`, the test runner), and are not meant to be copied out onto the host. -## Building from source (Linux) - -The development loop is native. It needs `clang`, `cmake`, `git`, `lld` and `libc++` with its std module source: - -```bash -./build.sh # produces bin/crafter-build -./bin/crafter-build # rebuild via project.cpp -``` - -The compiler is resolved once per run: `CRAFTER_BUILD_LLVM_DIR` if set, else a `clang++` installed beside crafter-build (`/bin`), else PATH. `crafter-build --version` prints the version and the compiler in use; `crafter-build --help` lists the other overrides (`CRAFTER_BUILD_LIBCXX_DIR`, `CRAFTER_BUILD_LIBCLANG`, `CRAFTER_BUILD_WASI_SYSROOT`, `CRAFTER_MINGW_DIR`). A `Configuration` targets the host triple unless `cfg.target` says otherwise. - -The bootstrap targets the toolchain's own triple (`clang++ -print-target-triple`); override with `CRAFTER_BUILD_TARGET=`. The Flatpak is built from `packaging/flatpak/net.catcrafts.CrafterBuild.yaml`, which runs this same bootstrap, the self-host build and the test suite inside the sandbox against the bundled toolchain (`CRAFTER_BUILD_LLVM_DIR=/app`). - ## Writing a project.cpp Crafter Build loads a `project.cpp` from the current directory. The file exports one function that returns a populated `Configuration`: @@ -107,24 +94,6 @@ import Crafter.LintRules; #pragma crafter dependency "" [branch "" | commit ""] [project ""] ``` -## Install layout - -Inside the Flatpak: - -``` -/app/bin/crafter-build # the executable -/app/bin/clang++ lld llvm-ar wasmtime # the bundled toolchain the executable resolves first -/app/lib/libcrafter-build.a # static archive for downstream consumers -/app/share/crafter-build/*.cppm # module sources (rebuilt on the user's machine on first run) -/app/share/crafter-build/libc++-modules// - # std module sources of other libc++ releases, for sysroots without their own -/app/share/wasi-sysroot/ # WASI sysroot for wasm32 targets -~/.var/app/net.catcrafts.CrafterBuild/cache/crafter.build/---/ - # std.pcm + Crafter.Build*.pcm, built locally per toolchain -``` - -A source build uses the same shape under the checkout (`bin/`, `lib/`, `share/crafter-build/`) with the system compiler and `~/.cache/crafter.build/`. - ## Tests Tests live under `tests//`. The simplest case is a single C++ file: From c6b0bbaf25266256361560b8c46772f9688ba767 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 3 Oct 2026 21:16:01 +0200 Subject: [PATCH 24/41] readme change --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ebfc905..590251b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Crafter Build -A C++26 modules build system. Project descriptions are written in C++ with no JSON, no Lua, no embedded DSL. You write a `project.cpp` that constructs a `Configuration` and returns it; Crafter Build compiles, loads, and executes it. +A C++26 modules build system. Project descriptions are written in C++ with no JSON, no Lua, no embedded DSL. You write a `project.cpp` that constructs a `Configuration` and returns it. Crafter Build compiles, loads, and executes it. ## Install @@ -39,7 +39,7 @@ extern "C" Configuration CrafterBuildProject(std::span a } ``` -Run `crafter-build` in that directory; outputs land at `bin/myapp----/myapp` and intermediates at `build/myapp----/`. The hash covers everything that changes what is compiled (build type, defines, flags, sysroot, the compiler itself), so two such builds never share a directory. After a compiler update, a new crafter-build release included, the first build starts a fresh directory and recompiles; `crafter-build clean` removes the ones left behind. Add `-r` to run the produced executable straight after a successful build. +Run `crafter-build` in that directory, outputs land at `bin/myapp----/myapp` and intermediates at `build/myapp----/`. The hash covers everything that changes what is compiled (build type, defines, flags, sysroot, the compiler itself), so two such builds never share a directory. After a compiler update, a new crafter-build release included, the first build starts a fresh directory and recompiles. `crafter-build clean` removes the ones left behind. Add `-r` to run the produced executable straight after a successful build. ## Dependencies @@ -75,7 +75,7 @@ Configuration* sibling = LocalProject({ .projectFile = "../Crafter.Math/project. cfg.dependencies = { math }; ``` -Clones share the external cache and identical specs share one `Configuration`. An unpinned source is `git pull`ed on every invocation; set `.commit` for reproducible, offline-after-first-fetch builds. +Clones share the external cache and identical specs share one `Configuration`. An unpinned source is `git pull`ed on every invocation, set `.commit` for reproducible, offline-after-first-fetch builds. A project loaded this way runs its `CrafterBuildProject` as a dependency, and `IsTopLevelProject()` is `false` while it does (`true` in the project crafter-build was pointed at, and outside any project file). Wire test-only dependencies behind it: a library whose test links crafter-build itself would otherwise load crafter-build's `project.cpp` from inside crafter-build and recurse. @@ -112,7 +112,7 @@ Test conventions: - **Exit codes**: `0` = ✅ pass, nonzero = ❌ fail, **`77`** = ⏭ skipped (autoconf convention; use it for runtime opt-outs like "tool not on PATH"). - Each test runs in its own subprocess — a segfault doesn't take the runner down. -- Default timeout is 60 s per test; runs in parallel up to `hardware_concurrency()`. +- Default timeout is 60 s per test, runs in parallel up to `hardware_concurrency()`. - Stdout+stderr is captured and printed atomically on completion. Failed/crashed/timed-out tests also write to `build/test-logs/.log`. - A broken fixture `project.cpp` is reported as a single failed test, not a fatal error — other tests still run. @@ -130,7 +130,7 @@ crafter-build test --timeout=5 # override per-t Outcomes: ✅ pass, ❌ fail, 💥 crash (with signal name), ⏱ timeout, ⏭ skipped. The runner exits 0 only if every non-skipped test passed. -`--target=` filters the run to tests whose `cfg.target` matches; default is the host triple. Per-target tests parse `--target=...` from their own args span and build for it; tests with hardcoded targets (e.g. host-only meta-tests) are filtered out under non-matching `--target=`. +`--target=` filters the run to tests whose `cfg.target` matches, default is the host triple. Per-target tests parse `--target=...` from their own args span and build for it, tests with hardcoded targets (e.g. host-only meta-tests) are filtered out under non-matching `--target=`. ### Cross-target test runners @@ -145,9 +145,9 @@ TestRunner::Cmd("wine") // local exec via prefix co For elaborate setups (rsync, ssh ControlMaster, env vars on the spawn, `QEMU_LD_PREFIX` for sysroots), construct `TestRunner` directly — `copy`/`exec`/`cleanup`/`probe` are public string fields with `{bundle}`, `{remote_bundle}`, `{bin}`, `{bin_name}`, `{args}` placeholders. -For cross-arch builds, set `cfg.sysroot` on the `Configuration` to point at a sysroot for the target. `BuildStdPcm` and the compile/link commands both consume it: `--sysroot=` is passed to clang, and the std module is precompiled against the sysroot's libc++ headers. The module source has to come from the same libc++ release as those headers, so it is taken from `/usr/share/libc++/v1/std.cppm` when the sysroot ships it, else from the toolchain when both are the same major release, else from the sources crafter-build carries for other releases (`share/crafter-build/libc++-modules//`; `crafter-build --version` lists them). Alpine is the common case for the last two: its `libc++-dev` has the headers and no module source. When nothing matches, the build stops with one line naming both releases; installing that release's `share/libc++/v1` directory into the sysroot resolves it. The cheapest aarch64 sysroot is an extracted [Arch Linux ARM rootfs](http://os.archlinuxarm.org/) plus `libc++` + `libc++abi` + `gcc` installed into it via `pacman --root --config --dbpath /var/lib/pacman -Sy …` (point pacman at ALARM's mirrors via explicit config to avoid pulling host-arch packages). +For cross-arch builds, set `cfg.sysroot` on the `Configuration` to point at a sysroot for the target. `BuildStdPcm` and the compile/link commands both consume it: `--sysroot=` is passed to clang, and the std module is precompiled against the sysroot's libc++ headers. The module source has to come from the same libc++ release as those headers, so it is taken from `/usr/share/libc++/v1/std.cppm` when the sysroot ships it, else from the toolchain when both are the same major release, else from the sources crafter-build carries for other releases (`share/crafter-build/libc++-modules//`, `crafter-build --version` lists them). Alpine is the common case for the last two: its `libc++-dev` has the headers and no module source. When nothing matches, the build stops with one line naming both releases, installing that release's `share/libc++/v1` directory into the sysroot resolves it. The cheapest aarch64 sysroot is an extracted [Arch Linux ARM rootfs](http://os.archlinuxarm.org/) plus `libc++` + `libc++abi` + `gcc` installed into it via `pacman --root --config --dbpath /var/lib/pacman -Sy …` (point pacman at ALARM's mirrors via explicit config to avoid pulling host-arch packages). -For `--target=x86_64-w64-mingw32` (Windows from Linux), the pipeline cross-compiles as long as `mingw-w64` is installed. Pair it with `--runner=cmd:wine` to run the result. Only the MinGW ABI is supported; there is no MSVC target and no Windows host. `-fuse-ld=lld` is auto-injected on every link, and `-lstdc++exp` is auto-linked for the mingw target so user projects don't need to declare those flags. +For `--target=x86_64-w64-mingw32` (Windows from Linux), the pipeline cross-compiles as long as `mingw-w64` is installed. Pair it with `--runner=cmd:wine` to run the result. Only the MinGW ABI is supported, there is no MSVC target and no Windows host. `-fuse-ld=lld` is auto-injected on every link, and `-lstdc++exp` is auto-linked for the mingw target so user projects don't need to declare those flags. ### Configuring runners @@ -176,7 +176,7 @@ local cmd: # cmd:wine, cmd:qemu-aarch64, cmd:valgrind, ... ``` -Each runner carries a `probe` command (e.g. `which wine`); if it fails, every test using that runner is reported as ⏭ skipped and the rest of the suite continues. Skipped tests count toward the summary but don't fail the suite (`AllPassed()` returns true on skip-only runs). +Each runner carries a `probe` command (e.g. `which wine`), if it fails, every test using that runner is reported as ⏭ skipped and the rest of the suite continues. Skipped tests count toward the summary but don't fail the suite (`AllPassed()` returns true on skip-only runs). ### Linking the parent project's library @@ -203,7 +203,7 @@ extern "C" Configuration CrafterBuildProject(std::span) ## Linting & formatting -Lint rules are C++ in `project.cpp` with no `.lintrc`, no YAML, and no built-in rules. A rule is a named callback that runs once per source file; register it on the `Configuration` before returning: +Lint rules are C++ in `project.cpp` with no `.lintrc`, no YAML, and no built-in rules. A rule is a named callback that runs once per source file, register it on the `Configuration` before returning: ```cpp cfg.AddLintRule("no-tabs", [](Crafter::LintContext& ctx) { @@ -244,7 +244,7 @@ Lint findings print compiler-style — `file:line: warning: message [rule]` — | `Extension()` | file extension, e.g. `".cppm"` | | `CommentStripped()` | `content` with comments and string/char literal bodies blanked to spaces — newlines kept, so line numbers stay valid (raw strings not recognized yet) | | `Report(line, msg)` | record a finding (`line == 0` for whole-file) | -| `SetContent(str)` | replace the file's content — makes the rule a transform; `lines` re-splits, `CommentStripped()` re-derives. Build the new string, call once at the end; don't assign `content` directly | +| `SetContent(str)` | replace the file's content — makes the rule a transform, `lines` re-splits, `CommentStripped()` re-derives. Build the new string, call once at the end, don't assign `content` directly | Suppression is comment-based : @@ -275,7 +275,7 @@ This project is licensed under the GNU Lesser General Public License, version 3 The example code under [examples/](examples/) is licensed under the MIT license (see [examples/LICENSE](examples/LICENSE)), so it can be freely copied into your own projects. -The [lib/](lib/) directory contains vendored third-party code (nlohmann/json, glslang's DirStackFileIncluder) that remains under its own permissive licenses; see the headers of those files. +The [lib/](lib/) directory contains vendored third-party code (nlohmann/json, glslang's DirStackFileIncluder) that remains under its own permissive licenses, see the headers of those files. ## Copyright From e280c955f7c2da68154fa2cd722a4ef68c081349 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 25/41] fix(host-cache): hash every part of the project.so stamp The stamp that decides whether a compiled project file can be reused was meant to cover the crafter-build version, the toolchain key, the module-source stamp and the compile command. Only the version went in. The four parts were joined by a format string with "\0" between the fields, and a format string is read as a C string: it ended at the first NUL, so std::format saw "{}" and dropped the other arguments without a diagnostic. A project.so is loaded into the crafter-build process and fills in Configuration objects by their layout. With the stamp blind to the declarations, one built against other module sources was loaded as current whenever the version string was unchanged, which is every working-tree build (`git describe --dirty` does not follow the tree's contents) and every local Flatpak build. Growing a class that project files construct then corrupts the heap: a project.so built while Shader was 56 bytes, loaded after it had become 104, aborted with "free(): invalid size" as soon as it declared a shader. CI builds were spared only because each carries its own version. The parts are now appended to a std::string with NUL separators, the way the other stamps in this file are built. Every existing project.so carries a stamp of the version alone, so each is recompiled once. Co-Authored-By: Claude Fable 5.1 --- implementations/Crafter.Build-Platform.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/implementations/Crafter.Build-Platform.cpp b/implementations/Crafter.Build-Platform.cpp index f8a7694..03cdba2 100644 --- a/implementations/Crafter.Build-Platform.cpp +++ b/implementations/Crafter.Build-Platform.cpp @@ -845,7 +845,18 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 26/41] test(host-cache): changed declarations recompile project.so HostCacheSourceStamp already edits a module source with a backdated mtime and expects the PCM cache to be rebuilt. Its own header names the real danger, a project.so compiled against declarations that no longer match the process it is loaded into, but it never looked at the project.so. It now records the file's mtime before the edit and expects a newer one after it, with project.cpp untouched, so only the stamp can force the rebuild. Fails without the previous commit: the stamp was the version string alone and the project.so was reused. Co-Authored-By: Claude Fable 5.1 --- tests/HostCacheSourceStamp/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/HostCacheSourceStamp/main.cpp b/tests/HostCacheSourceStamp/main.cpp index b6904e8..4110c82 100644 --- a/tests/HostCacheSourceStamp/main.cpp +++ b/tests/HostCacheSourceStamp/main.cpp @@ -119,6 +119,17 @@ int main() { Check(fs::last_write_time(probePcm) == afterCold, "unchanged sources do not rebuild the cache"); Check(ReadStamp(moduleCacheDir) == firstStamp, "unchanged sources keep the same stamp"); + // The compiled project file is the other half of the same hazard: it is + // loaded into this process and must have been compiled against the + // declarations this process was built from. + fs::path projectSo; + for (const fs::directory_entry& entry : fs::recursive_directory_iterator(projectDir)) { + if (entry.path().filename() == "project.so") projectSo = entry.path(); + } + Check(!projectSo.empty(), "the project file was compiled to a project.so"); + if (projectSo.empty()) return 1; + const fs::file_time_type soBeforeEdit = fs::last_write_time(projectSo); + // Now the case the mtime rule cannot see. The edit lands in a module the // probe does not name, because the cached PCMs import each other: a change // anywhere in the set invalidates all of them. @@ -139,6 +150,11 @@ int main() { Check(fs::last_write_time(probePcm) > afterCold, "a content change rebuilds the cache even when the source's mtime is older"); Check(ReadStamp(moduleCacheDir) != firstStamp, "the recorded stamp follows the sources"); + // project.cpp itself is untouched, so only the stamp can force this. It + // once covered the version string alone (the rest sat behind a NUL in a + // format string): a project.so built against a class of another size was + // reused and corrupted the heap when it filled one in. + Check(fs::last_write_time(projectSo) > soBeforeEdit, "changed declarations recompile project.so even though project.cpp is unchanged"); fs::remove_all(scratch, ec); From ddaab5a32a3bd3e16687d66221e1b9b4bbfd6134 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 27/41] feat(shader): per-entry defines and a named output variant Crafter.Graphics has to ship each of its compute shaders in two builds of one source: one that indexes VK_EXT_descriptor_heap and one that reads the same slots from a descriptor set, for drivers without the extension. A Shader was a path, an entry point and a stage, and its output name was derived from the path alone, so one source could only be compiled once. Shader gains `defines`, spelled like the compiler's -D ("NAME" is defined to 1, "NAME=VALUE" to VALUE) and handed to glslang as a preamble, and `variant`, which names the output: "ui.comp.glsl" with variant "sets" becomes "ui.comp.sets.spv". OutputName() is the one place that name is formed; Check, Compile and the forwarding of a dependency's shaders into the consumer's bin dir all use it. Two fields make one mistake easy: defines set, variant forgotten. Both entries then compiled in parallel into the same file, the build reported success with whichever finished last, and Check() called both up to date from then on. The same has always been true of a.vert next to a.frag. Build() now refuses a configuration in which two entries share an output name and says which. A define without a name, or one spanning lines, and a variant with a path separator are rejected by name. Left to glslang the first two surface as "-1:" with no file, and the last as a failed open after the whole compile. Not addressed: Check() still compares only the source's mtime, so a change to the defines, or to an included file, does not recompile (#35). Co-Authored-By: Claude Fable 5.1 --- implementations/Crafter.Build-Clang.cpp | 22 +++++++++++-- implementations/Crafter.Build-Shader.cpp | 41 ++++++++++++++++++++++-- interfaces/Crafter.Build-Shader.cppm | 18 +++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index d2aee22..05fa4b8 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -502,10 +502,28 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map buildCancelled{false}; + // Two entries with the same output name would compile in parallel into + // one file: whichever finished last won, the build reported success, and + // Check() then called both up to date. The usual way in is one source + // registered twice with different defines and no variant; a.vert next to + // a.frag is another. + { + std::unordered_map outputs; + for (const Shader& shader : config.shaders) { + const auto [existing, inserted] = outputs.emplace(shader.OutputName().string(), &shader); + if (inserted) continue; + buildError = std::format("shaders {} and {} both compile to {}: give one of them a distinct Shader::variant", existing->second->path.string(), shader.path.string(), existing->first); + buildCancelled.store(true); + break; + } + } for (const Shader& shader : config.shaders) { + if (buildCancelled.load(std::memory_order_relaxed)) break; if (shader.Check(outputDir)) continue; threads.emplace_back([&shader, &outputDir, &shaderIncludeDirs, &buildError, &buildCancelled]() { - Progress::Task task(std::format("Compiling shader {}", shader.path.filename().string())); + // A source registered more than once shows up once per variant; + // name the variant so the lines can be told apart. + Progress::Task task(shader.variant.empty() ? std::format("Compiling shader {}", shader.path.filename().string()) : std::format("Compiling shader {} ({})", shader.path.filename().string(), shader.variant)); if (buildCancelled.load(std::memory_order_relaxed)) return; std::string result = shader.Compile(outputDir, shaderIncludeDirs); @@ -878,7 +896,7 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_mapBinDir(); for (const Shader& shader : dep->shaders) { - fs::path src = depBinDir / shader.path.filename().replace_extension("spv"); + fs::path src = depBinDir / shader.OutputName(); if (!fs::exists(src)) continue; copyTree(src, outputDir / src.filename()); } diff --git a/implementations/Crafter.Build-Shader.cpp b/implementations/Crafter.Build-Shader.cpp index e251c50..722e635 100644 --- a/implementations/Crafter.Build-Shader.cpp +++ b/implementations/Crafter.Build-Shader.cpp @@ -36,9 +36,17 @@ namespace { namespace Crafter { Shader::Shader(fs::path&& path, std::string&& entrypoint, ShaderType type) : path(std::move(path)), entrypoint(std::move(entrypoint)), type(type) { + } + fs::path Shader::OutputName() const { + // replace_extension swaps only the last extension, so a stage suffix + // survives: "ui.comp.glsl" becomes "ui.comp.spv", or "ui.comp.sets.spv" + // for the variant "sets". + fs::path name = path.filename(); + name.replace_extension(variant.empty() ? std::string("spv") : std::format("{}.spv", variant)); + return name; } bool Shader::Check(const fs::path& outputDir) const { - fs::path spv = outputDir / path.filename().replace_extension("spv"); + fs::path spv = outputDir / OutputName(); return fs::exists(spv) && fs::last_write_time(path) < fs::last_write_time(spv); } std::string Shader::Compile(const fs::path& outputDir, std::span includeDirs) const { @@ -52,6 +60,9 @@ namespace Crafter { auto fail = [&](std::string_view stage, std::string log) { glslang::FinalizeProcess(); std::string out = path.string(); + // Several variants of one source compile in parallel; without the + // variant the message would not say which of them failed. + if (!variant.empty()) out += std::format(" ({})", variant); out += ": "; out += stage; if (!log.empty()) { @@ -75,8 +86,34 @@ namespace Crafter { const char* shaderSource = src.data(); const std::int32_t shaderSourceLen = static_cast(src.size()); + // A variant is one component of a file name. Caught here, because a + // separator would otherwise surface as "failed to open SPIR-V output" + // after the whole compile. + if (variant.find_first_of("/\\") != std::string::npos) { + return fail(std::format("shader variant '{}' must not contain a path separator", variant), {}); + } + + // One #define line per entry, split like the compiler's -D: "NAME" + // defines it to 1, "NAME=VALUE" to VALUE. glslang places the preamble + // ahead of the whole source (it finds #version by scanning first), so + // line numbers in the user's file are unaffected. It keeps the + // pointer it is handed instead of copying, so the string is declared + // here to outlive parse() below. + std::string preamble; + for (const std::string& define : defines) { + const std::size_t eq = define.find('='); + const std::string name = define.substr(0, eq); + // An error inside the preamble is reported as "-1:" with no + // file and no macro name, so reject the malformed ones by name. + if (name.empty() || define.find_first_of("\r\n") != std::string::npos) { + return fail(std::format("invalid shader define '{}': expected NAME or NAME=VALUE on one line", define), {}); + } + preamble += std::format("#define {} {}\n", name, eq == std::string::npos ? std::string("1") : define.substr(eq + 1)); + } + glslang::TShader shader(glslangType); shader.setStringsWithLengthsAndNames(&shaderSource, &shaderSourceLen, fileNameList, 1); + if (!preamble.empty()) shader.setPreamble(preamble.c_str()); shader.setEntryPoint(entrypoint.c_str()); shader.setSourceEntryPoint(entrypoint.c_str()); shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4); @@ -110,7 +147,7 @@ namespace Crafter { glslang::GlslangToSpv(*intermediate, spirv, &logger); std::string spvLog = logger.getAllMessages(); - fs::path filename = path.filename().replace_extension("spv"); + fs::path filename = OutputName(); std::ofstream file(outputDir/filename, std::ios::binary); if (!file) { return fail("failed to open SPIR-V output", (outputDir/filename).string()); diff --git a/interfaces/Crafter.Build-Shader.cppm b/interfaces/Crafter.Build-Shader.cppm index 4b4d841..b059cfb 100644 --- a/interfaces/Crafter.Build-Shader.cppm +++ b/interfaces/Crafter.Build-Shader.cppm @@ -28,7 +28,25 @@ namespace Crafter { fs::path path; std::string entrypoint; ShaderType type; + // Preprocessor macros for this compile, spelled like the compiler's -D: + // "NAME" (defined to 1) or "NAME=VALUE". They are placed ahead of the + // source, so one file can be registered more than once and take a + // different #ifdef branch each time. Note the spelling differs from + // Configuration::defines, which is a list of {name, value} pairs. + std::vector defines; + // Names the output when one source is compiled more than once: empty + // gives ".spv", "sets" gives ".sets.spv". One file-name + // component: no path separators. Every entry of a configuration must + // end up with its own OutputName(); Build() fails if two collide, so + // each extra compile of a source needs its own variant. Check() + // compares only the source's mtime: changing defines without touching + // the source leaves the old output in place. + std::string variant; Shader(fs::path&& path, std::string&& entrypoint, ShaderType type); + // File name of the compiled SPIR-V, relative to the output directory. + // The single source of truth for it: Check, Compile and the artifact + // forwarding in Build() all go through here. + fs::path OutputName() const; bool Check(const fs::path& outputDir) const; std::string Compile(const fs::path& outputDir, std::span includeDirs) const; }; From 51d87a990f24ef02f423cafb89edd4a66bacc495 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 28/41] test(shader): variants through the Shader API and through Build() ShaderCompile registers one source twice and checks that the variant gets its own output name, takes the other #ifdef branch and receives the value of a NAME=VALUE define. Staleness is pinned per output: while only the plain output exists the variant must still be due, or Build() would never compile a variant added to a built project. Malformed defines and a variant with a path separator must be refused by name. ShaderVariantBuild drives the same through Build(), which is where the entries compile in parallel into one directory: a variant added to an already built configuration is compiled into its own file, and two entries with one output name fail the build without touching the existing output. Its fixture tests the macro with `#if`, which pins that a bare define is 1 and not empty. Co-Authored-By: Claude Fable 5.1 --- project.cpp | 1 + tests/ShaderCompile/fixture/variant.frag | 15 +++ tests/ShaderCompile/main.cpp | 75 ++++++++++++- tests/ShaderVariantBuild/fixture/main.cpp | 6 + tests/ShaderVariantBuild/fixture/tint.frag | 12 ++ tests/ShaderVariantBuild/main.cpp | 121 +++++++++++++++++++++ 6 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 tests/ShaderCompile/fixture/variant.frag create mode 100644 tests/ShaderVariantBuild/fixture/main.cpp create mode 100644 tests/ShaderVariantBuild/fixture/tint.frag create mode 100644 tests/ShaderVariantBuild/main.cpp diff --git a/project.cpp b/project.cpp index a7c3272..0395bbf 100644 --- a/project.cpp +++ b/project.cpp @@ -125,6 +125,7 @@ extern "C" Configuration CrafterBuildProject(std::span a cfg.AddTest("CompilerWarning").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("CleanProject").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("ShaderCompile").Dependencies({ CrafterBuildLib.get() }); + cfg.AddTest("ShaderVariantBuild").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("StandardArgs").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("TestRunnerSpec").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("VariantId").Dependencies({ CrafterBuildLib.get() }); diff --git a/tests/ShaderCompile/fixture/variant.frag b/tests/ShaderCompile/fixture/variant.frag new file mode 100644 index 0000000..7d0178c --- /dev/null +++ b/tests/ShaderCompile/fixture/variant.frag @@ -0,0 +1,15 @@ +#version 450 + +// Compiled twice by the ShaderCompile test: once as is, once with VARIANT_RED +// and VARIANT_ALPHA=0.5 defined. The red branch does not compile unless the +// NAME=VALUE define arrives, and the two outputs only differ if the bare one +// does. +layout(location = 0) out vec4 outColor; + +void main() { +#ifdef VARIANT_RED + outColor = vec4(1.0, 0.0, 0.0, VARIANT_ALPHA); +#else + outColor = vec4(0.0, 1.0, 0.0, 1.0); +#endif +} diff --git a/tests/ShaderCompile/main.cpp b/tests/ShaderCompile/main.cpp index 20221d2..1ead9ca 100644 --- a/tests/ShaderCompile/main.cpp +++ b/tests/ShaderCompile/main.cpp @@ -8,7 +8,10 @@ using namespace Crafter; // Compile a tiny GLSL fragment shader directly via the Shader API. Verifies // the SPIR-V output lands in the target dir and starts with the SPIR-V magic -// number, without dragging in the full Build() pipeline. +// number, without dragging in the full Build() pipeline. The second half +// registers one source twice, with and without defines, and checks that the +// variant gets its own output, its own staleness and the other #ifdef branch +// (tests/ShaderVariantBuild drives the same thing through Build()). int main() { fs::path src = fs::current_path() / "tests" / "ShaderCompile" / "fixture" / "triangle.frag"; if (!fs::exists(src)) { @@ -52,5 +55,75 @@ int main() { std::println(std::cerr, "Shader::Check returned false on an up-to-date spv"); return 1; } + + // One source registered twice: the second entry carries defines and a + // variant name. It must land in its own file instead of overwriting the + // first, and it must take the other #ifdef branch. Crafter.Graphics ships + // each UI shader in a descriptor-heap and a descriptor-set flavour this + // way; if the variant were dropped from the output name, whichever compile + // finished last would silently win. + fs::path variantSrc = fs::current_path() / "tests" / "ShaderCompile" / "fixture" / "variant.frag"; + Shader plain(fs::path(variantSrc), "main", ShaderType::Fragment); + Shader red(fs::path(variantSrc), "main", ShaderType::Fragment); + red.defines = {"VARIANT_RED", "VARIANT_ALPHA=0.5"}; + red.variant = "red"; + + if (plain.OutputName() != "variant.spv" || red.OutputName() != "variant.red.spv") { + std::println(std::cerr, "unexpected output names: {} and {}", plain.OutputName().string(), red.OutputName().string()); + return 1; + } + for (const Shader* each : {&plain, &red}) { + // Staleness is per output, not per source: while only variant.spv + // exists, the variant is still due. A Check() that ignored the + // variant would call it up to date here, and Build() would then + // never compile a variant added to an already built project. + if (each == &red && red.Check(outDir)) { + std::println(std::cerr, "Shader::Check called the variant up to date before it was ever compiled"); + return 1; + } + std::string variantErr = each->Compile(outDir, includeDirs); + if (!variantErr.empty()) { + std::println(std::cerr, "variant shader compile failed: {}", variantErr); + return 1; + } + if (!each->Check(outDir)) { + std::println(std::cerr, "Shader::Check returned false for {}", each->OutputName().string()); + return 1; + } + } + + // Malformed defines and variants are refused by name. Left to glslang, an + // empty macro name surfaces as "-1:" with no file, and a variant + // with a separator as a failed open after the whole compile. + for (const char* bad : {"", "=1", "A\nB"}) { + Shader malformed(fs::path(variantSrc), "main", ShaderType::Fragment); + malformed.defines = {bad}; + malformed.variant = "malformed"; + if (!malformed.Compile(outDir, includeDirs).contains("invalid shader define")) { + std::println(std::cerr, "a malformed define was not rejected by name"); + return 1; + } + } + Shader nested(fs::path(variantSrc), "main", ShaderType::Fragment); + nested.variant = "sub/red"; + if (!nested.Compile(outDir, includeDirs).contains("path separator")) { + std::println(std::cerr, "a variant with a path separator was not rejected by name"); + return 1; + } + + auto readAll = [](const fs::path& file) { + std::ifstream stream(file, std::ios::binary); + return std::string(std::istreambuf_iterator(stream), std::istreambuf_iterator()); + }; + std::string plainSpv = readAll(outDir / "variant.spv"); + std::string redSpv = readAll(outDir / "variant.red.spv"); + if (plainSpv.empty() || redSpv.empty()) { + std::println(std::cerr, "a variant output is missing or empty"); + return 1; + } + if (plainSpv == redSpv) { + std::println(std::cerr, "both variants compiled to the same SPIR-V: the defines did not reach the preprocessor"); + return 1; + } return 0; } diff --git a/tests/ShaderVariantBuild/fixture/main.cpp b/tests/ShaderVariantBuild/fixture/main.cpp new file mode 100644 index 0000000..97eb9af --- /dev/null +++ b/tests/ShaderVariantBuild/fixture/main.cpp @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +int main() { + return 0; +} diff --git a/tests/ShaderVariantBuild/fixture/tint.frag b/tests/ShaderVariantBuild/fixture/tint.frag new file mode 100644 index 0000000..c227813 --- /dev/null +++ b/tests/ShaderVariantBuild/fixture/tint.frag @@ -0,0 +1,12 @@ +#version 450 + +// Built twice by the ShaderVariantBuild test: as is, and with TINT_RED defined. +layout(location = 0) out vec4 outColor; + +void main() { +#if TINT_RED + outColor = vec4(1.0, 0.0, 0.0, 1.0); +#else + outColor = vec4(0.0, 1.0, 0.0, 1.0); +#endif +} diff --git a/tests/ShaderVariantBuild/main.cpp b/tests/ShaderVariantBuild/main.cpp new file mode 100644 index 0000000..517b99c --- /dev/null +++ b/tests/ShaderVariantBuild/main.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +import std; +import Crafter.Build; +namespace fs = std::filesystem; +using namespace Crafter; + +// One shader source can be registered more than once, each entry with its own +// defines and a variant that names its output ("tint.spv", "tint.red.spv"). +// Build() compiles the entries in parallel into one directory, which is where +// the feature can go wrong in ways the Shader API alone does not show: +// +// - both entries must land in the bin dir, with different contents; +// - an entry added later must still be compiled when the other one's output +// is already there and up to date; +// - two entries that would write the same file (defines set, variant +// forgotten) used to race silently: the file held whichever compile +// finished last, the build reported success, and Check() called both up +// to date from then on. That has to be a build error naming the cause. +// +// The fixture's `#if TINT_RED` also pins that a bare define is defined to 1, +// like the compiler's -D: an empty definition would not compile. +namespace { + std::int32_t Failures = 0; + + void Check(bool cond, std::string_view msg) { + if (!cond) { + std::println(std::cerr, "FAIL: {}", msg); + ++Failures; + } + } + + // Shader outputs are compared by mtime against the source, and the test + // adds an entry between passes, so work on a fresh copy outside the repo. + fs::path StageFixture() { + fs::path source = fs::current_path() / "tests" / "ShaderVariantBuild" / "fixture"; + fs::path staged = fs::temp_directory_path() / "crafter-build-shader-variant-build"; + fs::remove_all(staged); + fs::copy(source, staged, fs::copy_options::recursive); + return staged; + } + + Configuration MakeApp(const fs::path& staged) { + Configuration app; + app.path = staged; + app.name = "tinted"; + app.outputName = "tinted"; + app.target = HostTarget(); + app.type = ConfigurationType::Executable; + std::array ifaces = {}; + std::array impls = { "main" }; + app.GetInterfacesAndImplementations(ifaces, impls); + return app; + } + + Shader& AddTint(Configuration& app, const fs::path& staged) { + return app.shaders.emplace_back(staged / "tint.frag", std::string("main"), ShaderType::Fragment); + } + + BuildResult BuildOnce(Configuration& app) { + // A fresh depResults per pass: the map memoizes a Configuration's + // build for the duration of one pass. + std::unordered_map> depResults; + std::mutex depMutex; + return Build(app, depResults, depMutex); + } + + std::string ReadAll(const fs::path& file) { + std::ifstream stream(file, std::ios::binary); + return std::string(std::istreambuf_iterator(stream), std::istreambuf_iterator()); + } +} + +int main() { + const fs::path staged = StageFixture(); + + // Pass 1: the plain entry alone. + Configuration app = MakeApp(staged); + AddTint(app, staged); + BuildResult first = BuildOnce(app); + Check(first.result.empty(), std::format("the plain entry builds: {}", first.result)); + const fs::path plainSpv = app.BinDir() / "tint.spv"; + const fs::path redSpv = app.BinDir() / "tint.red.spv"; + Check(fs::exists(plainSpv), "tint.spv is in the bin dir"); + Check(!fs::exists(redSpv), "no variant output before the variant is registered"); + + // Pass 2: the variant is added while tint.spv is already up to date. It + // has its own staleness check and must be compiled, into its own file. + Shader& red = AddTint(app, staged); + red.defines = {"TINT_RED"}; + red.variant = "red"; + BuildResult second = BuildOnce(app); + Check(second.result.empty(), std::format("plain + variant builds: {}", second.result)); + Check(fs::exists(redSpv), "tint.red.spv is in the bin dir"); + const std::string plainBytes = ReadAll(plainSpv); + const std::string redBytes = ReadAll(redSpv); + Check(!plainBytes.empty() && !redBytes.empty(), "both outputs have content"); + Check(plainBytes != redBytes, "the variant took the other #if branch"); + + // Pass 3: the same define without a variant. Both entries would write + // tint.spv, so the build has to stop and say so, and must leave the file + // from pass 2 alone. + Configuration clash = MakeApp(staged); + AddTint(clash, staged); + Shader& unnamed = AddTint(clash, staged); + unnamed.defines = {"TINT_RED"}; + BuildResult third = BuildOnce(clash); + Check(!third.result.empty(), "two entries with one output name fail the build"); + Check(third.result.contains("tint.spv") && third.result.contains("variant"), std::format("the error names the file and the fix: {}", third.result)); + Check(ReadAll(plainSpv) == plainBytes, "the existing output is not overwritten by the failed build"); + + std::error_code ec; + fs::remove_all(staged, ec); + + if (Failures > 0) { + std::println(std::cerr, "{} assertions failed", Failures); + return 1; + } + return 0; +} From 3c96d78a7d7a2efc2c72899d9f0115dc90b522d1 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 29/41] fix(sysroot): compile against the sysroot's libc++ headers clang's driver ranks the host toolchain's own libc++ headers (/../include/c++/v1) above --sysroot. BuildStdPcm has forced the sysroot's copy for the std module for that reason, but ordinary compiles did not. A unit that only does `import std;` never noticed. A unit that includes a standard header textually got the host's libc++, configured for the host's libc: cross-building Crafter.Graphics for aarch64 musl, Crafter.Asset's vendored gdeflate died on with "unknown rune table for this platform". Against a glibc sysroot the same unit would compile against the wrong libc++ release and say nothing. Every non-wasm, non-mingw compile with a sysroot now gets `-nostdinc++ -isystem /usr/include/c++/v1`. wasm has its own sysroot handling and mingw is a libstdc++ target. -nostdinc++ leaves -stdlib=libc++ unused on each compile, which clang reports once per unit, so the command also carries -Wno-unused-command-line-argument. The cost is that the driver's unused-argument diagnostics are silent on sysroot builds, including for a user's own misplaced flag (a -l or -Wl, option in compileFlags). Not addressed: external cmake dependencies are still configured without the target or the sysroot, and objects already built in an existing sysroot build directory are not invalidated by the new flags. Co-Authored-By: Claude Fable 5.1 --- implementations/Crafter.Build-Clang.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index 05fa4b8..d10b158 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -296,6 +296,21 @@ CompileCommand Crafter::GetCompileCommand(const Configuration& config) { if (!config.sysroot.empty()) { out.command += std::format(" --sysroot={}", config.sysroot); + // Same reason as in BuildStdPcm: the driver ranks the host + // toolchain's own libc++ headers (/../include/c++/v1) above + // --sysroot. A unit that only does `import std;` never notices, but + // one that includes a standard header textually (vendored C++ such as + // Crafter.Asset's gdeflate) would get host libc++ configured for the + // host libc, which fails on a musl sysroot with "unknown rune table + // for this platform". Force the sysroot's copy. -nostdinc++ leaves + // -stdlib=libc++ unused on every compile ("argument unused during + // compilation"), hence the silencer; the price is that it also hides + // that diagnostic for a user's own misplaced flag (a -l or -Wl, + // option in compileFlags) on sysroot builds. Not for mingw, which + // uses libstdc++ and has no libc++ in its sysroot. + if (!isWasm && config.target != "x86_64-w64-mingw32") { + out.command += std::format(" -nostdinc++ -isystem {}/usr/include/c++/v1 -Wno-unused-command-line-argument", config.sysroot); + } } if (isWasm) { // -mllvm is consumed by codegen but not the link driver, which is the From 4ca8f14c70ec727046cec963f208be6bd08b6297 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 30/41] test(sysroot): the compile command forces the sysroot's libc++ GetCompileCommand is checked as text, without a compiler: the forced include directory is present for linux-gnu and linux-musl targets with a sysroot, and absent without a sysroot, for wasm32 and for mingw. Co-Authored-By: Claude Fable 5.1 --- project.cpp | 1 + tests/SysrootCompileFlags/main.cpp | 65 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/SysrootCompileFlags/main.cpp diff --git a/project.cpp b/project.cpp index 0395bbf..43584c8 100644 --- a/project.cpp +++ b/project.cpp @@ -131,6 +131,7 @@ extern "C" Configuration CrafterBuildProject(std::span a cfg.AddTest("VariantId").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("ToolchainResolve").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("SysrootStdModule").Dependencies({ CrafterBuildLib.get() }); + cfg.AddTest("SysrootCompileFlags").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("VersionFlag").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("WasiBrowserRuntime").Dependencies({ CrafterBuildLib.get() }); cfg.AddTest("WasmVariants").Dependencies({ CrafterBuildLib.get() }); diff --git a/tests/SysrootCompileFlags/main.cpp b/tests/SysrootCompileFlags/main.cpp new file mode 100644 index 0000000..bfaf95b --- /dev/null +++ b/tests/SysrootCompileFlags/main.cpp @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® + +import std; +import Crafter.Build; +namespace fs = std::filesystem; +using namespace Crafter; + +// clang's driver ranks the host toolchain's own libc++ headers +// (/../include/c++/v1) above --sysroot. A unit that only does +// `import std;` never notices, because the std module is precompiled with the +// sysroot's headers forced. A unit that includes a standard header textually +// (vendored C++ in a dependency, say) got the host's libc++ configured for the +// host's libc: against a musl sysroot every such unit died with "unknown rune +// table for this platform", and against a glibc sysroot it compiled against +// the wrong release without a word. +// +// GetCompileCommand therefore has to point every sysroot compile at the +// sysroot's copy. It must not do so where there is no sysroot, for wasm (which +// has its own sysroot handling) or for mingw (libstdc++, no libc++ at all). +// The command is checked as text: no compiler runs. +namespace { + std::int32_t Failures = 0; + + void Check(bool cond, std::string_view msg) { + if (!cond) { + std::println(std::cerr, "FAIL: {}", msg); + ++Failures; + } + } + + std::string CommandFor(std::string_view target, std::string_view sysroot) { + Configuration cfg; + cfg.path = "/tmp/crafter-build-sysroot-compile-flags"; + cfg.name = "flags"; + cfg.outputName = "flags"; + cfg.target = std::string(target); + cfg.march = "generic"; + cfg.mtune = "generic"; + cfg.type = ConfigurationType::Executable; + cfg.sysroot = std::string(sysroot); + return GetCompileCommand(cfg).command; + } +} + +int main() { + const std::string sysroot = "/opt/crafter-build-test-sysroot"; + const std::string forced = std::format("-nostdinc++ -isystem {}/usr/include/c++/v1", sysroot); + + for (std::string_view target : {"aarch64-alpine-linux-musl", "aarch64-unknown-linux-gnu", "x86_64-pc-linux-gnu"}) { + const std::string command = CommandFor(target, sysroot); + Check(command.contains(std::format("--sysroot={}", sysroot)), std::format("{}: the sysroot is passed", target)); + Check(command.contains(forced), std::format("{}: the sysroot's libc++ headers are forced", target)); + } + + Check(!CommandFor("x86_64-pc-linux-gnu", "").contains("/usr/include/c++/v1 -Wno-unused-command-line-argument"), "no sysroot: nothing is forced"); + Check(!CommandFor("wasm32-wasip1", sysroot).contains(forced), "wasm: left to its own sysroot handling"); + Check(!CommandFor("x86_64-w64-mingw32", sysroot).contains(forced), "mingw: libstdc++ target, libc++ headers are not forced"); + + if (Failures > 0) { + std::println(std::cerr, "{} assertions failed", Failures); + return 1; + } + return 0; +} From fe38cc1f6618a3f85c82083cd41fe812b5ab518c Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH 31/41] docs: sysroot compiles use the sysroot's libc++ headers Co-Authored-By: Claude Fable 5.1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 590251b..921a814 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ TestRunner::Cmd("wine") // local exec via prefix co For elaborate setups (rsync, ssh ControlMaster, env vars on the spawn, `QEMU_LD_PREFIX` for sysroots), construct `TestRunner` directly — `copy`/`exec`/`cleanup`/`probe` are public string fields with `{bundle}`, `{remote_bundle}`, `{bin}`, `{bin_name}`, `{args}` placeholders. -For cross-arch builds, set `cfg.sysroot` on the `Configuration` to point at a sysroot for the target. `BuildStdPcm` and the compile/link commands both consume it: `--sysroot=` is passed to clang, and the std module is precompiled against the sysroot's libc++ headers. The module source has to come from the same libc++ release as those headers, so it is taken from `/usr/share/libc++/v1/std.cppm` when the sysroot ships it, else from the toolchain when both are the same major release, else from the sources crafter-build carries for other releases (`share/crafter-build/libc++-modules//`, `crafter-build --version` lists them). Alpine is the common case for the last two: its `libc++-dev` has the headers and no module source. When nothing matches, the build stops with one line naming both releases, installing that release's `share/libc++/v1` directory into the sysroot resolves it. The cheapest aarch64 sysroot is an extracted [Arch Linux ARM rootfs](http://os.archlinuxarm.org/) plus `libc++` + `libc++abi` + `gcc` installed into it via `pacman --root --config --dbpath /var/lib/pacman -Sy …` (point pacman at ALARM's mirrors via explicit config to avoid pulling host-arch packages). +For cross-arch builds, set `cfg.sysroot` on the `Configuration` to point at a sysroot for the target. `BuildStdPcm` and the compile/link commands both consume it: `--sysroot=` is passed to clang, and the std module is precompiled against the sysroot's libc++ headers. Ordinary compiles are pointed at the same headers (`-nostdinc++ -isystem /usr/include/c++/v1`), so a unit that `#include`s a standard header gets the sysroot's libc++ and not the host toolchain's. The module source has to come from the same libc++ release as those headers, so it is taken from `/usr/share/libc++/v1/std.cppm` when the sysroot ships it, else from the toolchain when both are the same major release, else from the sources crafter-build carries for other releases (`share/crafter-build/libc++-modules//`, `crafter-build --version` lists them). Alpine is the common case for the last two: its `libc++-dev` has the headers and no module source. When nothing matches, the build stops with one line naming both releases, installing that release's `share/libc++/v1` directory into the sysroot resolves it. The cheapest aarch64 sysroot is an extracted [Arch Linux ARM rootfs](http://os.archlinuxarm.org/) plus `libc++` + `libc++abi` + `gcc` installed into it via `pacman --root --config --dbpath /var/lib/pacman -Sy …` (point pacman at ALARM's mirrors via explicit config to avoid pulling host-arch packages). For `--target=x86_64-w64-mingw32` (Windows from Linux), the pipeline cross-compiles as long as `mingw-w64` is installed. Pair it with `--runner=cmd:wine` to run the result. Only the MinGW ABI is supported, there is no MSVC target and no Windows host. `-fuse-ld=lld` is auto-injected on every link, and `-lstdc++exp` is auto-linked for the mingw target so user projects don't need to declare those flags. From 4af9280e794899edad6632ce65e3c84e8dfc8a5f Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 17:05:42 +0200 Subject: [PATCH 32/41] removed json.hpp --- lib/json.hpp | 25361 ------------------------------------------------- 1 file changed, 25361 deletions(-) delete mode 100644 lib/json.hpp diff --git a/lib/json.hpp b/lib/json.hpp deleted file mode 100644 index bbf28ff..0000000 --- a/lib/json.hpp +++ /dev/null @@ -1,25361 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - -/****************************************************************************\ - * Note on documentation: The source files contain links to the online * - * documentation of the public API at https://json.nlohmann.me. This URL * - * contains the most recent documentation and should also be applicable to * - * previous versions; documentation for deprecated functions is not * - * removed, but marked deprecated. See "Generate documentation" section in * - * file docs/README.md. * - \ *****************************************************************************/ - -#ifndef INCLUDE_NLOHMANN_JSON_HPP_ -#define INCLUDE_NLOHMANN_JSON_HPP_ - -#include // all_of, find, for_each -#include // nullptr_t, ptrdiff_t, size_t -#include // hash, less -#include // initializer_list -#ifndef JSON_NO_IO -#include // istream, ostream -#endif // JSON_NO_IO -#include // random_access_iterator_tag -#include // unique_ptr -#include // string, stoi, to_string -#include // declval, forward, move, pair, swap -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// This file contains all macro definitions affecting or depending on the ABI - -#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK -#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) -#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 -#warning "Already included a different version of the library!" -#endif -#endif -#endif - -#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) - -#ifndef JSON_DIAGNOSTICS -#define JSON_DIAGNOSTICS 0 -#endif - -#ifndef JSON_DIAGNOSTIC_POSITIONS -#define JSON_DIAGNOSTIC_POSITIONS 0 -#endif - -#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON -#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 -#endif - -#if JSON_DIAGNOSTICS -#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag -#else -#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS -#endif - -#if JSON_DIAGNOSTIC_POSITIONS -#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp -#else -#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS -#endif - -#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON -#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp -#else -#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 -#endif - -// Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ -NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) - -#define NLOHMANN_JSON_ABI_TAGS \ -NLOHMANN_JSON_ABI_TAGS_CONCAT( \ -NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ -NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ -NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) - -// Construct the namespace version component -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ -_v ## major ## _ ## minor ## _ ## patch -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ -NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) - -#if NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_VERSION -#else -#define NLOHMANN_JSON_NAMESPACE_VERSION \ -NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ -NLOHMANN_JSON_VERSION_MINOR, \ -NLOHMANN_JSON_VERSION_PATCH) -#endif - -// Combine namespace components -#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b -#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ -NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) - -#ifndef NLOHMANN_JSON_NAMESPACE -#define NLOHMANN_JSON_NAMESPACE \ -nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ -NLOHMANN_JSON_ABI_TAGS, \ -NLOHMANN_JSON_NAMESPACE_VERSION) -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN -#define NLOHMANN_JSON_NAMESPACE_BEGIN \ -namespace nlohmann \ -{ \ - inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) \ - { - #endif - - #ifndef NLOHMANN_JSON_NAMESPACE_END - #define NLOHMANN_JSON_NAMESPACE_END \ - } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ -} // namespace nlohmann -#endif - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // transform -#include // array -#include // forward_list -#include // inserter, front_inserter, end -#include // map -#ifdef JSON_HAS_CPP_17 -#include // optional -#endif -#include // string -#include // tuple, make_tuple -#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include // unordered_map -#include // pair, declval -#include // valarray - - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // nullptr_t -#include // exception -#if JSON_DIAGNOSTICS -#include // accumulate -#endif -#include // runtime_error -#include // to_string -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // uint8_t -#include // string - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // declval, pair -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - template struct make_void - { - using type = void; - }; - template using void_t = typename make_void::type; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - // https://en.cppreference.com/w/cpp/experimental/is_detected - struct nonesuch - { - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; - }; - - template class Op, - class... Args> - struct detector - { - using value_t = std::false_type; - using type = Default; - }; - - template class Op, class... Args> - struct detector>, Op, Args...> - { - using value_t = std::true_type; - using type = Op; - }; - - template class Op, class... Args> - using is_detected = typename detector::value_t; - - template class Op, class... Args> - struct is_detected_lazy : is_detected { }; - - template class Op, class... Args> - using detected_t = typename detector::type; - - template class Op, class... Args> - using detected_or = detector; - - template class Op, class... Args> - using detected_or_t = typename detected_or::type; - - template class Op, class... Args> - using is_detected_exact = std::is_same>; - - template class Op, class... Args> - using is_detected_convertible = - std::is_convertible, To>; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - - -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson -// SPDX-License-Identifier: MIT - -/* Hedley - https://nemequ.github.io/hedley - * Created by Evan Nemerson - */ - -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) -#if defined(JSON_HEDLEY_VERSION) -#undef JSON_HEDLEY_VERSION -#endif -#define JSON_HEDLEY_VERSION 15 - -#if defined(JSON_HEDLEY_STRINGIFY_EX) -#undef JSON_HEDLEY_STRINGIFY_EX -#endif -#define JSON_HEDLEY_STRINGIFY_EX(x) #x - -#if defined(JSON_HEDLEY_STRINGIFY) -#undef JSON_HEDLEY_STRINGIFY -#endif -#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) - -#if defined(JSON_HEDLEY_CONCAT_EX) -#undef JSON_HEDLEY_CONCAT_EX -#endif -#define JSON_HEDLEY_CONCAT_EX(a,b) a##b - -#if defined(JSON_HEDLEY_CONCAT) -#undef JSON_HEDLEY_CONCAT -#endif -#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) - -#if defined(JSON_HEDLEY_CONCAT3_EX) -#undef JSON_HEDLEY_CONCAT3_EX -#endif -#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c - -#if defined(JSON_HEDLEY_CONCAT3) -#undef JSON_HEDLEY_CONCAT3 -#endif -#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) - -#if defined(JSON_HEDLEY_VERSION_ENCODE) -#undef JSON_HEDLEY_VERSION_ENCODE -#endif -#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) -#undef JSON_HEDLEY_VERSION_DECODE_MAJOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) -#undef JSON_HEDLEY_VERSION_DECODE_MINOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) -#undef JSON_HEDLEY_VERSION_DECODE_REVISION -#endif -#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) - -#if defined(JSON_HEDLEY_GNUC_VERSION) -#undef JSON_HEDLEY_GNUC_VERSION -#endif -#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) -#define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#elif defined(__GNUC__) -#define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) -#endif - -#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) -#undef JSON_HEDLEY_GNUC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GNUC_VERSION) -#define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION) -#undef JSON_HEDLEY_MSVC_VERSION -#endif -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) -#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) && !defined(__ICL) -#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) && !defined(__ICL) -#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) -#undef JSON_HEDLEY_MSVC_VERSION_CHECK -#endif -#if !defined(JSON_HEDLEY_MSVC_VERSION) -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) -#else -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION) -#undef JSON_HEDLEY_INTEL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) -#define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) && !defined(__ICL) -#define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) -#undef JSON_HEDLEY_INTEL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_VERSION) -#define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) -#undef JSON_HEDLEY_INTEL_CL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) -#define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) -#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) -#define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION) -#undef JSON_HEDLEY_PGI_VERSION -#endif -#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) -#define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) -#undef JSON_HEDLEY_PGI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PGI_VERSION) -#define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION) -#undef JSON_HEDLEY_SUNPRO_VERSION -#endif -#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) -#define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) -#elif defined(__SUNPRO_C) -#define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) -#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) -#define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) -#elif defined(__SUNPRO_CC) -#define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) -#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_SUNPRO_VERSION) -#define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) -#undef JSON_HEDLEY_EMSCRIPTEN_VERSION -#endif -#if defined(__EMSCRIPTEN__) -#define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) -#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) -#define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION) -#undef JSON_HEDLEY_ARM_VERSION -#endif -#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) -#define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) -#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) -#define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) -#undef JSON_HEDLEY_ARM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_ARM_VERSION) -#define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION) -#undef JSON_HEDLEY_IBM_VERSION -#endif -#if defined(__ibmxl__) -#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) -#elif defined(__xlC__) && defined(__xlC_ver__) -#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) -#elif defined(__xlC__) -#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) -#undef JSON_HEDLEY_IBM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IBM_VERSION) -#define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_VERSION) -#undef JSON_HEDLEY_TI_VERSION -#endif -#if \ -defined(__TI_COMPILER_VERSION__) && \ -( \ -defined(__TMS470__) || defined(__TI_ARM__) || \ -defined(__MSP430__) || \ -defined(__TMS320C2000__) \ -) -#if (__TI_COMPILER_VERSION__ >= 16000000) -#define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif -#endif - -#if defined(JSON_HEDLEY_TI_VERSION_CHECK) -#undef JSON_HEDLEY_TI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_VERSION) -#define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) -#undef JSON_HEDLEY_TI_CL2000_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) -#define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) -#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) -#define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION) -#undef JSON_HEDLEY_TI_CL430_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) -#define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) -#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL430_VERSION) -#define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) -#undef JSON_HEDLEY_TI_ARMCL_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) -#define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) -#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) -#define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) -#undef JSON_HEDLEY_TI_CL6X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) -#define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) -#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) -#define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) -#undef JSON_HEDLEY_TI_CL7X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) -#define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) -#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) -#define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) -#undef JSON_HEDLEY_TI_CLPRU_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) -#define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) -#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) -#define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION) -#undef JSON_HEDLEY_CRAY_VERSION -#endif -#if defined(_CRAYC) -#if defined(_RELEASE_PATCHLEVEL) -#define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) -#else -#define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) -#endif -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) -#undef JSON_HEDLEY_CRAY_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_CRAY_VERSION) -#define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION) -#undef JSON_HEDLEY_IAR_VERSION -#endif -#if defined(__IAR_SYSTEMS_ICC__) -#if __VER__ > 1000 -#define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) -#else -#define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) -#endif -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) -#undef JSON_HEDLEY_IAR_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IAR_VERSION) -#define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION) -#undef JSON_HEDLEY_TINYC_VERSION -#endif -#if defined(__TINYC__) -#define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) -#undef JSON_HEDLEY_TINYC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TINYC_VERSION) -#define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION) -#undef JSON_HEDLEY_DMC_VERSION -#endif -#if defined(__DMC__) -#define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) -#undef JSON_HEDLEY_DMC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_DMC_VERSION) -#define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION) -#undef JSON_HEDLEY_COMPCERT_VERSION -#endif -#if defined(__COMPCERT_VERSION__) -#define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) -#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_COMPCERT_VERSION) -#define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION) -#undef JSON_HEDLEY_PELLES_VERSION -#endif -#if defined(__POCC__) -#define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) -#undef JSON_HEDLEY_PELLES_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PELLES_VERSION) -#define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) -#undef JSON_HEDLEY_MCST_LCC_VERSION -#endif -#if defined(__LCC__) && defined(__LCC_MINOR__) -#define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) -#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) -#define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION) -#undef JSON_HEDLEY_GCC_VERSION -#endif -#if \ -defined(JSON_HEDLEY_GNUC_VERSION) && \ -!defined(__clang__) && \ -!defined(JSON_HEDLEY_INTEL_VERSION) && \ -!defined(JSON_HEDLEY_PGI_VERSION) && \ -!defined(JSON_HEDLEY_ARM_VERSION) && \ -!defined(JSON_HEDLEY_CRAY_VERSION) && \ -!defined(JSON_HEDLEY_TI_VERSION) && \ -!defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ -!defined(JSON_HEDLEY_TI_CL430_VERSION) && \ -!defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ -!defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ -!defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ -!defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ -!defined(__COMPCERT__) && \ -!defined(JSON_HEDLEY_MCST_LCC_VERSION) -#define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) -#undef JSON_HEDLEY_GCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GCC_VERSION) -#define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_HAS_ATTRIBUTE -#endif -#if \ -defined(__has_attribute) && \ -( \ -(!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ -) -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) -#else -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) -#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else -#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) -#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else -#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE -#endif -#if \ -defined(__has_cpp_attribute) && \ -defined(__cplusplus) && \ -(!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) -#else -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) -#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS -#endif -#if !defined(__cplusplus) || !defined(__has_cpp_attribute) -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#elif \ -!defined(JSON_HEDLEY_PGI_VERSION) && \ -!defined(JSON_HEDLEY_IAR_VERSION) && \ -(!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ -(!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) -#else -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) -#define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else -#define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) -#define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else -#define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_BUILTIN) -#undef JSON_HEDLEY_HAS_BUILTIN -#endif -#if defined(__has_builtin) -#define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) -#else -#define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) -#undef JSON_HEDLEY_GNUC_HAS_BUILTIN -#endif -#if defined(__has_builtin) -#define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else -#define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) -#undef JSON_HEDLEY_GCC_HAS_BUILTIN -#endif -#if defined(__has_builtin) -#define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else -#define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_FEATURE) -#undef JSON_HEDLEY_HAS_FEATURE -#endif -#if defined(__has_feature) -#define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) -#else -#define JSON_HEDLEY_HAS_FEATURE(feature) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) -#undef JSON_HEDLEY_GNUC_HAS_FEATURE -#endif -#if defined(__has_feature) -#define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else -#define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) -#undef JSON_HEDLEY_GCC_HAS_FEATURE -#endif -#if defined(__has_feature) -#define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else -#define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_EXTENSION) -#undef JSON_HEDLEY_HAS_EXTENSION -#endif -#if defined(__has_extension) -#define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) -#else -#define JSON_HEDLEY_HAS_EXTENSION(extension) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) -#undef JSON_HEDLEY_GNUC_HAS_EXTENSION -#endif -#if defined(__has_extension) -#define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else -#define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) -#undef JSON_HEDLEY_GCC_HAS_EXTENSION -#endif -#if defined(__has_extension) -#define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else -#define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) -#define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) -#else -#define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) -#define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else -#define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) -#define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else -#define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_WARNING) -#undef JSON_HEDLEY_HAS_WARNING -#endif -#if defined(__has_warning) -#define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) -#else -#define JSON_HEDLEY_HAS_WARNING(warning) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) -#undef JSON_HEDLEY_GNUC_HAS_WARNING -#endif -#if defined(__has_warning) -#define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else -#define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_WARNING) -#undef JSON_HEDLEY_GCC_HAS_WARNING -#endif -#if defined(__has_warning) -#define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else -#define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if \ -(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ -defined(__clang__) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ -JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ -(JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) -#define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) -#define JSON_HEDLEY_PRAGMA(value) __pragma(value) -#else -#define JSON_HEDLEY_PRAGMA(value) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) -#undef JSON_HEDLEY_DIAGNOSTIC_PUSH -#endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) -#undef JSON_HEDLEY_DIAGNOSTIC_POP -#endif -#if defined(__clang__) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) -#define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) -#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#else -#define JSON_HEDLEY_DIAGNOSTIC_PUSH -#define JSON_HEDLEY_DIAGNOSTIC_POP -#endif - -/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for - HEDLEY INTERNAL USE ONLY. API sub*ject to change without notice. */ -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") -# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") -# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ -_Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ -_Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ -xpr \ -JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ -_Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ -xpr \ -JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ -xpr \ -JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# endif -#endif -#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x -#endif - -#if defined(JSON_HEDLEY_CONST_CAST) -#undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif \ -JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ -((T) (expr)); \ -JSON_HEDLEY_DIAGNOSTIC_POP \ -})) -#else -# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) -#undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else -#define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) -#undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else -#define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) -#undef JSON_HEDLEY_CPP_CAST -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") -# define JSON_HEDLEY_CPP_CAST(T, expr) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ -((T) (expr)) \ -JSON_HEDLEY_DIAGNOSTIC_POP -# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) -# define JSON_HEDLEY_CPP_CAST(T, expr) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("diag_suppress=Pe137") \ -JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) -# endif -#else -# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) -#elif \ -JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") -#elif \ -JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif - -#if defined(JSON_HEDLEY_DEPRECATED) -#undef JSON_HEDLEY_DEPRECATED -#endif -#if defined(JSON_HEDLEY_DEPRECATED_FOR) -#undef JSON_HEDLEY_DEPRECATED_FOR -#endif -#if \ -JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) -#elif \ -(JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) -#elif defined(__cplusplus) && (__cplusplus >= 201402L) -#define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) -#elif \ -JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ -JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ -JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") -#else -#define JSON_HEDLEY_DEPRECATED(since) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) -#endif - -#if defined(JSON_HEDLEY_UNAVAILABLE) -#undef JSON_HEDLEY_UNAVAILABLE -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) -#else -#define JSON_HEDLEY_UNAVAILABLE(available_since) -#endif - -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) -#undef JSON_HEDLEY_WARN_UNUSED_RESULT -#endif -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) -#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) -#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) -#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) -#define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) -#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) -#define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) -#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) -#elif defined(_Check_return_) /* SAL */ -#define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ -#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ -#else -#define JSON_HEDLEY_WARN_UNUSED_RESULT -#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) -#endif - -#if defined(JSON_HEDLEY_SENTINEL) -#undef JSON_HEDLEY_SENTINEL -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) -#else -#define JSON_HEDLEY_SENTINEL(position) -#endif - -#if defined(JSON_HEDLEY_NO_RETURN) -#undef JSON_HEDLEY_NO_RETURN -#endif -#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_NO_RETURN __noreturn -#elif \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -#define JSON_HEDLEY_NO_RETURN _Noreturn -#elif defined(__cplusplus) && (__cplusplus >= 201103L) -#define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) -#elif \ -JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -#define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) -#define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) -#define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) -#define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#else -#define JSON_HEDLEY_NO_RETURN -#endif - -#if defined(JSON_HEDLEY_NO_ESCAPE) -#undef JSON_HEDLEY_NO_ESCAPE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) -#define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) -#else -#define JSON_HEDLEY_NO_ESCAPE -#endif - -#if defined(JSON_HEDLEY_UNREACHABLE) -#undef JSON_HEDLEY_UNREACHABLE -#endif -#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) -#undef JSON_HEDLEY_UNREACHABLE_RETURN -#endif -#if defined(JSON_HEDLEY_ASSUME) -#undef JSON_HEDLEY_ASSUME -#endif -#if \ -JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_ASSUME(expr) __assume(expr) -#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) -#define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) -#if defined(__cplusplus) -#define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) -#else -#define JSON_HEDLEY_ASSUME(expr) _nassert(expr) -#endif -#endif -#if \ -(JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ -JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif defined(JSON_HEDLEY_ASSUME) -#define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif -#if !defined(JSON_HEDLEY_ASSUME) -#if defined(JSON_HEDLEY_UNREACHABLE) -#define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) -#else -#define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) -#endif -#endif -#if defined(JSON_HEDLEY_UNREACHABLE) -#if \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) -#define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) -#else -#define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() -#endif -#else -#define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE) -#define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif - -JSON_HEDLEY_DIAGNOSTIC_PUSH -#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") -#pragma clang diagnostic ignored "-Wpedantic" -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif -#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) -#if defined(__clang__) -#pragma clang diagnostic ignored "-Wvariadic-macros" -#elif defined(JSON_HEDLEY_GCC_VERSION) -#pragma GCC diagnostic ignored "-Wvariadic-macros" -#endif -#endif -#if defined(JSON_HEDLEY_NON_NULL) -#undef JSON_HEDLEY_NON_NULL -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) -#define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) -#else -#define JSON_HEDLEY_NON_NULL(...) -#endif -JSON_HEDLEY_DIAGNOSTIC_POP - -#if defined(JSON_HEDLEY_PRINTF_FORMAT) -#undef JSON_HEDLEY_PRINTF_FORMAT -#endif -#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) -#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) -#elif \ -JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) -#else -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) -#endif - -#if defined(JSON_HEDLEY_CONSTEXPR) -#undef JSON_HEDLEY_CONSTEXPR -#endif -#if defined(__cplusplus) -#if __cplusplus >= 201103L -#define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) -#endif -#endif -#if !defined(JSON_HEDLEY_CONSTEXPR) -#define JSON_HEDLEY_CONSTEXPR -#endif - -#if defined(JSON_HEDLEY_PREDICT) -#undef JSON_HEDLEY_PREDICT -#endif -#if defined(JSON_HEDLEY_LIKELY) -#undef JSON_HEDLEY_LIKELY -#endif -#if defined(JSON_HEDLEY_UNLIKELY) -#undef JSON_HEDLEY_UNLIKELY -#endif -#if defined(JSON_HEDLEY_UNPREDICTABLE) -#undef JSON_HEDLEY_UNPREDICTABLE -#endif -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) -#define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) -#endif -#if \ -(JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) -#elif \ -(JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ -JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ -(((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ -(__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ -})) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ -(__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ -})) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#else -# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) -# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) -#endif -#if !defined(JSON_HEDLEY_UNPREDICTABLE) -#define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) -#endif - -#if defined(JSON_HEDLEY_MALLOC) -#undef JSON_HEDLEY_MALLOC -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -#define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_MALLOC __declspec(restrict) -#else -#define JSON_HEDLEY_MALLOC -#endif - -#if defined(JSON_HEDLEY_PURE) -#undef JSON_HEDLEY_PURE -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PURE __attribute__((__pure__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") -#elif defined(__cplusplus) && \ -( \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ -) -# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") -#else -# define JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_CONST) -#undef JSON_HEDLEY_CONST -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_CONST __attribute__((__const__)) -#elif \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -#define JSON_HEDLEY_CONST _Pragma("no_side_effect") -#else -#define JSON_HEDLEY_CONST JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_RESTRICT) -#undef JSON_HEDLEY_RESTRICT -#endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) -#define JSON_HEDLEY_RESTRICT restrict -#elif \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ -JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ -JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ -defined(__clang__) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_RESTRICT __restrict -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) -#define JSON_HEDLEY_RESTRICT _Restrict -#else -#define JSON_HEDLEY_RESTRICT -#endif - -#if defined(JSON_HEDLEY_INLINE) -#undef JSON_HEDLEY_INLINE -#endif -#if \ -(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ -(defined(__cplusplus) && (__cplusplus >= 199711L)) -#define JSON_HEDLEY_INLINE inline -#elif \ -defined(JSON_HEDLEY_GCC_VERSION) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) -#define JSON_HEDLEY_INLINE __inline__ -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_INLINE __inline -#else -#define JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_ALWAYS_INLINE) -#undef JSON_HEDLEY_ALWAYS_INLINE -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ -JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif defined(__cplusplus) && \ -( \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ -) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") -#else -# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_NEVER_INLINE) -#undef JSON_HEDLEY_NEVER_INLINE -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ -JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ -(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ -(JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ -(JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ -JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ -JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ -JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -#define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) -#define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) -#define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -#define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) -#define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) -#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#else -#define JSON_HEDLEY_NEVER_INLINE -#endif - -#if defined(JSON_HEDLEY_PRIVATE) -#undef JSON_HEDLEY_PRIVATE -#endif -#if defined(JSON_HEDLEY_PUBLIC) -#undef JSON_HEDLEY_PUBLIC -#endif -#if defined(JSON_HEDLEY_IMPORT) -#undef JSON_HEDLEY_IMPORT -#endif -#if defined(_WIN32) || defined(__CYGWIN__) -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC __declspec(dllexport) -# define JSON_HEDLEY_IMPORT __declspec(dllimport) -#else -# if \ -JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ -JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ -( \ -defined(__TI_EABI__) && \ -( \ -(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ -) \ -) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) -# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) -# else -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC -# endif -# define JSON_HEDLEY_IMPORT extern -#endif - -#if defined(JSON_HEDLEY_NO_THROW) -#undef JSON_HEDLEY_NO_THROW -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) -#define JSON_HEDLEY_NO_THROW __declspec(nothrow) -#else -#define JSON_HEDLEY_NO_THROW -#endif - -#if defined(JSON_HEDLEY_FALL_THROUGH) -#undef JSON_HEDLEY_FALL_THROUGH -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) -#define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) -#define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) -#elif defined(__fallthrough) /* SAL */ -#define JSON_HEDLEY_FALL_THROUGH __fallthrough -#else -#define JSON_HEDLEY_FALL_THROUGH -#endif - -#if defined(JSON_HEDLEY_RETURNS_NON_NULL) -#undef JSON_HEDLEY_RETURNS_NON_NULL -#endif -#if \ -JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) -#elif defined(_Ret_notnull_) /* SAL */ -#define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ -#else -#define JSON_HEDLEY_RETURNS_NON_NULL -#endif - -#if defined(JSON_HEDLEY_ARRAY_PARAM) -#undef JSON_HEDLEY_ARRAY_PARAM -#endif -#if \ -defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ -!defined(__STDC_NO_VLA__) && \ -!defined(__cplusplus) && \ -!defined(JSON_HEDLEY_PGI_VERSION) && \ -!defined(JSON_HEDLEY_TINYC_VERSION) -#define JSON_HEDLEY_ARRAY_PARAM(name) (name) -#else -#define JSON_HEDLEY_ARRAY_PARAM(name) -#endif - -#if defined(JSON_HEDLEY_IS_CONSTANT) -#undef JSON_HEDLEY_IS_CONSTANT -#endif -#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) -#undef JSON_HEDLEY_REQUIRE_CONSTEXPR -#endif -/* JSON_HEDLEY_IS_CONSTEXPR_ is for - HEDLEY INTERNAL USE ONLY. API sub*ject to change without notice. */ -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) -#undef JSON_HEDLEY_IS_CONSTEXPR_ -#endif -#if \ -JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ -JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ -(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ -JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ -JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -#define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) -#endif -#if !defined(__cplusplus) -# if \ -JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ -JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ -JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) -#if defined(__INTPTR_TYPE__) -#define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) -#else -#include -#define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) -#endif -# elif \ -( \ -defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ -!defined(JSON_HEDLEY_SUNPRO_VERSION) && \ -!defined(JSON_HEDLEY_PGI_VERSION) && \ -!defined(JSON_HEDLEY_IAR_VERSION)) || \ -(JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ -JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ -JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) -#if defined(__INTPTR_TYPE__) -#define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) -#else -#include -#define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) -#endif -# elif \ -defined(JSON_HEDLEY_GCC_VERSION) || \ -defined(JSON_HEDLEY_INTEL_VERSION) || \ -defined(JSON_HEDLEY_TINYC_VERSION) || \ -defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ -JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ -defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ -defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ -defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ -defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ -defined(__clang__) -# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ -sizeof(void) != \ -sizeof(*( \ -1 ? \ -((void*) ((expr) * 0L) ) : \ -((struct { char v[sizeof(void) * 2]; } *) 1) \ -) \ -) \ -) -# endif -#endif -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) -#if !defined(JSON_HEDLEY_IS_CONSTANT) -#define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) -#endif -#define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) -#else -#if !defined(JSON_HEDLEY_IS_CONSTANT) -#define JSON_HEDLEY_IS_CONSTANT(expr) (0) -#endif -#define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) -#endif - -#if defined(JSON_HEDLEY_BEGIN_C_DECLS) -#undef JSON_HEDLEY_BEGIN_C_DECLS -#endif -#if defined(JSON_HEDLEY_END_C_DECLS) -#undef JSON_HEDLEY_END_C_DECLS -#endif -#if defined(JSON_HEDLEY_C_DECL) -#undef JSON_HEDLEY_C_DECL -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { -#define JSON_HEDLEY_END_C_DECLS } -#define JSON_HEDLEY_C_DECL extern "C" -#else -#define JSON_HEDLEY_BEGIN_C_DECLS -#define JSON_HEDLEY_END_C_DECLS -#define JSON_HEDLEY_C_DECL -#endif - -#if defined(JSON_HEDLEY_STATIC_ASSERT) -#undef JSON_HEDLEY_STATIC_ASSERT -#endif -#if \ -!defined(__cplusplus) && ( \ -(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ -(JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ -JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ -defined(_Static_assert) \ -) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) -#elif \ -(defined(__cplusplus) && (__cplusplus >= 201103L)) || \ -JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) -#else -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) -#endif - -#if defined(JSON_HEDLEY_NULL) -#undef JSON_HEDLEY_NULL -#endif -#if defined(__cplusplus) -#if __cplusplus >= 201103L -#define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) -#elif defined(NULL) -#define JSON_HEDLEY_NULL NULL -#else -#define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) -#endif -#elif defined(NULL) -#define JSON_HEDLEY_NULL NULL -#else -#define JSON_HEDLEY_NULL ((void*) 0) -#endif - -#if defined(JSON_HEDLEY_MESSAGE) -#undef JSON_HEDLEY_MESSAGE -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_MESSAGE(msg) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ -JSON_HEDLEY_PRAGMA(message msg) \ -JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) -#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_WARNING) -#undef JSON_HEDLEY_WARNING -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_WARNING(msg) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ -JSON_HEDLEY_PRAGMA(clang warning msg) \ -JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ -JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ -JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ -JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) -#elif \ -JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE) -#undef JSON_HEDLEY_REQUIRE -#endif -#if defined(JSON_HEDLEY_REQUIRE_MSG) -#undef JSON_HEDLEY_REQUIRE_MSG -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) -# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") -# define JSON_HEDLEY_REQUIRE(expr) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ -__attribute__((diagnose_if(!(expr), #expr, "error"))) \ -JSON_HEDLEY_DIAGNOSTIC_POP -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ -__attribute__((diagnose_if(!(expr), msg, "error"))) \ -JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) -# endif -#else -# define JSON_HEDLEY_REQUIRE(expr) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) -#endif - -#if defined(JSON_HEDLEY_FLAGS) -#undef JSON_HEDLEY_FLAGS -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) -#define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) -#else -#define JSON_HEDLEY_FLAGS -#endif - -#if defined(JSON_HEDLEY_FLAGS_CAST) -#undef JSON_HEDLEY_FLAGS_CAST -#endif -#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) -# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ -JSON_HEDLEY_DIAGNOSTIC_PUSH \ -_Pragma("warning(disable:188)") \ -((T) (expr)); \ -JSON_HEDLEY_DIAGNOSTIC_POP \ -})) -#else -# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) -#endif - -#if defined(JSON_HEDLEY_EMPTY_BASES) -#undef JSON_HEDLEY_EMPTY_BASES -#endif -#if \ -(JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ -JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -#define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) -#else -#define JSON_HEDLEY_EMPTY_BASES -#endif - -/* Remaining macros are deprecated. */ - -#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) -#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK -#endif -#if defined(__clang__) -#define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) -#else -#define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) -#undef JSON_HEDLEY_CLANG_HAS_BUILTIN -#endif -#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) - -#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) -#undef JSON_HEDLEY_CLANG_HAS_FEATURE -#endif -#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) - -#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) -#undef JSON_HEDLEY_CLANG_HAS_EXTENSION -#endif -#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) - -#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) -#undef JSON_HEDLEY_CLANG_HAS_WARNING -#endif -#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) - -#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ - - -// This file contains all internal macro definitions (except those affecting ABI) -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -// #include - - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) -#if defined(__clang__) -#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 -#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" -#endif -#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 -#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" -#endif -#endif -#endif - -// C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) -#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) -#define JSON_HAS_CPP_20 -#define JSON_HAS_CPP_17 -#define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 -#define JSON_HAS_CPP_17 -#define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) -#define JSON_HAS_CPP_14 -#endif -// the cpp 11 flag is always specified because it is the minimal required version -#define JSON_HAS_CPP_11 -#endif - -#ifdef __has_include -#if __has_include() -#include -#endif -#endif - -#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) -#ifdef JSON_HAS_CPP_17 -#if defined(__cpp_lib_filesystem) -#define JSON_HAS_FILESYSTEM 1 -#elif defined(__cpp_lib_experimental_filesystem) -#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 -#elif !defined(__has_include) -#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 -#elif __has_include() -#define JSON_HAS_FILESYSTEM 1 -#elif __has_include() -#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 -#endif - -// std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ -#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#endif - -// no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support -#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#endif - -// no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support -#if defined(__clang_major__) && __clang_major__ < 7 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#endif - -// no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support -#if defined(_MSC_VER) && _MSC_VER < 1914 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#endif - -// no filesystem support before iOS 13 -#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#endif - -// no filesystem support before macOS Catalina -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#endif -#endif -#endif - -#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM -#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_FILESYSTEM -#define JSON_HAS_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_THREE_WAY_COMPARISON -#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ -&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L -#define JSON_HAS_THREE_WAY_COMPARISON 1 -#else -#define JSON_HAS_THREE_WAY_COMPARISON 0 -#endif -#endif - -#ifndef JSON_HAS_RANGES -// ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error -#if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 -#define JSON_HAS_RANGES 0 -#elif defined(__cpp_lib_ranges) -#define JSON_HAS_RANGES 1 -#else -#define JSON_HAS_RANGES 0 -#endif -#endif - -#ifndef JSON_HAS_STATIC_RTTI -#if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 -#define JSON_HAS_STATIC_RTTI 1 -#else -#define JSON_HAS_STATIC_RTTI 0 -#endif -#endif - -#ifdef JSON_HAS_CPP_17 -#define JSON_INLINE_VARIABLE inline -#else -#define JSON_INLINE_VARIABLE -#endif - -#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) -#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else -#define JSON_NO_UNIQUE_ADDRESS -#endif - -// disable documentation warnings on clang -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" -#pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -// allow disabling exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) -#define JSON_THROW(exception) throw exception -#define JSON_TRY try -#define JSON_CATCH(exception) catch(exception) -#define JSON_INTERNAL_CATCH(exception) catch(exception) -#else -#include -#define JSON_THROW(exception) std::abort() -#define JSON_TRY if(true) -#define JSON_CATCH(exception) if(false) -#define JSON_INTERNAL_CATCH(exception) if(false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) -#undef JSON_THROW -#define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) -#undef JSON_TRY -#define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) -#undef JSON_CATCH -#define JSON_CATCH JSON_CATCH_USER -#undef JSON_INTERNAL_CATCH -#define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) -#undef JSON_INTERNAL_CATCH -#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -// allow overriding assert -#if !defined(JSON_ASSERT) -#include // assert -#define JSON_ASSERT(x) assert(x) -#endif - -// allow to access some private functions (needed by the test suite) -#if defined(JSON_TESTS_PRIVATE) -#define JSON_PRIVATE_UNLESS_TESTED public -#else -#define JSON_PRIVATE_UNLESS_TESTED private -#endif - -/*! - @ brief macro to briefly define a mapp*ing between an enum and JSON - @def NLOHMANN_JSON_SERIALIZE_ENUM - @since version 3.4.0 - */ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ -template \ -inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ -{ \ - /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ -} \ -template \ -inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ -{ \ - /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ -} - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ -template class ObjectType, \ -template class ArrayType, \ -class StringType, class BooleanType, class NumberIntegerType, \ -class NumberUnsignedType, class NumberFloatType, \ -template class AllocatorType, \ -template class JSONSerializer, \ -class BinaryType, \ -class CustomBaseClass> - -#define NLOHMANN_BASIC_JSON_TPL \ -basic_json - -// Macros to simplify conversion from/to types - -#define NLOHMANN_JSON_EXPAND( x ) x -#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME -#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ -NLOHMANN_JSON_PASTE64, \ -NLOHMANN_JSON_PASTE63, \ -NLOHMANN_JSON_PASTE62, \ -NLOHMANN_JSON_PASTE61, \ -NLOHMANN_JSON_PASTE60, \ -NLOHMANN_JSON_PASTE59, \ -NLOHMANN_JSON_PASTE58, \ -NLOHMANN_JSON_PASTE57, \ -NLOHMANN_JSON_PASTE56, \ -NLOHMANN_JSON_PASTE55, \ -NLOHMANN_JSON_PASTE54, \ -NLOHMANN_JSON_PASTE53, \ -NLOHMANN_JSON_PASTE52, \ -NLOHMANN_JSON_PASTE51, \ -NLOHMANN_JSON_PASTE50, \ -NLOHMANN_JSON_PASTE49, \ -NLOHMANN_JSON_PASTE48, \ -NLOHMANN_JSON_PASTE47, \ -NLOHMANN_JSON_PASTE46, \ -NLOHMANN_JSON_PASTE45, \ -NLOHMANN_JSON_PASTE44, \ -NLOHMANN_JSON_PASTE43, \ -NLOHMANN_JSON_PASTE42, \ -NLOHMANN_JSON_PASTE41, \ -NLOHMANN_JSON_PASTE40, \ -NLOHMANN_JSON_PASTE39, \ -NLOHMANN_JSON_PASTE38, \ -NLOHMANN_JSON_PASTE37, \ -NLOHMANN_JSON_PASTE36, \ -NLOHMANN_JSON_PASTE35, \ -NLOHMANN_JSON_PASTE34, \ -NLOHMANN_JSON_PASTE33, \ -NLOHMANN_JSON_PASTE32, \ -NLOHMANN_JSON_PASTE31, \ -NLOHMANN_JSON_PASTE30, \ -NLOHMANN_JSON_PASTE29, \ -NLOHMANN_JSON_PASTE28, \ -NLOHMANN_JSON_PASTE27, \ -NLOHMANN_JSON_PASTE26, \ -NLOHMANN_JSON_PASTE25, \ -NLOHMANN_JSON_PASTE24, \ -NLOHMANN_JSON_PASTE23, \ -NLOHMANN_JSON_PASTE22, \ -NLOHMANN_JSON_PASTE21, \ -NLOHMANN_JSON_PASTE20, \ -NLOHMANN_JSON_PASTE19, \ -NLOHMANN_JSON_PASTE18, \ -NLOHMANN_JSON_PASTE17, \ -NLOHMANN_JSON_PASTE16, \ -NLOHMANN_JSON_PASTE15, \ -NLOHMANN_JSON_PASTE14, \ -NLOHMANN_JSON_PASTE13, \ -NLOHMANN_JSON_PASTE12, \ -NLOHMANN_JSON_PASTE11, \ -NLOHMANN_JSON_PASTE10, \ -NLOHMANN_JSON_PASTE9, \ -NLOHMANN_JSON_PASTE8, \ -NLOHMANN_JSON_PASTE7, \ -NLOHMANN_JSON_PASTE6, \ -NLOHMANN_JSON_PASTE5, \ -NLOHMANN_JSON_PASTE4, \ -NLOHMANN_JSON_PASTE3, \ -NLOHMANN_JSON_PASTE2, \ -NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) -#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) -#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) -#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) -#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) -#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) -#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) -#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) -#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) -#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) -#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) -#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) -#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) -#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) -#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) -#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) -#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) -#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) -#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) -#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) -#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) -#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) -#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) -#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) -#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) -#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) -#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) -#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) -#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) -#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) -#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) -#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) -#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) -#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) -#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) -#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) -#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) -#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) -#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) -#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) -#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) -#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) -#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) -#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) -#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) -#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) -#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) -#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) -#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) -#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) -#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) -#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) -#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) -#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) -#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) -#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) -#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) -#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) -#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) -#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) -#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) -#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) -#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) - -#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; -#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); - -/*! - @ brief macro * - @def NLOHMANN_DEFINE_TYPE_INTRUSIVE - @since version 3.9.0 - */ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ -friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ -friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ -friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -/*! - @ brief macro * - @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE - @since version 3.9.0 - */ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ -inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ -inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ -inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! - @ brief macro * - @def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE - @since version 3.11.x - */ -#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \ -friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ -friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ -friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - -/*! - @ brief macro * - @def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE - @since version 3.11.x - */ -#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \ -inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ -inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ -inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ -inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - -// inspired from https://stackoverflow.com/a/26745591 -// allows to call any std function as if (e.g. with begin): -// using std::begin; begin(x); -// -// it allows using the detected idiom to retrieve the return type -// of such an expression -#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ -namespace detail { \ - using std::std_name; \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ -} \ -\ -namespace detail2 { \ - struct std_name##_tag \ - { \ - }; \ - \ - template \ - std_name##_tag std_name(T&&...); \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - \ - template \ - struct would_call_std_##std_name \ - { \ - static constexpr auto const value = ::nlohmann::detail:: \ - is_detected_exact::value; \ - }; \ -} /* namespace detail2 */ \ -\ -template \ -struct would_call_std_##std_name : detail2::would_call_std_##std_name \ -{ \ -} - -#ifndef JSON_USE_IMPLICIT_CONVERSIONS -#define JSON_USE_IMPLICIT_CONVERSIONS 1 -#endif - -#if JSON_USE_IMPLICIT_CONVERSIONS -#define JSON_EXPLICIT -#else -#define JSON_EXPLICIT explicit -#endif - -#ifndef JSON_DISABLE_ENUM_SERIALIZATION -#define JSON_DISABLE_ENUM_SERIALIZATION 0 -#endif - -#ifndef JSON_USE_GLOBAL_UDLS -#define JSON_USE_GLOBAL_UDLS 1 -#endif - -#if JSON_HAS_THREE_WAY_COMPARISON -#include // partial_ordering -#endif - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - /////////////////////////// - // JSON type enumeration // - /////////////////////////// - - /*! - @ brief the JSON type enumeration * - - This enumeration collects the different JSON types. It is internally used to - distinguish the stored values, and the functions @ref basic_json::is_null(), - @ref basic_json::is_object(), @ref basic_json::is_array(), - @ref basic_json::is_string(), @ref basic_json::is_boolean(), - @ref basic_json::is_number() (with @ref basic_json::is_number_integer(), - @ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), - @ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and - @ref basic_json::is_structured() rely on it. - - @note There are three enumeration entries (number_integer, number_unsigned, and - number_float), because the library distinguishes these three types for numbers: - @ref basic_json::number_unsigned_t is used for unsigned integers, - @ref basic_json::number_integer_t is used for signed integers, and - @ref basic_json::number_float_t is used for floating-point numbers or to - approximate integers which do not fit in the limits of their respective type. - - @sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON - value with the default value for a given type - - @since version 1.0.0 - */ - enum class value_t : std::uint8_t - { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function - }; - - /*! - @ brief comparison operator for JSON t*ypes - - Returns an ordering that is similar to Python: - - order: null < boolean < number < object < array < string < binary - - furthermore, each type is not smaller than itself - - discarded values are not comparable - - binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - - @since version 1.0.0 - */ - #if JSON_HAS_THREE_WAY_COMPARISON - inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* - #else - inline bool operator<(const value_t lhs, const value_t rhs) noexcept - #endif - { - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); - #if JSON_HAS_THREE_WAY_COMPARISON - if (l_index < order.size() && r_index < order.size()) - { - return order[l_index] <=> order[r_index]; // *NOPAD* - } - return std::partial_ordering::unordered; - #else - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; - #endif - } - - // GCC selects the built-in operator< over an operator rewritten from - // a user-defined spaceship operator - // Clang, MSVC, and ICC select the rewritten candidate - // (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) - #if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) - inline bool operator<(const value_t lhs, const value_t rhs) noexcept - { - return std::is_lt(lhs <=> rhs); // *NOPAD* - } - #endif - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - /*! - @ brief replace all occurrences of a s*ubstring by another string - - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f - - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** - - @since version 2.0.0 - */ - template - inline void replace_substring(StringType& s, const StringType& f, - const StringType& t) - { - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != StringType::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} - } - - /*! - * @brief string escaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to escape - * @return escaped string - * - * Note the order of escaping "~" to "~0" and "/" to "~1" is important. - */ - template - inline StringType escape(StringType s) - { - replace_substring(s, StringType{"~"}, StringType{"~0"}); - replace_substring(s, StringType{"/"}, StringType{"~1"}); - return s; - } - - /*! - * @brief string unescaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to unescape - * @return unescaped string - * - * Note the order of escaping "~1" to "/" and "~0" to "~" is important. - */ - template - static void unescape(StringType& s) - { - replace_substring(s, StringType{"~1"}, StringType{"/"}); - replace_substring(s, StringType{"~0"}, StringType{"~"}); - } - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // size_t - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - /// struct to capture the start position of the current token - struct position_t - { - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; - - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } - }; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-FileCopyrightText: 2018 The Abseil Authors -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - template - using uncvref_t = typename std::remove_cv::type>::type; - - #ifdef JSON_HAS_CPP_14 - - // the following utilities are natively available in C++14 - using std::enable_if_t; - using std::index_sequence; - using std::make_index_sequence; - using std::index_sequence_for; - - #else - - // alias templates to reduce boilerplate - template - using enable_if_t = typename std::enable_if::type; - - // The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h - // which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. - - //// START OF CODE FROM GOOGLE ABSEIL - - // integer_sequence - // - // Class template representing a compile-time integer sequence. An instantiation - // of `integer_sequence` has a sequence of integers encoded in its - // type through its template arguments (which is a common need when - // working with C++11 variadic templates). `absl::integer_sequence` is designed - // to be a drop-in replacement for C++14's `std::integer_sequence`. - // - // Example: - // - // template< class T, T... Ints > - // void user_function(integer_sequence); - // - // int main() - // { - // // user_function's `T` will be deduced to `int` and `Ints...` - // // will be deduced to `0, 1, 2, 3, 4`. - // user_function(make_integer_sequence()); - // } - template - struct integer_sequence - { - using value_type = T; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } - }; - - // index_sequence - // - // A helper template for an `integer_sequence` of `size_t`, - // `absl::index_sequence` is designed to be a drop-in replacement for C++14's - // `std::index_sequence`. - template - using index_sequence = integer_sequence; - - namespace utility_internal - { - - template - struct Extend; - - // Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. - template - struct Extend, SeqSize, 0> - { - using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; - }; - - template - struct Extend, SeqSize, 1> - { - using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; - }; - - // Recursion helper for 'make_integer_sequence'. - // 'Gen::type' is an alias for 'integer_sequence'. - template - struct Gen - { - using type = - typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; - }; - - template - struct Gen - { - using type = integer_sequence; - }; - - } // namespace utility_internal - - // Compile-time sequences of integers - - // make_integer_sequence - // - // This template alias is equivalent to - // `integer_sequence`, and is designed to be a drop-in - // replacement for C++14's `std::make_integer_sequence`. - template - using make_integer_sequence = typename utility_internal::Gen::type; - - // make_index_sequence - // - // This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, - // and is designed to be a drop-in replacement for C++14's - // `std::make_index_sequence`. - template - using make_index_sequence = make_integer_sequence; - - // index_sequence_for - // - // Converts a typename pack into an index sequence of the same length, and - // is designed to be a drop-in replacement for C++14's - // `std::index_sequence_for()` - template - using index_sequence_for = make_index_sequence; - - //// END OF CODE FROM GOOGLE ABSEIL - - #endif - - // dispatch utility (taken from ranges-v3) - template struct priority_tag : priority_tag < N - 1 > {}; - template<> struct priority_tag<0> {}; - - // taken from ranges-v3 - template - struct static_const - { - static JSON_INLINE_VARIABLE constexpr T value{}; - }; - - #ifndef JSON_HAS_CPP_17 - template - constexpr T static_const::value; - #endif - - template - constexpr std::array make_array(Args&& ... args) - { - return std::array {{static_cast(std::forward(args))...}}; - } - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // numeric_limits -#include // char_traits -#include // tuple -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // random_access_iterator_tag - -// #include - -// #include - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - - template - struct iterator_types {}; - - template - struct iterator_types < - It, - void_t> - { - using difference_type = typename It::difference_type; - using value_type = typename It::value_type; - using pointer = typename It::pointer; - using reference = typename It::reference; - using iterator_category = typename It::iterator_category; - }; - - // This is required as some compilers implement std::iterator_traits in a way that - // doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. - template - struct iterator_traits - { - }; - - template - struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types - { - }; - - template - struct iterator_traits::value>> - { - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; - }; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); - -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); - -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann -// SPDX-License-Identifier: MIT - -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector - -// #include - - -/*! - @ brief namespace for Niels Lohman*n - @see https://github.com/nlohmann - @since version 1.0.0 - */ -NLOHMANN_JSON_NAMESPACE_BEGIN - -/*! - @ brief default JSONSerializer tem*plate argument - - This serializer ignores the template arguments and uses ADL - ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) - for serialization. - */ -template -struct adl_serializer; - -/// a class to store JSON values -/// @sa https://json.nlohmann.me/api/basic_json/ -template class ObjectType = -std::map, -template class ArrayType = std::vector, -class StringType = std::string, class BooleanType = bool, -class NumberIntegerType = std::int64_t, -class NumberUnsignedType = std::uint64_t, -class NumberFloatType = double, -template class AllocatorType = std::allocator, -template class JSONSerializer = -adl_serializer, -class BinaryType = std::vector, // cppcheck-suppress syntaxError -class CustomBaseClass = void> -class basic_json; - -/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document -/// @sa https://json.nlohmann.me/api/json_pointer/ -template -class json_pointer; - -/*! - @ brief default specialization * - @sa https://json.nlohmann.me/api/json/ - */ -using json = basic_json<>; - -/// @brief a minimal map-like container that preserves insertion order -/// @sa https://json.nlohmann.me/api/ordered_map/ -template -struct ordered_map; - -/// @brief specialization that maintains the insertion order of object keys -/// @sa https://json.nlohmann.me/api/ordered_json/ -using ordered_json = basic_json; - -NLOHMANN_JSON_NAMESPACE_END - -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ - - -NLOHMANN_JSON_NAMESPACE_BEGIN -/*! - @ brief detail namespace with internal* helper functions - - This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail -{ - - ///////////// - // helpers // - ///////////// - - // Note to maintainers: - // - // Every trait in this file expects a non CV-qualified type. - // The only exceptions are in the 'aliases for detected' section - // (i.e. those of the form: decltype(T::member_function(std::declval()))) - // - // In this case, T has to be properly CV-qualified to constraint the function arguments - // (e.g. to_json(BasicJsonType&, const T&)) - - template struct is_basic_json : std::false_type {}; - - NLOHMANN_BASIC_JSON_TPL_DECLARATION - struct is_basic_json : std::true_type {}; - - // used by exceptions create() member functions - // true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t - // false_type otherwise - template - struct is_basic_json_context : - std::integral_constant < bool, - is_basic_json::type>::type>::value - || std::is_same::value > - {}; - - ////////////////////// - // json_ref helpers // - ////////////////////// - - template - class json_ref; - - template - struct is_json_ref : std::false_type {}; - - template - struct is_json_ref> : std::true_type {}; - - ////////////////////////// - // aliases for detected // - ////////////////////////// - - template - using mapped_type_t = typename T::mapped_type; - - template - using key_type_t = typename T::key_type; - - template - using value_type_t = typename T::value_type; - - template - using difference_type_t = typename T::difference_type; - - template - using pointer_t = typename T::pointer; - - template - using reference_t = typename T::reference; - - template - using iterator_category_t = typename T::iterator_category; - - template - using to_json_function = decltype(T::to_json(std::declval()...)); - - template - using from_json_function = decltype(T::from_json(std::declval()...)); - - template - using get_template_function = decltype(std::declval().template get()); - - // trait checking if JSONSerializer::from_json(json const&, udt&) exists - template - struct has_from_json : std::false_type {}; - - // trait checking if j.get is valid - // use this trait instead of std::is_constructible or std::is_convertible, - // both rely on, or make use of implicit conversions, and thus fail when T - // has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) - template - struct is_getable - { - static constexpr bool value = is_detected::value; - }; - - template - struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> - { - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; - }; - - // This trait checks if JSONSerializer::from_json(json const&) exists - // this overload is used for non-default-constructible user-defined-types - template - struct has_non_default_from_json : std::false_type {}; - - template - struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> - { - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; - }; - - // This trait checks if BasicJsonType::json_serializer::to_json exists - // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. - template - struct has_to_json : std::false_type {}; - - template - struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> - { - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; - }; - - template - using detect_key_compare = typename T::key_compare; - - template - struct has_key_compare : std::integral_constant::value> {}; - - // obtains the actual object key comparator - template - struct actual_object_comparator - { - using object_t = typename BasicJsonType::object_t; - using object_comparator_t = typename BasicJsonType::default_object_comparator_t; - using type = typename std::conditional < has_key_compare::value, - typename object_t::key_compare, object_comparator_t>::type; - }; - - template - using actual_object_comparator_t = typename actual_object_comparator::type; - - ///////////////// - // char_traits // - ///////////////// - - // Primary template of char_traits calls std char_traits - template - struct char_traits : std::char_traits - {}; - - // Explicitly define char traits for unsigned char since it is not standard - template<> - struct char_traits : std::char_traits - { - using char_type = unsigned char; - using int_type = uint64_t; - - // Redefine to_int_type function - static int_type to_int_type(char_type c) noexcept - { - return static_cast(c); - } - - static char_type to_char_type(int_type i) noexcept - { - return static_cast(i); - } - - static constexpr int_type eof() noexcept - { - return static_cast(std::char_traits::eof()); - } - }; - - // Explicitly define char traits for signed char since it is not standard - template<> - struct char_traits : std::char_traits - { - using char_type = signed char; - using int_type = uint64_t; - - // Redefine to_int_type function - static int_type to_int_type(char_type c) noexcept - { - return static_cast(c); - } - - static char_type to_char_type(int_type i) noexcept - { - return static_cast(i); - } - - static constexpr int_type eof() noexcept - { - return static_cast(std::char_traits::eof()); - } - }; - - /////////////////// - // is_ functions // - /////////////////// - - // https://en.cppreference.com/w/cpp/types/conjunction - template struct conjunction : std::true_type { }; - template struct conjunction : B { }; - template - struct conjunction - : std::conditional(B::value), conjunction, B>::type {}; - - // https://en.cppreference.com/w/cpp/types/negation - template struct negation : std::integral_constant < bool, !B::value > { }; - - // Reimplementation of is_constructible and is_default_constructible, due to them being broken for - // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). - // This causes compile errors in e.g. clang 3.5 or gcc 4.9. - template - struct is_default_constructible : std::is_default_constructible {}; - - template - struct is_default_constructible> - : conjunction, is_default_constructible> {}; - - template - struct is_default_constructible> - : conjunction, is_default_constructible> {}; - - template - struct is_default_constructible> - : conjunction...> {}; - - template - struct is_default_constructible> - : conjunction...> {}; - - template - struct is_constructible : std::is_constructible {}; - - template - struct is_constructible> : is_default_constructible> {}; - - template - struct is_constructible> : is_default_constructible> {}; - - template - struct is_constructible> : is_default_constructible> {}; - - template - struct is_constructible> : is_default_constructible> {}; - - template - struct is_iterator_traits : std::false_type {}; - - template - struct is_iterator_traits> - { - private: - using traits = iterator_traits; - - public: - static constexpr auto value = - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value; - }; - - template - struct is_range - { - private: - using t_ref = typename std::add_lvalue_reference::type; - - using iterator = detected_t; - using sentinel = detected_t; - - // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator - // and https://en.cppreference.com/w/cpp/iterator/sentinel_for - // but reimplementing these would be too much work, as a lot of other concepts are used underneath - static constexpr auto is_iterator_begin = - is_iterator_traits>::value; - - public: - static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; - }; - - template - using iterator_t = enable_if_t::value, result_of_begin())>>; - - template - using range_value_t = value_type_t>>; - - // The following implementation of is_complete_type is taken from - // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ - // and is written by Xiang Fan who agreed to using it in this library. - - template - struct is_complete_type : std::false_type {}; - - template - struct is_complete_type : std::true_type {}; - - template - struct is_compatible_object_type_impl : std::false_type {}; - - template - struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> - { - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = - is_constructible::value && - is_constructible::value; - }; - - template - struct is_compatible_object_type - : is_compatible_object_type_impl {}; - - template - struct is_constructible_object_type_impl : std::false_type {}; - - template - struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> - { - using object_t = typename BasicJsonType::object_t; - - static constexpr bool value = - (is_default_constructible::value && - (std::is_move_assignable::value || - std::is_copy_assignable::value) && - (is_constructible::value && - std::is_same < - typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type >::value)) || - (has_from_json::value || - has_non_default_from_json < - BasicJsonType, - typename ConstructibleObjectType::mapped_type >::value); - }; - - template - struct is_constructible_object_type - : is_constructible_object_type_impl {}; - - template - struct is_compatible_string_type - { - static constexpr auto value = - is_constructible::value; - }; - - template - struct is_constructible_string_type - { - // launder type through decltype() to fix compilation failure on ICPC - #ifdef __INTEL_COMPILER - using laundered_type = decltype(std::declval()); - #else - using laundered_type = ConstructibleStringType; - #endif - - static constexpr auto value = - conjunction < - is_constructible, - is_detected_exact>::value; - }; - - template - struct is_compatible_array_type_impl : std::false_type {}; - - template - struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < - is_detected::value&& - is_iterator_traits>>::value&& - // special case for types like std::filesystem::path whose iterator's value_type are themselves - // c.f. https://github.com/nlohmann/json/pull/3073 - !std::is_same>::value >> - { - static constexpr bool value = - is_constructible>::value; - }; - - template - struct is_compatible_array_type - : is_compatible_array_type_impl {}; - - template - struct is_constructible_array_type_impl : std::false_type {}; - - template - struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t::value >> - : std::true_type {}; - - template - struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t < !std::is_same::value&& - !is_compatible_string_type::value&& - is_default_constructible::value&& - (std::is_move_assignable::value || - std::is_copy_assignable::value)&& - is_detected::value&& - is_iterator_traits>>::value&& - is_detected::value&& - // special case for types like std::filesystem::path whose iterator's value_type are themselves - // c.f. https://github.com/nlohmann/json/pull/3073 - !std::is_same>::value&& - is_complete_type < - detected_t>::value >> - { - using value_type = range_value_t; - - static constexpr bool value = - std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, - value_type >::value; - }; - - template - struct is_constructible_array_type - : is_constructible_array_type_impl {}; - - template - struct is_compatible_integer_type_impl : std::false_type {}; - - template - struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& - !std::is_same::value >> - { - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value = - is_constructible::value && - CompatibleLimits::is_integer && - RealLimits::is_signed == CompatibleLimits::is_signed; - }; - - template - struct is_compatible_integer_type - : is_compatible_integer_type_impl {}; - - template - struct is_compatible_type_impl: std::false_type {}; - - template - struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t::value >> - { - static constexpr bool value = - has_to_json::value; - }; - - template - struct is_compatible_type - : is_compatible_type_impl {}; - - template - struct is_constructible_tuple : std::false_type {}; - - template - struct is_constructible_tuple> : conjunction...> {}; - - template - struct is_json_iterator_of : std::false_type {}; - - template - struct is_json_iterator_of : std::true_type {}; - - template - struct is_json_iterator_of : std::true_type - {}; - - // checks if a given type T is a template specialization of Primary - template