From 3de5120d299a5e17c9c17dfd2bfc4bb4e6340941 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 20 Jun 2022 20:58:47 +0300 Subject: Add support for additional *-build package manifest values and alternative buildfile naming --- libbpkg/manifest.cxx | 90 ++++++++++++++++++-- libbpkg/manifest.hxx | 33 ++++++++ tests/manifest/testscript | 206 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 323 insertions(+), 6 deletions(-) diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index 9578977..4555bbc 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -3367,6 +3367,20 @@ namespace bpkg return (fl & f) != package_manifest_flags::none; }; + // Based on the `*-build[2]` value name set the manifest's alt_naming flag + // if absent and verify that it doesn't change otherwise. + // + auto alt_naming = [&m, &bad_name] (const string& n) + { + bool v (n.size () > 7 && n.compare (n.size () - 7, 7, "-build2") == 0); + + if (!m.alt_naming) + m.alt_naming = v; + else if (*m.alt_naming != v) + bad_name (string (*m.alt_naming ? "alternative" : "standard") + + " buildfile naming scheme is already used"); + }; + // Cache the upstream version manifest value and validate whether it's // allowed later, after the version value is parsed. // @@ -3701,20 +3715,77 @@ namespace bpkg tests.push_back (move (nv)); } - else if (n == "bootstrap-build") + else if (n == "bootstrap-build" || n == "bootstrap-build2") { + alt_naming (n); + if (m.bootstrap_build) - bad_name ("package bootstrap-build redefinition"); + bad_name ("package " + n + " redefinition"); m.bootstrap_build = move (v); } - else if (n == "root-build") + else if (n == "root-build" || n == "root-build2") { + alt_naming (n); + if (m.root_build) - bad_name ("package root-build redefinition"); + bad_name ("package " + n + " redefinition"); m.root_build = move (v); } + else if ((n.size () > 6 && n.compare (n.size () - 6, 6, "-build") == 0) || + (n.size () > 7 && n.compare (n.size () - 7, 7, "-build2") == 0)) + { + alt_naming (n); + + // Verify that the path doesn't contain backslashes which would be + // interpreted differently on Windows and POSIX. + // + if (n.find ('\\') != string::npos) + bad_name ("backslash in package buildfile path"); + + // Strip the '-build' suffix. + // + n.resize (n.size () - (*m.alt_naming ? 7 : 6)); + + try + { + path f (move (n)); // Note: not empty. + + // Fail if the value name is something like `config/-build`. + // + if (f.to_directory ()) + bad_name ("empty package buildfile name"); + + if (f.absolute ()) + bad_name ("absolute package buildfile path"); + + // Verify that the path refers inside the package's build/ + // subdirectory. + // + f.normalize (); // Note: can't throw since the path is relative. + + if (dir_path::traits_type::parent (*f.begin ())) + bad_name ("package buildfile path refers outside build/ " + "subdirectory"); + + // Check for duplicates. + // + vector& bs (m.buildfiles); + if (find_if (bs.begin (), bs.end (), + [&f] (const auto& v) {return v.path == f;}) != + bs.end ()) + { + bad_name ("package buildfile redefinition"); + } + + bs.push_back (buildfile (move (f), move (v))); + } + catch (const invalid_path&) + { + bad_name ("invalid package buildfile path"); + } + } else if (n == "location") { if (flag (package_manifest_flags::forbid_location)) @@ -4446,11 +4517,18 @@ namespace bpkg : c.config + "/" + *c.target, c.comment)); + bool an (m.alt_naming && *m.alt_naming); + if (m.bootstrap_build) - s.next ("bootstrap-build", *m.bootstrap_build); + s.next (an ? "bootstrap-build2" : "bootstrap-build", + *m.bootstrap_build); if (m.root_build) - s.next ("root-build", *m.root_build); + s.next (an ? "root-build2" : "root-build", *m.root_build); + + for (const auto& bf: m.buildfiles) + s.next (bf.path.posix_string () + (an ? "-build2" : "-build"), + bf.content); if (m.location) s.next ("location", m.location->posix_string ()); diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 09d64d1..a3415cc 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -1054,6 +1054,29 @@ namespace bpkg string () const; }; + // Package's buildfile path and content. + // + struct buildfile + { + // The path is relative to the package's build/ subdirectory with the + // extension stripped. + // + // For example, for the build/config/common.build file the path will be + // config/common. + // + // Note that the actual file path depends on the project's buildfile + // naming scheme and for the config/common example above the actual path + // can also be build2/config/common.build2. + // + butl::path path; + std::string content; + + buildfile () = default; + buildfile (butl::path p, std::string c) + : path (std::move (p)), + content (std::move (c)) {} + }; + class LIBBPKG_EXPORT package_manifest { public: @@ -1090,9 +1113,19 @@ namespace bpkg butl::small_vector builds; std::vector build_constraints; + // If true, then this package use the alternative buildfile naming scheme + // (build2/, .build2). In the manifest serialization this is encoded as + // either *-build or *-build2 value names. + // + butl::optional alt_naming; + butl::optional bootstrap_build; butl::optional root_build; + // Additional buildfiles which are potentially included by root.build. + // + std::vector buildfiles; + // The following values are only valid in the manifest list (and only for // certain repository types). // diff --git a/tests/manifest/testscript b/tests/manifest/testscript index ded4c57..760a9de 100644 --- a/tests/manifest/testscript +++ b/tests/manifest/testscript @@ -3174,6 +3174,212 @@ stdin:6:8: error: unexpected enable clause EOE } + + : buildfile + : + { + : standard-naming + : + $* <>EOF + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + bootstrap-build:\ + project = libfoo + + using version + using config + using dist + using test + using install + \ + root-build:\ + include config/common.build + + cxx.std = latest + + using cxx + \ + config/common-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOF + + : alt-naming + : + $* <>EOF + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + bootstrap-build2:\ + project = libfoo + + using version + using config + using dist + using test + using install + \ + root-build2:\ + include config/common.build + + cxx.std = latest + + using cxx + \ + config/common-build2:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOF + + : mixed-naming + : + $* <>EOE != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + bootstrap-build:\ + project = libfoo + + using version + using config + using dist + using test + using install + \ + root-build:\ + include config/common.build + + cxx.std = latest + + using cxx + \ + config/common-build2:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + stdin:22:1: error: standard buildfile naming scheme is already used + EOE + + : backslash + : + $* <>EOE != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + config\common-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + stdin:6:1: error: backslash in package buildfile path + EOE + + : unknown + : + $* <>EOE != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + common.build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + stdin:6:1: error: unknown name 'common.build' in package manifest + EOE + + : empty-name + : + $* <>EOE != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + config/-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + stdin:6:1: error: empty package buildfile name + EOE + + : absolute-invalid + : + $* <>~%EOE% != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + /config/common-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + %stdin:6:1: error: (absolute|invalid) package buildfile path% + EOE + + : outside + : + $* <>EOE != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + common/../../common-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + stdin:6:1: error: package buildfile path refers outside build/ subdirectory + EOE + + : redefinition + : + $* <>EOE != 0 + : 1 + name: libfoo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + common-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + common-build:\ + { + config [bool] config.libfoo.extras ?= false + } + \ + EOI + stdin:11:1: error: package buildfile redefinition + EOE + } } : package-list -- cgit v1.1