aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-03-21 06:33:33 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-03-21 06:33:33 +0200
commit8ac8605c903e769d373a87d3acf167acc9a08a56 (patch)
tree1f5b4e5fe5e7d8adcb821bff69f582aecbea7fe3
parent7c250c8f3855b33c20ac84cd3210cb7f4a30f848 (diff)
Handle auxiliary environment in bbot-worker
-rw-r--r--bbot/worker/worker.cxx212
-rw-r--r--doc/manual.cli6
2 files changed, 189 insertions, 29 deletions
diff --git a/bbot/worker/worker.cxx b/bbot/worker/worker.cxx
index 8217c45..8388d03 100644
--- a/bbot/worker/worker.cxx
+++ b/bbot/worker/worker.cxx
@@ -1219,6 +1219,9 @@ upload_manifest (tracer& trace,
}
}
+static strings
+parse_auxiliary_environment (const string&, const char*); // See below.
+
static const string worker_checksum ("5"); // Logic version.
static int bbot::
@@ -2356,6 +2359,26 @@ build (size_t argc, const char* argv[])
operation_result* pr (&add_result ("configure"));
operation_result& r (*pr); // @@ TMP: Apple Clang 14.0.3 ICE
+ // If we have auxiliary environment, show it in the logs.
+ //
+ if (tm.auxiliary_environment)
+ {
+ strings es (parse_auxiliary_environment (*tm.auxiliary_environment,
+ comment_begin));
+ if (!es.empty ())
+ {
+ for (const string& e: es)
+ {
+ r.log += e;
+ r.log += '\n';
+ }
+
+ // Add a trailing blank line to separate this from the rest.
+ //
+ r.log += '\n';
+ }
+ }
+
// Noop, just for the log record.
//
change_wd (trace, &r.log, rwd);
@@ -5942,6 +5965,104 @@ build (size_t argc, const char* argv[])
return 3;
}
+// Parse the task_manifest::auxiliary_environment value into the list of
+// environment variable assignments as expected by the process API. Throw
+// invalid_argument if the auxiliary environment is invalid.
+//
+// If comment is not NULL, then add blank and comment lines prefixed with this
+// string (which is normally either '#' or 'rem'). This mode is used to print
+// the environment into the build log.
+//
+static strings
+parse_auxiliary_environment (const string& s, const char* comment = nullptr)
+{
+ strings r;
+
+ // Note: parse observing blanks.
+ //
+ for (size_t b (0), e (0), m (0), n (s.size ());
+ next_word (s, n, b, e, m, '\n', '\r'), b != n; )
+ {
+ string line (s, b, e - b);
+
+ if (trim (line).empty ()) // Blank.
+ {
+ if (comment != nullptr)
+ r.push_back (comment);
+
+ continue;
+ }
+
+ if (line.front () == '#') // Comment.
+ {
+ if (comment != nullptr)
+ {
+ line.erase (0, 1);
+ line.insert (0, comment);
+ r.push_back (move (line));
+ }
+
+ continue;
+ }
+
+ size_t p (line.find ('='));
+
+ if (p == string::npos)
+ throw invalid_argument ("missing '=' in '" + line + '\'');
+
+ string name (line, 0, p);
+
+ if (trim_right (name).empty () ||
+ name.find_first_not_of (
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") != string::npos)
+ {
+ throw invalid_argument ("invalid variable name '" + name + '\'');
+ }
+
+ // Disallow certain well-known environment variables.
+ //
+ if (name == "PATH"
+#if defined(_WIN32)
+#elif defined(__APPLE__)
+ || name == "DYLD_LIBRARY_PATH"
+#else // Linux, FreeBSD, NetBSD, OpenBSD
+ || name == "LD_LIBRARY_PATH"
+#endif
+ )
+ {
+ throw invalid_argument ("disallowed variable name '" + name + '\'');
+ }
+
+ line.erase (0, p + 1); // Value.
+
+ // Note: we allow empty values.
+ //
+ if (!trim_left (line).empty ())
+ {
+ // Unquote.
+ //
+ char c (line.front ());
+ if (c == '"' || c == '\'')
+ {
+ if (line.size () == 1 || line.back () != c)
+ throw invalid_argument ("invalid quoted value '" + line + '\'');
+
+ line.pop_back ();
+ line.erase (0, 1);
+ }
+ }
+
+ // Reassemble.
+ //
+ line.insert (0, 1, '=');
+ line.insert (0, name);
+
+ r.push_back (move (line));
+ }
+
+ return r;
+}
+
static int
startup ()
{
@@ -5951,11 +6072,13 @@ startup ()
//
// 1. Download the task manifest into the build directory (CWD).
//
- // 2. Parse it and get the target.
+ // 2. Parse it and get the target, environment name, and auxiliary
+ // environment.
//
- // 3. Find the environment setup executable for this target.
+ // 3. Find the environment setup executable for this name.
//
- // 4. Execute the environment setup executable.
+ // 4. Execute the environment setup executable for this target in the
+ // auxiliary environment.
//
// 5. If the environment setup executable fails, then upload the (failed)
// result ourselves.
@@ -5970,6 +6093,33 @@ startup ()
//
task_manifest tm;
+ auto upload_result = [&trace, &tm] (result_status rs,
+ operation_results&& ors)
+ {
+ const string url ("tftp://" + ops.tftp_host () + "/result.manifest.lz4");
+
+ // If we failed before being able to parse the task manifest, use the
+ // "unknown" values for the package name and version.
+ //
+ result_manifest rm {
+ tm.name.empty () ? bpkg::package_name ("unknown") : tm.name,
+ tm.version.empty () ? bpkg::version ("0") : tm.version,
+ rs,
+ move (ors),
+ worker_checksum,
+ nullopt /* dependency_checksum */
+ };
+
+ try
+ {
+ upload_manifest (trace, url, rm, "result");
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to upload result manifest to " << url << ": " << e;
+ }
+ };
+
try
{
// Download the task.
@@ -6051,6 +6201,31 @@ startup ()
fail << "no default environment setup executable in " << env_dir;
}
+ // Auxiliary environment.
+ //
+ strings aux_env;
+ if (tm.auxiliary_environment)
+ {
+ try
+ {
+ aux_env = parse_auxiliary_environment (*tm.auxiliary_environment);
+ }
+ catch (const invalid_argument& e)
+ {
+ // Note: include (unparsed) environment into the log so that we can
+ // see what we are dealing with.
+ //
+ operation_result r {
+ "configure",
+ result_status::abort,
+ *tm.auxiliary_environment + "\n" +
+ "error: invalid auxiliary environment: " + e.what () + '\n'};
+
+ upload_result (result_status::abort, {move (r)});
+ return 1;
+ }
+ }
+
// Run it.
//
strings os;
@@ -6088,7 +6263,12 @@ startup ()
// result manifest. There is no reason to retry (most likely there is
// nobody listening on the other end anymore).
//
- switch (run_io_exit (trace, 0, 2, 2, pp, tg, argv0.effect_string (), os))
+ switch (run_io_exit (trace,
+ 0 /* stdin */, 2 /* stdout */, 2 /* stderr */,
+ process_env (pp, aux_env),
+ tg,
+ argv0.effect_string (),
+ os))
{
case 3:
case 2: return 1;
@@ -6098,29 +6278,7 @@ startup ()
}
catch (const failed&)
{
- const string url ("tftp://" + ops.tftp_host () + "/result.manifest.lz4");
-
- // If we failed before being able to parse the task manifest, use the
- // "unknown" values for the package name and version.
- //
- result_manifest rm {
- tm.name.empty () ? bpkg::package_name ("unknown") : tm.name,
- tm.version.empty () ? bpkg::version ("0") : tm.version,
- result_status::abnormal,
- operation_results {},
- worker_checksum,
- nullopt /* dependency_checksum */
- };
-
- try
- {
- upload_manifest (trace, url, rm, "result");
- }
- catch (const io_error& e)
- {
- fail << "unable to upload result manifest to " << url << ": " << e;
- }
-
+ upload_result (result_status::abnormal, operation_results {});
return 1;
}
}
diff --git a/doc/manual.cli b/doc/manual.cli
index 0de6d0b..2fa3248 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -621,8 +621,10 @@ one per line, in the form:
\
Whitespaces before \c{<name>}, around \c{=}, and after \c{<value>} as well as
-blank lines and lines that start with \c{#} are ignored. The \c{<value>} part
-as a whole can be single ('\ ') or double (\"\ \") quoted. For example:
+blank lines and lines that start with \c{#} are ignored. The \c{<name>} part
+must only contain capital alphabetic, numeric, and \c{_} characters. The
+\c{<value>} part as a whole can be single ('\ ') or double (\"\ \")
+quoted. For example:
\
DATABASE_HOST=192.168.0.1