aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/algorithm.hxx4
-rw-r--r--build2/algorithm.ixx4
-rw-r--r--build2/bash/init.cxx2
-rw-r--r--build2/bash/utility.hxx9
-rw-r--r--build2/cc/common.cxx1
-rw-r--r--build2/cc/common.hxx2
-rw-r--r--build2/cc/pkgconfig.cxx8
-rw-r--r--build2/context.cxx8
-rw-r--r--build2/context.hxx4
-rw-r--r--build2/file.cxx59
-rw-r--r--build2/file.hxx2
-rw-r--r--build2/function.cxx2
-rw-r--r--build2/functions-project-name.cxx63
-rw-r--r--build2/install/init.cxx2
-rw-r--r--build2/name.cxx4
-rw-r--r--build2/name.hxx4
-rw-r--r--build2/operation.cxx2
-rw-r--r--build2/parser.cxx27
-rw-r--r--build2/parser.hxx6
-rw-r--r--build2/prerequisite.cxx2
-rw-r--r--build2/prerequisite.hxx8
-rw-r--r--build2/target.hxx6
-rw-r--r--build2/types.hxx5
-rw-r--r--build2/utility.cxx16
-rw-r--r--build2/utility.hxx16
-rw-r--r--build2/variable.cxx53
-rw-r--r--build2/variable.hxx22
-rw-r--r--build2/variable.ixx11
-rw-r--r--build2/version/init.cxx16
-rw-r--r--build2/version/module.hxx11
-rw-r--r--tests/name/pattern.test4
-rw-r--r--tests/value/reverse.test4
32 files changed, 271 insertions, 116 deletions
diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx
index 22b8471..430cc9f 100644
--- a/build2/algorithm.hxx
+++ b/build2/algorithm.hxx
@@ -65,7 +65,7 @@ namespace build2
const string& name,
const string* ext = nullptr, // NULL means unspecified.
const scope* = nullptr, // NULL means dir is absolute.
- const optional<string>& proj = nullopt);
+ const optional<project_name>& proj = nullopt);
const target*
search_existing (const target_type& type,
@@ -74,7 +74,7 @@ namespace build2
const string& name,
const string* ext = nullptr,
const scope* = nullptr,
- const optional<string>& proj = nullopt);
+ const optional<project_name>& proj = nullopt);
// As above but specify the target type as template argument.
//
diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx
index fb77e9c..e41b87f 100644
--- a/build2/algorithm.ixx
+++ b/build2/algorithm.ixx
@@ -78,7 +78,7 @@ namespace build2
const string& name,
const string* ext,
const scope* scope,
- const optional<string>& proj)
+ const optional<project_name>& proj)
{
return search (
t,
@@ -99,7 +99,7 @@ namespace build2
const string& name,
const string* ext,
const scope* scope,
- const optional<string>& proj)
+ const optional<project_name>& proj)
{
return search_existing (
prerequisite_key {
diff --git a/build2/bash/init.cxx b/build2/bash/init.cxx
index ff653c2..7bfea58 100644
--- a/build2/bash/init.cxx
+++ b/build2/bash/init.cxx
@@ -54,7 +54,7 @@ namespace build2
// Install into bin/<project>/ by default stripping the .bash
// extension from <project> if present.
//
- const string& p (cast<string> (rs.vars[var_project]));
+ const project_name& p (cast<project_name> (rs.vars[var_project]));
install_path<bash> (bs, dir_path ("bin") /= project_base (p));
install_mode<bash> (bs, "644");
diff --git a/build2/bash/utility.hxx b/build2/bash/utility.hxx
index d8d9a43..67f4552 100644
--- a/build2/bash/utility.hxx
+++ b/build2/bash/utility.hxx
@@ -14,13 +14,12 @@ namespace build2
{
// Strip the .bash extension from the project name.
//
+ // Note that the result may not be a valid project name.
+ //
inline string
- project_base (const string& n)
+ project_base (const project_name& pn)
{
- size_t p (path::traits::find_extension (n));
- return p == string::npos || casecmp (n.c_str () + p, ".bash", 5) != 0
- ? n
- : string (n, 0, p);
+ return pn.base ("bash");
}
}
}
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index 3c4994e..ca73483 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -444,6 +444,7 @@ namespace build2
// search (name, scope).
//
dir_path out;
+
prerequisite_key pk {n.proj, {tt, &n.dir, &out, &n.value, ext}, &s};
xt = search_library_existing (a, sysd, usrd, pk);
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index 27706f3..18824cb 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -288,7 +288,7 @@ namespace build2
bool
pkgconfig_load (action, const scope&,
bin::lib&, bin::liba*, bin::libs*,
- const optional<string>&,
+ const optional<project_name>&,
const string&,
const dir_path&,
const dir_paths&,
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index 5ace6ec..55c8b35 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -456,7 +456,7 @@ namespace build2
lib& lt,
liba* at,
libs* st,
- const optional<string>& proj,
+ const optional<project_name>& proj,
const string& stem,
const dir_path& libd,
const dir_paths& top_sysd,
@@ -551,7 +551,7 @@ namespace build2
if (proj)
{
f = dir;
- f /= *proj;
+ f /= proj->string ();
f += sfx;
f += ".pc";
if (exists (f))
@@ -1102,7 +1102,7 @@ namespace build2
lib&,
liba*,
libs*,
- const optional<string>&,
+ const optional<project_name>&,
const string&,
const dir_path&,
const dir_paths&,
@@ -1142,7 +1142,7 @@ namespace build2
ofdstream os (p);
{
- const string& n (cast<string> (rs.vars[var_project]));
+ const project_name& n (cast<project_name> (rs.vars[var_project]));
lookup vl (rs.vars[var_version]);
if (!vl)
diff --git a/build2/context.cxx b/build2/context.cxx
index 225ff1c..a8cf042 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -718,10 +718,10 @@ namespace build2
// Note that subprojects is not typed since the value requires
// pre-processing (see file.cxx).
//
- var_project = &vp.insert<string> ("project", v_p);
- var_amalgamation = &vp.insert<dir_path> ("amalgamation", v_p);
- var_subprojects = &vp.insert ("subprojects", v_p);
- var_version = &vp.insert<string> ("version", v_p);
+ var_project = &vp.insert<project_name> ("project", v_p);
+ var_amalgamation = &vp.insert<dir_path> ("amalgamation", v_p);
+ var_subprojects = &vp.insert ("subprojects", v_p);
+ var_version = &vp.insert<string> ("version", v_p);
var_project_url = &vp.insert<string> ("project.url", v_p);
var_project_summary = &vp.insert<string> ("project.summary", v_p);
diff --git a/build2/context.hxx b/build2/context.hxx
index cfe770f..cf7eee6 100644
--- a/build2/context.hxx
+++ b/build2/context.hxx
@@ -408,11 +408,11 @@ namespace build2
// Return the project name or empty string if unnamed.
//
- inline const string&
+ inline const project_name&
project (const scope& root)
{
auto l (root[var_project]);
- return l ? cast<string> (l) : empty_string;
+ return l ? cast<project_name> (l) : empty_project_name;
}
// Return the src/out directory corresponding to the given out/src. The
diff --git a/build2/file.cxx b/build2/file.cxx
index 1367b8c..d987226 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -47,9 +47,10 @@ namespace build2
{
// See find_subprojects() for details.
//
- const string& n (path::traits::is_separator (i->first.back ())
- ? empty_string
- : i->first);
+ const project_name& n (
+ path::traits::is_separator (i->first.string ().back ())
+ ? empty_project_name
+ : i->first);
os << (i != b ? " " : "") << n << '@' << i->second;
}
@@ -464,7 +465,7 @@ namespace build2
// Extract the project name from bootstrap.build.
//
- static string
+ static project_name
find_project_name (const dir_path& out_root,
const dir_path& fallback_src_root,
bool* src_hint = nullptr)
@@ -480,7 +481,7 @@ namespace build2
if (s.root_scope () == &s && s.out_path () == out_root)
{
if (lookup l = s.vars[var_project])
- return cast<string> (l);
+ return cast<project_name> (l);
src_root = s.src_path_;
}
@@ -518,7 +519,7 @@ namespace build2
}
}
- string name;
+ project_name name;
{
path f (*src_root / bootstrap_file);
auto p (extract_variable (f, *var_project));
@@ -527,7 +528,7 @@ namespace build2
fail << "variable " << var_project->name << " expected "
<< "as a first line in " << f;
- name = cast<string> (move (p.first));
+ name = cast<project_name> (move (p.first));
}
l5 ([&]{trace << "extracted project name '" << name << "' for "
@@ -576,7 +577,7 @@ namespace build2
// Load its name. Note that here we don't use fallback src_root
// since this function is used to scan both out_root and src_root.
//
- string name (find_project_name (sd, dir_path (), &src));
+ project_name name (find_project_name (sd, dir_path (), &src));
// If the name is empty, then is is an unnamed project. While the
// 'project' variable stays empty, here we come up with a surrogate
@@ -585,7 +586,8 @@ namespace build2
// sub-directory and appending a trailing directory separator to it.
//
if (name.empty ())
- name = dir.posix_string () + path::traits::directory_separator;
+ name = project_name (dir.posix_string () + '/',
+ project_name::raw_string);
// @@ Can't use move() because we may need the values in diagnostics
// below. Looks like C++17 try_emplace() is what we need.
@@ -770,7 +772,7 @@ namespace build2
{
// Project name.
//
- string n;
+ project_name n;
if (i->pair)
{
if (i->pair != '@')
@@ -778,7 +780,7 @@ namespace build2
try
{
- n = convert<string> (move (*i));
+ n = convert<project_name> (move (*i));
if (n.empty ())
fail << "empty project name in variable subprojects";
@@ -821,7 +823,8 @@ namespace build2
// See find_subprojects() for details on unnamed projects.
//
if (n.empty ())
- n = d.posix_string () + '/';
+ n = project_name (d.posix_string () + '/',
+ project_name::raw_string);
}
sps.emplace (move (n), move (d));
@@ -1082,24 +1085,6 @@ namespace build2
return rs;
}
- // Convert project name to import variable name.
- //
- static inline string
- import_name (const string& p)
- {
- // For simplicity let's assume project name is a package name and only
- // sanitize characters that we don't like from what's valid there.
- //
- auto sanitize = [] (char c)
- {
- return (c == '-' || c == '+' || c == '.') ? '_' : c;
- };
-
- string r;
- transform (p.begin (), p.end (), back_inserter (r), sanitize);
- return r;
- }
-
names
import (scope& ibase, name target, const location& loc)
{
@@ -1113,13 +1098,13 @@ namespace build2
//
if (target.unqualified ())
{
- target.proj = string ();
+ target.proj = project_name ();
return names {move (target)};
}
// Otherwise, get the project name and convert the target to unqualified.
//
- string proj (move (*target.proj));
+ project_name proj (move (*target.proj));
target.proj = nullopt;
scope& iroot (*ibase.root_scope ());
@@ -1137,7 +1122,7 @@ namespace build2
for (;;) // Break-out loop.
{
- string n ("config.import." + import_name (proj));
+ string n ("config.import." + proj.variable ());
// config.import.<proj>
//
@@ -1230,7 +1215,7 @@ namespace build2
// First check the amalgamation itself.
//
- if (r != &iroot && cast<string> (r->vars[var_project]) == proj)
+ if (r != &iroot && cast<project_name> (r->vars[var_project]) == proj)
{
out_root = r->out_path ();
break;
@@ -1345,7 +1330,7 @@ namespace build2
// Now we know this project's name as well as all its subprojects.
//
- if (cast<string> (root->vars[var_project]) == proj)
+ if (cast<project_name> (root->vars[var_project]) == proj)
break;
if (auto l = root->vars[var_subprojects])
@@ -1422,7 +1407,7 @@ namespace build2
tracer trace ("import");
assert (pk.proj);
- const string& proj (*pk.proj);
+ const project_name& proj (*pk.proj);
// Target type-specific search.
//
@@ -1492,7 +1477,7 @@ namespace build2
dr << info << "consider adding its installation location" <<
info << "or explicitly specify its project name";
else
- dr << info << "use config.import." << import_name (proj)
+ dr << info << "use config.import." << proj.variable ()
<< " command line variable to specifying its project out_root";
dr << endf;
diff --git a/build2/file.hxx b/build2/file.hxx
index bb136cf..9f93365 100644
--- a/build2/file.hxx
+++ b/build2/file.hxx
@@ -19,7 +19,7 @@ namespace build2
class location;
class prerequisite_key;
- using subprojects = std::map<string, dir_path>;
+ using subprojects = std::map<project_name, dir_path>;
ostream&
operator<< (ostream&, const subprojects&); // Print as name@dir sequence.
diff --git a/build2/function.cxx b/build2/function.cxx
index f1e0fdd..44bda57 100644
--- a/build2/function.cxx
+++ b/build2/function.cxx
@@ -375,6 +375,7 @@ namespace build2
void regex_functions (); // functions-regex.cxx
void string_functions (); // functions-string.cxx
void target_triplet_functions (); // functions-target-triplet.cxx
+ void project_name_functions (); // functions-target-triplet.cxx
struct functions_init
{
@@ -389,6 +390,7 @@ namespace build2
regex_functions ();
string_functions ();
target_triplet_functions ();
+ project_name_functions ();
}
};
diff --git a/build2/functions-project-name.cxx b/build2/functions-project-name.cxx
new file mode 100644
index 0000000..f77b9cd
--- /dev/null
+++ b/build2/functions-project-name.cxx
@@ -0,0 +1,63 @@
+// file : build2/functions-project-name.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/function.hxx>
+#include <build2/variable.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ void
+ project_name_functions ()
+ {
+ function_family f ("project_name");
+
+ f["string"] = [](project_name p) {return move (p).string ();};
+
+ f["base"] = [](project_name p, optional<string> ext)
+ {
+ return ext ? p.base (ext->c_str ()) : p.base ();
+ };
+
+ f["base"] = [](project_name p, names ext)
+ {
+ return p.base (convert<string> (move (ext)).c_str ());
+ };
+
+ f["extension"] = &project_name::extension;
+ f["variable"] = &project_name::variable;
+
+ // Project name-specific overloads from builtins.
+ //
+ function_family b ("builtin");
+
+ b[".concat"] = [](project_name n, string s)
+ {
+ string r (move (n).string ());
+ r += s;
+ return r;
+ };
+
+ b[".concat"] = [](string s, project_name n)
+ {
+ s += n.string ();
+ return s;
+ };
+
+ b[".concat"] = [](project_name n, names ns)
+ {
+ string r (move (n).string ());
+ r += convert<string> (move (ns));
+ return r;
+ };
+
+ b[".concat"] = [](names ns, project_name n)
+ {
+ string r (convert<string> (move (ns)));
+ r += n.string ();
+ return r;
+ };
+ }
+}
diff --git a/build2/install/init.cxx b/build2/install/init.cxx
index 682f6b0..5c28cfa 100644
--- a/build2/install/init.cxx
+++ b/build2/install/init.cxx
@@ -250,7 +250,7 @@ namespace build2
if (s)
config::save_module (rs, "install", INT32_MAX);
- const string& n (project (rs));
+ const string& n (project (rs).string ());
// Global config.install.* values.
//
diff --git a/build2/name.cxx b/build2/name.cxx
index 9fc661b..d1f9ce6 100644
--- a/build2/name.cxx
+++ b/build2/name.cxx
@@ -25,7 +25,7 @@ namespace build2
if (n.proj)
{
- r += *n.proj;
+ r += n.proj->string ();
r += '%';
}
@@ -119,7 +119,7 @@ namespace build2
if (n.proj)
{
- write_string (*n.proj);
+ write_string (n.proj->string ());
os << '%';
}
diff --git a/build2/name.hxx b/build2/name.hxx
index 54fd130..e688aed 100644
--- a/build2/name.hxx
+++ b/build2/name.hxx
@@ -33,7 +33,7 @@ namespace build2
//
struct name
{
- optional<string> proj;
+ optional<project_name> proj;
dir_path dir;
string type;
string value;
@@ -48,7 +48,7 @@ namespace build2
name (dir_path d, string t, string v)
: dir (move (d)), type (move (t)), value (move (v)) {}
- name (optional<string> p, dir_path d, string t, string v)
+ name (optional<project_name> p, dir_path d, string t, string v)
: proj (move (p)), dir (move (d)), type (move (t)), value (move (v)) {}
bool
diff --git a/build2/operation.cxx b/build2/operation.cxx
index 37e7c91..c42f92a 100644
--- a/build2/operation.cxx
+++ b/build2/operation.cxx
@@ -493,7 +493,7 @@ namespace build2
// This could be a simple project that doesn't set project name.
//
cout
- << "project: " << cast_empty<string> (s[var_project]) << endl
+ << "project: " << cast_empty<project_name> (s[var_project]) << endl
<< "version: " << cast_empty<string> (s[var_version]) << endl
<< "summary: " << cast_empty<string> (s[var_project_summary]) << endl
<< "url: " << cast_empty<string> (s[var_project_url]) << endl
diff --git a/build2/parser.cxx b/build2/parser.cxx
index 4fe30e9..57c7fc9 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -2150,6 +2150,7 @@ namespace build2
n == "name" ? ptr (value_traits<name>::value_type) :
n == "name_pair" ? ptr (value_traits<name_pair>::value_type) :
n == "target_triplet" ? ptr (value_traits<target_triplet>::value_type) :
+ n == "project_name" ? ptr (value_traits<project_name>::value_type) :
n == "uint64s" ? ptr (value_traits<uint64s>::value_type) :
n == "strings" ? ptr (value_traits<strings>::value_type) :
@@ -2799,7 +2800,7 @@ namespace build2
names& ns,
const char* what,
size_t pairn,
- const optional<string>& pp,
+ const optional<project_name>& pp,
const dir_path* dp,
const string* tp)
{
@@ -2827,7 +2828,7 @@ namespace build2
// Project.
//
- optional<string> p;
+ optional<project_name> p;
if (cn.proj)
{
if (pp)
@@ -3186,7 +3187,7 @@ namespace build2
const char* what,
const string* separators,
size_t pairn,
- const optional<string>& pp,
+ const optional<project_name>& pp,
const dir_path* dp,
const string* tp,
bool cross)
@@ -3209,7 +3210,7 @@ namespace build2
// Parse names until closing '}' expanding patterns.
//
auto parse = [&r, &t, &tt, pmode, what, separators, this] (
- const optional<string>& pp,
+ const optional<project_name>& pp,
const dir_path* dp,
const string* tp)
{
@@ -3350,7 +3351,7 @@ namespace build2
const char* what,
const string* separators,
size_t pairn,
- const optional<string>& pp,
+ const optional<project_name>& pp,
const dir_path* dp,
const string* tp,
bool cross) -> parse_names_result
@@ -3708,8 +3709,8 @@ namespace build2
// First take care of project. A project-qualified name is not very
// common, so we can afford some copying for the sake of simplicity.
//
- optional<string> p1;
- const optional<string>* pp1 (&pp);
+ optional<project_name> p1;
+ const optional<project_name>* pp1 (&pp);
if (p != string::npos)
{
@@ -3733,7 +3734,17 @@ namespace build2
if (pp)
fail (t) << "nested project name " << proj;
- p1 = move (proj);
+ try
+ {
+ p1 = !proj.empty ()
+ ? project_name (move (proj))
+ : project_name ();
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (t) << "invalid project name '" << proj << "': " << e;
+ }
+
pp1 = &p1;
}
}
diff --git a/build2/parser.hxx b/build2/parser.hxx
index edb2f84..acd0845 100644
--- a/build2/parser.hxx
+++ b/build2/parser.hxx
@@ -307,7 +307,7 @@ namespace build2
const char* what = "name",
const string* separators = &name_separators,
size_t pairn = 0,
- const optional<string>& prj = nullopt,
+ const optional<project_name>& prj = nullopt,
const dir_path* dir = nullptr,
const string* type = nullptr,
bool cross = true);
@@ -319,7 +319,7 @@ namespace build2
const char* what,
const string* separators,
size_t pairn,
- const optional<string>& prj,
+ const optional<project_name>& prj,
const dir_path* dir,
const string* type,
bool cross);
@@ -341,7 +341,7 @@ namespace build2
names&,
const char* what,
size_t pairn,
- const optional<string>& prj,
+ const optional<project_name>& prj,
const dir_path* dir,
const string* type);
diff --git a/build2/prerequisite.cxx b/build2/prerequisite.cxx
index 5900589..5837328 100644
--- a/build2/prerequisite.cxx
+++ b/build2/prerequisite.cxx
@@ -15,8 +15,6 @@ namespace build2
{
// prerequisite_key
//
- const optional<string> prerequisite_key::nullproj = nullopt;
-
ostream&
operator<< (ostream& os, const prerequisite_key& pk)
{
diff --git a/build2/prerequisite.hxx b/build2/prerequisite.hxx
index b8707ce..3d8121a 100644
--- a/build2/prerequisite.hxx
+++ b/build2/prerequisite.hxx
@@ -31,12 +31,10 @@ namespace build2
public:
typedef build2::scope scope_type;
- const optional<string>& proj;
+ const optional<project_name>& proj;
target_key tk; // The .dir and .out members can be relative.
const scope_type* scope; // Can be NULL if tk.dir is absolute.
- static const optional<string> nullproj;
-
template <typename T>
bool is_a () const {return tk.is_a<T> ();}
bool is_a (const target_type& tt) const {return tk.is_a (tt);}
@@ -65,7 +63,7 @@ namespace build2
// bar/ here is relative to the scope, not to foo/. Plus, bar/ can resolve
// to either src or out.
//
- const optional<string> proj;
+ const optional<project_name> proj;
const target_type_type& type;
const dir_path dir; // Normalized absolute or relative (to scope).
const dir_path out; // Empty, normalized absolute, or relative.
@@ -102,7 +100,7 @@ namespace build2
append (const variable&, const target_type&);
public:
- prerequisite (optional<string> p,
+ prerequisite (optional<project_name> p,
const target_type_type& t,
dir_path d,
dir_path o,
diff --git a/build2/target.hxx b/build2/target.hxx
index 0f8c8ea..db5de67 100644
--- a/build2/target.hxx
+++ b/build2/target.hxx
@@ -882,14 +882,12 @@ namespace build2
return member != nullptr ? member->dir : prerequisite.dir;
}
- const optional<string>&
+ const optional<project_name>&
proj () const
{
// Member cannot be project-qualified.
//
- return member != nullptr
- ? prerequisite_key::nullproj
- : prerequisite.proj;
+ return member != nullptr ? nullopt_project_name : prerequisite.proj;
}
const scope_type&
diff --git a/build2/types.hxx b/build2/types.hxx
index 8764c35..94604e0 100644
--- a/build2/types.hxx
+++ b/build2/types.hxx
@@ -42,6 +42,7 @@
#include <libbutl/timestamp.mxx>
#include <libbutl/vector-view.mxx>
#include <libbutl/small-vector.mxx>
+#include <libbutl/project-name.mxx>
#include <libbutl/target-triplet.mxx>
#include <libbutl/standard-version.mxx>
@@ -273,6 +274,10 @@ namespace build2
using butl::standard_version;
using butl::standard_version_constraint;
+ // <libbutl/project-name.mxx>
+ //
+ using butl::project_name;
+
// Diagnostics location.
//
class location
diff --git a/build2/utility.cxx b/build2/utility.cxx
index 07cf88e..2d8e59b 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -254,13 +254,15 @@ namespace build2
fail (loc) << "unable to execute " << args[0] << ": " << e << endf;
}
- const string empty_string;
- const path empty_path;
- const dir_path empty_dir_path;
-
- const optional<string> nullopt_string;
- const optional<path> nullopt_path;
- const optional<dir_path> nullopt_dir_path;
+ const string empty_string;
+ const path empty_path;
+ const dir_path empty_dir_path;
+ const project_name empty_project_name;
+
+ const optional<string> nullopt_string;
+ const optional<path> nullopt_path;
+ const optional<dir_path> nullopt_dir_path;
+ const optional<project_name> nullopt_project_name;
void
append_options (cstrings& args, const lookup& l, const char* e)
diff --git a/build2/utility.hxx b/build2/utility.hxx
index 6e76f05..0beec2e 100644
--- a/build2/utility.hxx
+++ b/build2/utility.hxx
@@ -388,15 +388,17 @@ namespace build2
verbosity, pp, args, forward<F> (f), error, ignore_exit, checksum);
}
- // Empty/nullopt string and path.
+ // Empty/nullopt string, path, and project name.
//
- extern const string empty_string;
- extern const path empty_path;
- extern const dir_path empty_dir_path;
+ extern const string empty_string;
+ extern const path empty_path;
+ extern const dir_path empty_dir_path;
+ extern const project_name empty_project_name;
- extern const optional<string> nullopt_string;
- extern const optional<path> nullopt_path;
- extern const optional<dir_path> nullopt_dir_path;
+ extern const optional<string> nullopt_string;
+ extern const optional<path> nullopt_path;
+ extern const optional<dir_path> nullopt_dir_path;
+ extern const optional<project_name> nullopt_project_name;
// Hash a path potentially without the specific directory prefix.
//
diff --git a/build2/variable.cxx b/build2/variable.cxx
index 406123d..c304d0c 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -549,7 +549,7 @@ namespace build2
//
if (n.qualified ())
{
- string p (*n.proj);
+ string p (move (*n.proj).string ());
p += '%';
p += s;
p.swap (s);
@@ -563,7 +563,7 @@ namespace build2
if (r->qualified ())
{
- s += *r->proj;
+ s += r->proj->string ();
s += '%';
}
@@ -1080,6 +1080,55 @@ namespace build2
&default_empty<target_triplet>
};
+ // project_name value
+ //
+ project_name value_traits<project_name>::
+ convert (name&& n, name* r)
+ {
+ if (r == nullptr)
+ {
+ if (n.simple ())
+ {
+ try
+ {
+ return n.empty () ? project_name () : project_name (move (n.value));
+ }
+ catch (const invalid_argument& e)
+ {
+ throw invalid_argument (
+ string ("invalid project_name value: ") + e.what ());
+ }
+ }
+
+ // Fall through.
+ }
+
+ throw_invalid_argument (n, r, "project_name");
+ }
+
+ const project_name&
+ value_traits<project_name>::empty_instance = empty_project_name;
+
+ const char* const value_traits<project_name>::type_name = "project_name";
+
+ const value_type value_traits<project_name>::value_type
+ {
+ type_name,
+ sizeof (project_name),
+ nullptr, // No base.
+ nullptr, // No element.
+ &default_dtor<project_name>,
+ &default_copy_ctor<project_name>,
+ &default_copy_assign<project_name>,
+ &simple_assign<project_name>,
+ nullptr, // Append not supported.
+ nullptr, // Prepend not supported.
+ &simple_reverse<project_name>,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<project_name>,
+ &default_empty<project_name>
+ };
+
// variable_pool
//
void variable_pool::
diff --git a/build2/variable.hxx b/build2/variable.hxx
index f9b8b3c..401cc77 100644
--- a/build2/variable.hxx
+++ b/build2/variable.hxx
@@ -820,7 +820,6 @@ namespace build2
static const build2::value_type value_type;
};
-
// target_triplet
//
template <>
@@ -841,6 +840,27 @@ namespace build2
static const build2::value_type value_type;
};
+ // project_name
+ //
+ template <>
+ struct value_traits<project_name>
+ {
+ static_assert (sizeof (project_name) <= value::size_,
+ "insufficient space");
+
+ static project_name convert (name&&, name*);
+ static void assign (value&, project_name&&);
+ static name reverse (const project_name& x) {return name (x.string ());}
+ static int compare (const project_name& x, const project_name& y) {
+ return x.compare (y);}
+ static bool empty (const project_name& x) {return x.empty ();}
+
+ static const bool empty_value = true;
+ static const project_name& empty_instance;
+ static const char* const type_name;
+ static const build2::value_type value_type;
+ };
+
// vector<T>
//
template <typename T>
diff --git a/build2/variable.ixx b/build2/variable.ixx
index 991556a..8c16559 100644
--- a/build2/variable.ixx
+++ b/build2/variable.ixx
@@ -611,6 +611,17 @@ namespace build2
new (&v.data_) target_triplet (move (x));
}
+ // project_name value
+ //
+ inline void value_traits<project_name>::
+ assign (value& v, project_name&& x)
+ {
+ if (v)
+ v.as<project_name> () = move (x);
+ else
+ new (&v.data_) project_name (move (x));
+ }
+
// vector<T> value
//
template <typename T>
diff --git a/build2/version/init.cxx b/build2/version/init.cxx
index 397d7da..4234451 100644
--- a/build2/version/init.cxx
+++ b/build2/version/init.cxx
@@ -138,10 +138,18 @@ namespace build2
catch (const invalid_argument& e)
{
fail (l) << "invalid version constraint for dependency "
- << b << ": " << e;
+ << d << ": " << e;
}
- ds.emplace (move (n), move (c));
+ try
+ {
+ ds.emplace (project_name (move (n)).variable (), move (c));
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (l) << "invalid package name for dependency "
+ << d << ": " << e;
+ }
}
}
}
@@ -232,7 +240,7 @@ namespace build2
// Create the module.
//
- mod.reset (new module (cast<string> (rs.vars[var_project]),
+ mod.reset (new module (cast<project_name> (rs.vars[var_project]),
move (v),
committed,
rewritten,
@@ -284,7 +292,7 @@ namespace build2
if (!val)
{
- string p (cast<string> (rs.vars[var_project]));
+ string p (cast<project_name> (rs.vars[var_project]).string ());
p += '-';
p += v.string ();
val = move (p);
diff --git a/build2/version/module.hxx b/build2/version/module.hxx
index 56536c3..36dd735 100644
--- a/build2/version/module.hxx
+++ b/build2/version/module.hxx
@@ -16,7 +16,8 @@ namespace build2
{
namespace version
{
- // The 'depends' values from manifest.
+ // The 'depends' values from manifest. Note that the package names are
+ // sanitized for use in variable names.
//
using dependency_constraints = std::map<string, string>;
@@ -24,7 +25,9 @@ namespace build2
{
static const string name;
- const string& project; // The project variable value.
+ // The project variable value sanitized for use in variable names.
+ //
+ const string project;
butl::standard_version version;
bool committed; // Whether this is a committed snapshot.
@@ -34,12 +37,12 @@ namespace build2
bool dist_uncommitted = false;
- module (const string& p,
+ module (const project_name& p,
butl::standard_version v,
bool c,
bool r,
dependency_constraints d)
- : project (p),
+ : project (p.variable ()),
version (move (v)),
committed (c),
rewritten (r),
diff --git a/tests/name/pattern.test b/tests/name/pattern.test
index 792a7c3..fad8478 100644
--- a/tests/name/pattern.test
+++ b/tests/name/pattern.test
@@ -9,8 +9,8 @@ define txt: file
txt{*}: extension = txt
EOI
-$* <'print p%*.txt' >'p%*.txt' : project-simple
-$* <'print p%{*.txt -x*}' >'p%*.txt p%-x*' : project-group
+$* <'print pp%*.txt' >'pp%*.txt' : project-simple
+$* <'print pp%{*.txt -x*}' >'pp%*.txt pp%-x*' : project-group
$* <"print '*.txt'" >'*.txt' : quoted-single
$* <'print "*.txt"' >'*.txt' : quoted-double
diff --git a/tests/value/reverse.test b/tests/value/reverse.test
index 5208536..9ea9d09 100644
--- a/tests/value/reverse.test
+++ b/tests/value/reverse.test
@@ -42,9 +42,9 @@
: combined
:
$* <<EOI >>EOO
- print ([strings] x%foo@y%bar x%foo/@y%bar/)
+ print ([strings] xx%foo@yy%bar xx%foo/@yy%bar/)
EOI
- x%foo@y%bar x%foo/@y%bar/
+ xx%foo@yy%bar xx%foo/@yy%bar/
EOO
}