aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-03-17 11:09:36 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-03-17 11:09:36 +0200
commit120722059c8612fb13d7bf585a5593582a49220d (patch)
tree163286ebf4ac50515df8542a34652ef185910184
parent83f8b6a45fc041586819537ca86be2eb534f79b0 (diff)
Implement alternative command line buildspec and variable assignment syntax
b test: foo/ bar/ b config.import.libhello = ../libhello/
-rw-r--r--build2/b.cli29
-rw-r--r--build2/b.cxx62
2 files changed, 89 insertions, 2 deletions
diff --git a/build2/b.cli b/build2/b.cli
index 0d345bf..f1fd7dc 100644
--- a/build2/b.cli
+++ b/build2/b.cli
@@ -87,6 +87,26 @@ namespace build2
b '{clean disfigure}(...)' # similar to distclean
\
+ In POSIX shells parenthesis are special characters and must be quoted
+ when used in a buildspec. Besides being an inconvenience in itself,
+ quoting also inhibits path auto-completion. To help with this situation a
+ shortcut syntax is available for executing a single operation or
+ meta-operation, for example:
+
+ \
+ b clean: foo/ bar/ # clean(foo/ bar/)
+ b configure: src/@out/ # configure(src/@out/)
+ b create: conf/, cxx # create(conf/, cxx)
+ b configure: config.cxx=g++ src/ # configure(src/) config.cxx=g++
+ \
+
+ To activate the shortcut syntax the first buildspec argument must start
+ with an operation or meta-operation name and end with a colon (\cb{:}).
+ To transform the shortcut syntax to the normal buildspec syntax the colon
+ is replaced with the opening parenthesis (\cb{(}), the rest of the
+ buildspec arguments are treated as is, and the final closing parenthesis
+ (\cb{)}) is added.
+
For each <target> the driver expects to find \cb{buildfile} either in the
target's directory or, if the directory is part of the \cb{out} tree
(\cb{out_base}), in the corresponding \cb{src} directory (\cb{src_base}).
@@ -142,6 +162,15 @@ namespace build2
b config.cxx=clang++ config.cxx.coptions=-O3
\
+ Similar to buildspec, POSIX shells often inhibit path auto-completion on
+ the right hand side of a variable assignment. To help with this situation
+ the assignment can be broken down into three separate command line
+ arguments, for example:
+
+ \
+ b config.import.libhello = ../libhello/
+ \
+
The build system has the following built-in and pre-defined
meta-operations:
diff --git a/build2/b.cxx b/build2/b.cxx
index d2ad368..1f0464f 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -117,6 +117,9 @@ main (int argc, char* argv[])
{
cl::argv_scanner scan (argc, argv);
+ size_t argn (0); // Argument count.
+ bool shortcut (false); // True if the shortcut syntax is used.
+
for (bool opt (true), var (true); scan.more (); )
{
if (opt)
@@ -159,12 +162,43 @@ main (int argc, char* argv[])
continue;
}
- if (strchr (s, '=') != nullptr) // Covers =, +=, and =+.
+ if (const char* p = strchr (s, '=')) // Covers =, +=, and =+.
{
+ // Diagnose the empty variable name situation. Note that we don't
+ // allow "partially broken down" assignments (as in foo =bar)
+ // since foo= bar would be ambigous.
+ //
+ if (p == s || (p == s + 1 && *s == '+'))
+ fail << "missing variable name in '" << s << "'";
+
cmd_vars.push_back (s);
continue;
}
+ // Handle the "broken down" variable assignments (i.e., foo = bar
+ // instead of foo=bar).
+ //
+ if (scan.more ())
+ {
+ const char* a (scan.peek ());
+
+ if (strcmp (a, "=" ) == 0 ||
+ strcmp (a, "+=") == 0 ||
+ strcmp (a, "=+") == 0)
+ {
+ string v (s);
+ v += a;
+
+ scan.next ();
+
+ if (scan.more ())
+ v += scan.next ();
+
+ cmd_vars.push_back (move (v));
+ continue;
+ }
+ }
+
// Fall through.
}
@@ -174,10 +208,34 @@ main (int argc, char* argv[])
// diagnostics (i.e., we could have used <buildspec-1>, <buildspec-2>
// to give the idea about which argument is invalid).
//
- if (!args.empty ())
+ // Or we could separate arguments with newlines so that a line number
+ // signifies the argument number.
+ //
+ if (argn != 0)
args += ' ';
args += s;
+
+ // See if we are using the shortcut syntax.
+ //
+ if (argn == 0 && args.back () == ':')
+ {
+ args.back () = '(';
+ shortcut = true;
+ }
+
+ argn++;
+ }
+
+ // Add the closing parenthesis unless there wasn't anything in between
+ // in which case pop the opening one.
+ //
+ if (shortcut)
+ {
+ if (argn == 1)
+ args.pop_back ();
+ else
+ args += ')';
}
}
catch (const cl::exception& e)