aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/script/parser.cxx40
-rw-r--r--libbuild2/script/parser.hxx7
-rw-r--r--libbuild2/script/run.cxx46
-rw-r--r--libbuild2/script/script.cxx10
-rw-r--r--libbuild2/script/script.hxx1
5 files changed, 87 insertions, 17 deletions
diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx
index ebfd5fc..f234d58 100644
--- a/libbuild2/script/parser.cxx
+++ b/libbuild2/script/parser.cxx
@@ -1028,8 +1028,9 @@ namespace build2
if (prog && tt == type::word && t.value == "env")
{
parsed_env r (parse_env_builtin (t, tt));
+ c.cwd = move (r.cwd);
c.variables = move (r.variables);
- c.timeout = r.timeout;
+ c.timeout = r.timeout;
env = true;
}
@@ -1411,10 +1412,16 @@ namespace build2
return r;
};
+ auto bad = [&i, &o, this] (const string& v)
+ {
+ fail (i->second) << "env: invalid value '" << v << "' for option '"
+ << o << "'";
+ };
+
// As above but convert the option value to a number and fail on
// error.
//
- auto num = [&i, &o, &str, this] (const char* ln, const char* sn)
+ auto num = [&str, &bad] (const char* ln, const char* sn)
{
optional<uint64_t> r;
if (optional<string> s = str (ln, sn))
@@ -1422,8 +1429,29 @@ namespace build2
r = parse_number (*s);
if (!r)
- fail (i->second) << "env: invalid value '" << *s
- << "' for option '" << o << "'";
+ bad (*s);
+ }
+
+ return r;
+ };
+
+ // As above but convert the option value to a directory path and fail
+ // on error.
+ //
+ auto dir = [&str, &bad] (const char* ln, const char* sn)
+ {
+ optional<dir_path> r;
+ if (optional<string> s = str (ln, sn))
+ try
+ {
+ // Note that we don't need to check that the path is not empty,
+ // since str() fails for empty values.
+ //
+ r = dir_path (move (*s));
+ }
+ catch (const invalid_path& e)
+ {
+ bad (e.path);
}
return r;
@@ -1435,6 +1463,10 @@ namespace build2
{
r.timeout = chrono::seconds (*v);
}
+ else if (optional<dir_path> v = dir ("--cwd", "-c"))
+ {
+ r.cwd = move (*v);
+ }
else if (optional<string> v = str ("--unset", "-u"))
{
verify_environment_var_name (*v, "env: ", i->second, o.c_str ());
diff --git a/libbuild2/script/parser.hxx b/libbuild2/script/parser.hxx
index 9098b3c..2a10311 100644
--- a/libbuild2/script/parser.hxx
+++ b/libbuild2/script/parser.hxx
@@ -130,15 +130,16 @@ namespace build2
pre_parse_line_start (token&, token_type&, lexer_mode);
// Parse the env pseudo-builtin arguments up to the program name. Return
- // the program execution timeout, the list of the variables that should
- // be unset ("name") and/or set ("name=value") in the command
+ // the program execution timeout, CWD, the list of the variables that
+ // should be unset ("name") and/or set ("name=value") in the command
// environment, and the token/type that starts the program name. Note
// that the variable unsets come first, if present.
//
struct parsed_env
{
optional<duration> timeout;
- environment_vars variables;
+ optional<dir_path> cwd;
+ environment_vars variables;
};
parsed_env
diff --git a/libbuild2/script/run.cxx b/libbuild2/script/run.cxx
index 91fc9ac..f3b5cad 100644
--- a/libbuild2/script/run.cxx
+++ b/libbuild2/script/run.cxx
@@ -1227,6 +1227,8 @@ namespace build2
//
const command& c (*bc);
+ const dir_path& wdir (*env.work_dir.path);
+
// Register the command explicit cleanups. Verify that the path being
// cleaned up is a sub-path of the script working directory. Fail if
// this is not the case.
@@ -1234,7 +1236,7 @@ namespace build2
for (const auto& cl: c.cleanups)
{
const path& p (cl.path);
- path np (normalize (p, *env.work_dir.path, ll));
+ path np (normalize (p, wdir, ll));
const string& ls (np.leaf ().string ());
bool wc (ls == "*" || ls == "**" || ls == "***");
@@ -1320,6 +1322,10 @@ namespace build2
if (!first || !last)
fail (ll) << program << " builtin must be the only pipe command";
+ if (c.cwd)
+ fail (ll) << "current working directory cannot be specified for "
+ << program << " builtin";
+
if (!c.variables.empty ())
fail (ll) << "environment variables cannot be (un)set for "
<< program << " builtin";
@@ -1455,7 +1461,7 @@ namespace build2
}
case redirect_type::file:
{
- isp = normalize (in.file.path, *env.work_dir.path, ll);
+ isp = normalize (in.file.path, wdir, ll);
open_stdin ();
break;
@@ -1549,9 +1555,9 @@ namespace build2
// or null-device descriptor for merge, pass or null redirects
// respectively (not opening any file).
//
- auto open = [&env, &ll, &std_path] (const redirect& r,
- int dfd,
- path& p) -> auto_fd
+ auto open = [&env, &wdir, &ll, &std_path] (const redirect& r,
+ int dfd,
+ path& p) -> auto_fd
{
assert (dfd == 1 || dfd == 2);
const char* what (dfd == 1 ? "stdout" : "stderr");
@@ -1592,7 +1598,7 @@ namespace build2
//
p = r.file.mode == redirect_fmode::compare
? std_path (what)
- : normalize (r.file.path, *env.work_dir.path, ll);
+ : normalize (r.file.path, wdir, ll);
m |= r.file.mode == redirect_fmode::append
? fdopen_mode::at_end
@@ -1810,6 +1816,28 @@ namespace build2
}
};
+ // Derive the process/builtin CWD.
+ //
+ // If the process/builtin CWD is specified via the env pseudo-builtin,
+ // then use that, completing it relative to the script environment work
+ // directory, if it is relative. Otherwise, use the script environment
+ // work directory.
+ //
+ dir_path completed_cwd;
+ if (c.cwd && c.cwd->relative ())
+ completed_cwd = wdir / *c.cwd;
+
+ const dir_path& cwd (!completed_cwd.empty () ? completed_cwd :
+ c.cwd ? *c.cwd :
+ wdir);
+
+ // Unless CWD is the script environment work directory (which always
+ // exists), verify that it exists and fail if it doesn't.
+ //
+ if (&cwd != &wdir && !exists (cwd))
+ fail (ll) << "specified working directory " << cwd
+ << " does not exist";
+
// Absent if the process/builtin misses the "unsuccessful" deadline.
//
optional<process_exit> exit;
@@ -2069,7 +2097,7 @@ namespace build2
builtin b (bi->function (r,
c.arguments,
move (ifd), move (ofd.out), move (efd),
- *env.work_dir.path,
+ cwd,
bcs));
pipe_command pc (b, c, ll, prev_cmd);
@@ -2159,7 +2187,7 @@ namespace build2
program (path (s, 1, s.size () - 1));
}
else
- program (*env.work_dir.path / p);
+ program (wdir / p);
}
}
catch (const invalid_path& e)
@@ -2189,7 +2217,7 @@ namespace build2
*pe.path,
args.data (),
{ifd.get (), -1}, process::pipe (ofd), {-1, efd.get ()},
- env.work_dir.path->string ().c_str (),
+ cwd.string ().c_str (),
pe.vars);
// Can't throw.
diff --git a/libbuild2/script/script.cxx b/libbuild2/script/script.cxx
index 298d71f..9e6eeed 100644
--- a/libbuild2/script/script.cxx
+++ b/libbuild2/script/script.cxx
@@ -411,7 +411,7 @@ namespace build2
{
// Print the env builtin if any of its options/arguments are present.
//
- if (!c.variables.empty () || c.timeout)
+ if (c.timeout || c.cwd || !c.variables.empty ())
{
o << "env";
@@ -421,6 +421,14 @@ namespace build2
o << " -t "
<< chrono::duration_cast<chrono::seconds> (*c.timeout).count ();
+ // CWD.
+ //
+ if (c.cwd)
+ {
+ o << " -c ";
+ print_path (*c.cwd);
+ }
+
// Variable unsets/sets.
//
auto b (c.variables.begin ()), i (b), e (c.variables.end ());
diff --git a/libbuild2/script/script.hxx b/libbuild2/script/script.hxx
index dd31e33..d162900 100644
--- a/libbuild2/script/script.hxx
+++ b/libbuild2/script/script.hxx
@@ -324,6 +324,7 @@ namespace build2
process_path program;
strings arguments;
+ optional<dir_path> cwd; // From env builtin.
environment_vars variables; // From env builtin.
optional<duration> timeout; // From env builtin.