diff --git a/src/cps/loader.cpp b/src/cps/loader.cpp index c0a0ee0..4578684 100644 --- a/src/cps/loader.cpp +++ b/src/cps/loader.cpp @@ -17,7 +17,7 @@ namespace cps::loader { namespace { - constexpr static std::string_view CPS_VERSION = "0.10.0"; + constexpr static std::string_view CPS_VERSION = "0.12.0"; template tl::expected, std::string> @@ -116,12 +116,14 @@ namespace cps::loader { const nlohmann::json & value = parent[name]; if (value.is_object()) { - // TODO: simplify this further, maybe with a loop? - auto && cb = [](auto && r) { return r.value_or(std::vector{}); }; - ret[KnownLanguages::c] = CPS_TRY(get_optional>(value, name, "c").map(cb)); - ret[KnownLanguages::cxx] = CPS_TRY(get_optional>(value, name, "c++").map(cb)); + auto && fallback = CPS_TRY(get_optional>(value, name, "*")) + .value_or(std::vector{}); + ret[KnownLanguages::c] = + CPS_TRY(get_optional>(value, name, "c")).value_or(fallback); + ret[KnownLanguages::cxx] = + CPS_TRY(get_optional>(value, name, "c++")).value_or(fallback); ret[KnownLanguages::fortran] = - CPS_TRY(get_optional>(value, name, "fortran").map(cb)); + CPS_TRY(get_optional>(value, name, "fortran")).value_or(fallback); } else if (value.is_array()) { std::vector fin; for (auto && v : value) { @@ -140,22 +142,44 @@ namespace cps::loader { template <> tl::expected get_required(const nlohmann::json & parent, std::string_view parent_name, const std::string & name) { - LangValues && lang = CPS_TRY(get_required(parent, parent_name, name)); Defines ret; - for (auto && [k, values] : lang) { - ret[k] = {}; - for (auto && value : values) { - if (value.front() == '!') { - ret[k].emplace_back(Define{value.substr(1), false}); - } else if (const size_t sep = value.find("="); sep != value.npos) { - std::string dkey = value.substr(0, sep); - std::string dvalue = value.substr(sep + 1); - ret[k].emplace_back(Define{dkey, dvalue}); - } else { - ret[k].emplace_back(Define{value}); + if (!parent.contains(name)) { + return ret; + } + + const nlohmann::json & defines = parent[name]; + if (!defines.is_object()) { + return tl::unexpected(fmt::format("Section `{}` of `{}` is not an object", parent_name, name)); + } + + const auto getter = + [&](const std::string & lang) -> tl::expected>, std::string> { + if (!defines.contains(lang)) { + return std::nullopt; + } + + std::vector ret2; + for (auto && [k, v] : defines.at(lang).items()) { + if (v.is_null()) { + ret2.emplace_back(Define{k}); + continue; } + if (!v.is_string()) { + return tl::unexpected(fmt::format( + "key `{}` of language `{}` of section `{}` of `{}` has a value that is not a string", k, + lang, parent_name, name)); + } + ret2.emplace_back(Define{k, v.get()}); } - } + + return ret2; + }; + + auto && fallback = CPS_TRY(getter("*")).value_or(std::vector{}); + ret[KnownLanguages::c] = CPS_TRY(getter("c")).value_or(fallback); + ret[KnownLanguages::cxx] = CPS_TRY(getter("cxx")).value_or(fallback); + ret[KnownLanguages::fortran] = CPS_TRY(getter("fortran")).value_or(fallback); + return ret; }; @@ -217,7 +241,7 @@ namespace cps::loader { auto const type = CPS_TRY(get_required(comp, name, "type").map(string_to_type)); auto const compile_flags = CPS_TRY(get_required(comp, name, "compile_flags")); auto const includes = CPS_TRY(get_required(comp, name, "includes")); - auto const defines = CPS_TRY(get_required(comp, name, "defines")); + auto const definitions = CPS_TRY(get_required(comp, name, "definitions")); auto const link_flags = CPS_TRY(get_optional>(comp, name, "link_flags")) .value_or(std::vector{}); auto const link_libraries = @@ -241,7 +265,7 @@ namespace cps::loader { .type = std::move(type), .compile_flags = std::move(compile_flags), .includes = std::move(includes), - .defines = std::move(defines), + .definitions = std::move(definitions), .link_flags = std::move(link_flags), .link_libraries = std::move(link_libraries), .location = std::move(location), @@ -259,17 +283,11 @@ namespace cps::loader { } // namespace - Define::Define(std::string name_) : name{std::move(name_)}, value{}, define{true} {}; - Define::Define(std::string name_, std::string value_) - : name{std::move(name_)}, value{std::move(value_)}, define{true} {}; - Define::Define(std::string name_, bool define_) : name{std::move(name_)}, value{}, define{define_} {}; - - bool Define::is_undefine() const { return !define; } - - bool Define::is_define() const { return define && value.empty(); } + Define::Define(std::string name_) : name{std::move(name_)}, value{std::nullopt} {}; + Define::Define(std::string name_, std::string value_) : name{std::move(name_)}, value{std::move(value_)} {}; std::string Define::get_name() const { return name; } - std::string Define::get_value() const { return value; } + std::optional Define::get_value() const { return value; } Configuration::Configuration() = default; Configuration::Configuration(LangValues cflags) : compile_flags{std::move(cflags)} {}; diff --git a/src/cps/loader.hpp b/src/cps/loader.hpp index 41bce71..5b68d4b 100644 --- a/src/cps/loader.hpp +++ b/src/cps/loader.hpp @@ -47,17 +47,13 @@ namespace cps::loader { public: Define(std::string name); Define(std::string name, std::string value); - Define(std::string name, bool define); - bool is_undefine() const; - bool is_define() const; std::string get_name() const; - std::string get_value() const; + std::optional get_value() const; private: std::string name; - std::string value; - bool define; + std::optional value; }; using LangValues = std::unordered_map>; @@ -68,7 +64,7 @@ namespace cps::loader { Type type; LangValues compile_flags; LangValues includes; - Defines defines; + Defines definitions; // TODO: configurations // TODO: std::vector link_features; std::vector link_flags; diff --git a/src/cps/printer.cpp b/src/cps/printer.cpp index 3221e04..01dd9da 100644 --- a/src/cps/printer.cpp +++ b/src/cps/printer.cpp @@ -37,14 +37,13 @@ namespace cps::printer { } if (conf.defines) { - if (auto && f = r.defines.find(loader::KnownLanguages::c); f != r.defines.end() && !f->second.empty()) { + if (auto && f = r.definitions.find(loader::KnownLanguages::c); + f != r.definitions.end() && !f->second.empty()) { auto && transformer = [](auto && d) { - if (d.is_define()) { - return fmt::format("-D{}", d.get_name()); - } else if (d.is_undefine()) { - return fmt::format("-U{}", d.get_name()); + if (auto && v = d.get_value()) { + return fmt::format("-D{}={}", d.get_name(), v.value()); } else { - return fmt::format("-D{}={}", d.get_name(), d.get_value()); + return fmt::format("-D{}", d.get_name()); } }; args.reserve(args.size() + f->second.size()); diff --git a/src/cps/search.cpp b/src/cps/search.cpp index 43a78d5..43045e7 100644 --- a/src/cps/search.cpp +++ b/src/cps/search.cpp @@ -236,7 +236,7 @@ namespace cps::search { // 1. the provided version (or Compat-Version) is < the required version // 2. This package lacks required components if (requirements.version) { - // From the CPS spec, version 0.10.0, for package::version, + // From the CPS spec, version 0.12.0, for package::version, // which as the same semantics as requirement::version: // // > If not provided, the CPS will not satisfy any request for @@ -459,7 +459,7 @@ namespace cps::search { // 2. if we do it at the search point we have to plumb overrides // deep into that merge_result(comp.includes, result.includes, prefix_replacer); - merge_result(comp.defines, result.defines); + merge_result(comp.definitions, result.definitions); merge_result(comp.compile_flags, result.compile_flags); merge_result(comp.link_libraries, result.link_libraries); merge_result(comp.link_flags, result.link_flags); diff --git a/src/cps/search.hpp b/src/cps/search.hpp index 8d9dd01..9e12785 100644 --- a/src/cps/search.hpp +++ b/src/cps/search.hpp @@ -20,7 +20,7 @@ namespace cps::search { std::string version; loader::LangValues includes; loader::LangValues compile_flags; - loader::Defines defines; + loader::Defines definitions; std::vector link_flags; std::vector link_libraries; std::vector link_location; diff --git a/tests/cases/cps-config.toml b/tests/cases/cps-config.toml index 390f951..5de331e 100644 --- a/tests/cases/cps-config.toml +++ b/tests/cases/cps-config.toml @@ -10,13 +10,13 @@ expected = "0.0.1" name = "cflags" cps = "minimal" args = ["flags", "--cflags"] -expected = "-fopenmp -I/usr/local/include -I/opt/include -DFOO=1 -DBAR=2 -UBAR -DOTHER" +expected = "-fopenmp -I/usr/local/include -I/opt/include -DFOO=1 -DBAR=2 -DOTHER" [[case]] name = "cflags-only-other" cps = "minimal" args = ["flags", "--cflags-only-other"] -expected = "-fopenmp -DFOO=1 -DBAR=2 -UBAR -DOTHER" +expected = "-fopenmp -DFOO=1 -DBAR=2 -DOTHER" [[case]] name = "cflags-only-I" @@ -108,3 +108,15 @@ name = "component link-flags only other" cps = "multiple-components" args = ["flags", "--component", "link-flags", "--libs-only-other"] expected = "-flto" + +[[case]] +name = "Star components" +cps = "full" +args = ["flags", "--component", "star_values", "--cflags", "--print-errors"] +expected = "-fvectorize -I/usr/local/include -I/opt/include -DBAR=2 -DFOO=1 -DOTHER" + +[[case]] +name = "Star components override by name" +cps = "full" +args = ["flags", "--component", "star_values_override", "--cflags", "--print-errors"] +expected = "-fvectorize -I/usr/local/include -I/opt/include -DBAR=2 -DFOO=1 -DOTHER" diff --git a/tests/cases/pkg-config-compat.toml b/tests/cases/pkg-config-compat.toml index ed61ea8..d1c87d1 100644 --- a/tests/cases/pkg-config-compat.toml +++ b/tests/cases/pkg-config-compat.toml @@ -32,13 +32,13 @@ expected = "-flto" name = "cflags" cps = "full" args = ["pkg-config", "--cflags"] -expected = "-fvectorize -I/usr/local/include -I/opt/include -DFOO=1 -DBAR=2 -UBAR -DOTHER" +expected = "-fvectorize -I/usr/local/include -I/opt/include -DFOO=1 -DBAR=2 -DOTHER" [[case]] name = "cflags-only-other" cps = "full" args = ["pkg-config", "--cflags-only-other"] -expected = "-fvectorize -DFOO=1 -DBAR=2 -UBAR -DOTHER" +expected = "-fvectorize -DFOO=1 -DBAR=2 -DOTHER" [[case]] name = "cflags-only-I" diff --git a/tests/cps-files/lib/cps/cps-path-not-set.cps.in b/tests/cps-files/lib/cps/cps-path-not-set.cps.in index cb3b4a1..1b3c2ad 100644 --- a/tests/cps-files/lib/cps/cps-path-not-set.cps.in +++ b/tests/cps-files/lib/cps/cps-path-not-set.cps.in @@ -1,6 +1,6 @@ { "name": "cps-path-not-set", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "version": "1.0.0", "components": { "default": { diff --git a/tests/cps-files/lib/cps/cps-path-set.cps.in b/tests/cps-files/lib/cps/cps-path-set.cps.in index 59ccebc..29315a3 100644 --- a/tests/cps-files/lib/cps/cps-path-set.cps.in +++ b/tests/cps-files/lib/cps/cps-path-set.cps.in @@ -1,6 +1,6 @@ { "name": "cps-path-set", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "cps_path": "@prefix@/${libdir}/cps/", "version": "1.0.0", "components": { diff --git a/tests/cps-files/lib/cps/diamond.cps b/tests/cps-files/lib/cps/diamond.cps index 46c3e3d..5cd8fc8 100644 --- a/tests/cps-files/lib/cps/diamond.cps +++ b/tests/cps-files/lib/cps/diamond.cps @@ -1,6 +1,6 @@ { "name": "diamond", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "requires": { "needs-components1": {}, "needs-components2": {} diff --git a/tests/cps-files/lib/cps/full.cps b/tests/cps-files/lib/cps/full.cps index ba1bd37..5f3fdac 100644 --- a/tests/cps-files/lib/cps/full.cps +++ b/tests/cps-files/lib/cps/full.cps @@ -1,6 +1,6 @@ { "name": "full", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "version": "1.2.1", "compat_version": "1.0.0", "components": { @@ -14,10 +14,10 @@ "/err" ] }, - "defines": { - "c": [ - "-DFAIL" - ] + "definitions": { + "c": { + "FAIL": null + } }, "location": "fake" }, @@ -32,17 +32,74 @@ "/opt/include" ] }, - "link_flags": ["-L/usr/lib/", "-lbar", "-flto"], - "defines": { + "link_flags": [ + "-L/usr/lib/", + "-lbar", + "-flto" + ], + "definitions": { + "c": { + "FOO": "1", + "BAR": "2", + "OTHER": null + } + }, + "location": "/something/lib/libfoo.so.1.2.0", + "link_location": "/something/lib/libfoo.so" + }, + "star_values": { + "type": "dylib", + "compile_flags": { + "*": ["-fvectorize"] + }, + "includes": { + "*": [ + "/usr/local/include", + "/opt/include" + ] + }, + "link_flags": [ + "-L/usr/lib/", + "-lbar", + "-flto" + ], + "definitions": { + "*": { + "FOO": "1", + "BAR": "2", + "OTHER": null + } + }, + "location": "/something/lib/libfoo.so.1.2.0", + "link_location": "/something/lib/libfoo.so" + }, + "star_values_override": { + "type": "dylib", + "compile_flags": { + "c": ["-fvectorize"], + "*": ["-bad-value"] + }, + "includes": { "c": [ - "FOO=1", - "BAR=2", - "!BAR", - "OTHER" + "/usr/local/include", + "/opt/include" ], - "c++": [ - "!FOO" - ] + "*": ["/dev/null"] + }, + "link_flags": [ + "-L/usr/lib/", + "-lbar", + "-flto" + ], + "definitions": { + "c": { + "FOO": "1", + "BAR": "2", + "OTHER": null + }, + "*": { + "BAD": "value" + } }, "location": "/something/lib/libfoo.so.1.2.0", "link_location": "/something/lib/libfoo.so" diff --git a/tests/cps-files/lib/cps/minimal.cps b/tests/cps-files/lib/cps/minimal.cps index 047e04c..8061972 100644 --- a/tests/cps-files/lib/cps/minimal.cps +++ b/tests/cps-files/lib/cps/minimal.cps @@ -1,6 +1,6 @@ { "name": "minimal", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "version": "1.0.0", "components": { "sample0": { @@ -13,10 +13,10 @@ "/err" ] }, - "defines": { - "c": [ - "-DFAIL" - ] + "definitions": { + "c": { + "FAIL": null + } }, "location": "fake" }, @@ -31,16 +31,12 @@ "/opt/include" ] }, - "defines": { - "c": [ - "FOO=1", - "BAR=2", - "!BAR", - "OTHER" - ], - "c++": [ - "!FOO" - ] + "definitions": { + "c": { + "FOO": "1", + "BAR": "2", + "OTHER": null + } }, "location": "fake" } diff --git a/tests/cps-files/lib/cps/multiple-components.cps b/tests/cps-files/lib/cps/multiple-components.cps index 0348aaa..3c296ef 100644 --- a/tests/cps-files/lib/cps/multiple-components.cps +++ b/tests/cps-files/lib/cps/multiple-components.cps @@ -1,6 +1,6 @@ { "name": "multiple-components", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "requires": { "minimal": {} }, @@ -15,7 +15,7 @@ "/usr/local/include" ] }, - "defines": [], + "definitions": {}, "location": "fake" }, "sample2": { @@ -26,13 +26,10 @@ "includes": [ "/opt/include" ], - "defines": { - "c": [ - "FOO=1" - ], - "c++": [ - "!FOO" - ] + "definitions": { + "c": { + "FOO": "1" + } }, "location": "/something/lib/libfoo.so.1.2.0" }, @@ -58,7 +55,11 @@ }, "link-flags": { "type": "dylib", - "link_flags": ["-L/usr/lib/", "-lbar", "-flto"], + "link_flags": [ + "-L/usr/lib/", + "-lbar", + "-flto" + ], "location": "/something/lib/libfoo.so" }, "requires-external": { diff --git a/tests/cps-files/lib/cps/needs-components1.cps b/tests/cps-files/lib/cps/needs-components1.cps index 682d87c..3ecb9af 100644 --- a/tests/cps-files/lib/cps/needs-components1.cps +++ b/tests/cps-files/lib/cps/needs-components1.cps @@ -1,6 +1,6 @@ { "name": "needs-components1", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "requires": { "multiple-components": { "components": [ diff --git a/tests/cps-files/lib/cps/needs-components2.cps b/tests/cps-files/lib/cps/needs-components2.cps index 69e5aac..9f2517d 100644 --- a/tests/cps-files/lib/cps/needs-components2.cps +++ b/tests/cps-files/lib/cps/needs-components2.cps @@ -1,6 +1,6 @@ { "name": "needs-components2", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "requires": { "multiple-components": { "components": [ diff --git a/tests/cps-files/lib/cps/needs-version.cps b/tests/cps-files/lib/cps/needs-version.cps index bdc4392..6075f30 100644 --- a/tests/cps-files/lib/cps/needs-version.cps +++ b/tests/cps-files/lib/cps/needs-version.cps @@ -1,17 +1,23 @@ { "name": "needs-components1", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "requires": { "multiple-components": { - "components": ["sample3"], + "components": [ + "sample3" + ], "version": "1.0" } }, "components": { "default": { "type": "interface", - "requires": ["multiple-components:sample3"] + "requires": [ + "multiple-components:sample3" + ] } }, - "default_components": ["default"] + "default_components": [ + "default" + ] } diff --git a/tests/loader.cpp b/tests/loader.cpp index b01b88a..2d006cf 100644 --- a/tests/loader.cpp +++ b/tests/loader.cpp @@ -27,7 +27,7 @@ namespace cps::utils::test { TEST(Loader, minimal_complete_package) { std::stringstream ss(R"({ "name": "minimal_complete_package", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "default": { "type": "archive", @@ -43,7 +43,7 @@ namespace cps::utils::test { TEST(Loader, components_must_not_be_empty) { std::stringstream ss(R"({ "name": "components_must_not_be_empty", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": {} } )"s); @@ -55,7 +55,7 @@ namespace cps::utils::test { TEST(Loader, archive_missing_location) { std::stringstream ss(R"({ "name": "archive_missing_location", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "default": { "type": "archive", @@ -70,7 +70,7 @@ namespace cps::utils::test { TEST(Loader, missing_name) { std::stringstream ss(R"({ - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "default": { "type": "archive", @@ -101,7 +101,7 @@ namespace cps::utils::test { TEST(Loader, missing_components) { std::stringstream ss(R"({ "name": "missing_components", - "cps_version": "0.10.0", + "cps_version": "0.12.0", } )"s); auto const package = cps::loader::load(ss, "missing_components"); @@ -111,7 +111,7 @@ namespace cps::utils::test { TEST(Loader, name_is_string) { std::stringstream ss(R"({ "name": [], - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "default": { "type": "archive", @@ -144,7 +144,7 @@ namespace cps::utils::test { TEST(Loader, components_is_object) { std::stringstream ss(R"({ "name": "components_is_object", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": [], } )"s); @@ -201,13 +201,13 @@ namespace cps::utils::test { )"s); auto const package = cps::loader::load(ss, "cps_version_is_0_10_0"); ASSERT_FALSE(package.has_value()) - << "should not have parsed, root requires `cps_version` value to exactly `0.10.0`"; + << "should not have parsed, root requires `cps_version` value to exactly `0.12.0`"; } TEST(Loader, valid_component_types) { std::stringstream ss(R"({ "name": "valid_component_types", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "a": { "type": "archive", @@ -242,7 +242,7 @@ namespace cps::utils::test { TEST(Loader, valid_component_type_extension) { std::stringstream ss(R"({ "name": "valid_component_type_extension", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "a": { "type": "archive", @@ -264,7 +264,7 @@ namespace cps::utils::test { TEST(Loader, not_recognized_type_is_valid_but_ignored_can_make_empty_component) { std::stringstream ss(R"({ "name": "not_recognized_type_is_valid_but_ignored_can_make_empty_component", - "cps_version": "0.10.0", + "cps_version": "0.12.0", "components": { "a": { "type": "not_recognized_type_is_valid_but_ignored",