diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2017-03-17 11:09:36 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2017-03-17 11:09:36 +0200 |
commit | 120722059c8612fb13d7bf585a5593582a49220d (patch) | |
tree | 163286ebf4ac50515df8542a34652ef185910184 | |
parent | 83f8b6a45fc041586819537ca86be2eb534f79b0 (diff) |
Implement alternative command line buildspec and variable assignment syntax
b test: foo/ bar/
b config.import.libhello = ../libhello/
-rw-r--r-- | build2/b.cli | 29 | ||||
-rw-r--r-- | build2/b.cxx | 62 |
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) |