From c326426d980378c9c1d6fd2be98a7ee55c2fd3f6 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 20 Jul 2017 01:47:01 +0300 Subject: Add support for install, test installed and uninstall operations --- bbot/worker/worker.cxx | 260 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 234 insertions(+), 26 deletions(-) (limited to 'bbot/worker/worker.cxx') diff --git a/bbot/worker/worker.cxx b/bbot/worker/worker.cxx index 67bbebe..2107dc3 100644 --- a/bbot/worker/worker.cxx +++ b/bbot/worker/worker.cxx @@ -9,6 +9,7 @@ #endif #include +#include // strchr() #include #include @@ -59,15 +60,17 @@ catch (const system_error& e) using regexes = vector; -// Match lines read from the command's stderr against the regular expressions -// and return the warning result status (instead of success) in case of a -// match. +// Run a named command. Name is used for logging and diagnostics only. Match +// lines read from the command's stderr against the regular expressions and +// return the warning result status (instead of success) in case of a match. // template static result_status -run_bpkg (tracer& t, - string& log, const regexes& warn_detect, - const string& cmd, A&&... a) +run_cmd (tracer& t, + string& log, const regexes& warn_detect, + const string& name, + const process_env& pe, + A&&... a) { try { @@ -90,9 +93,7 @@ run_bpkg (tracer& t, fdnull (), // Never reads from stdout. 2, // 1>&2 pipe.out, - "bpkg", - "-v", - cmd, + pe, forward (a)...)); pipe.out.close (); @@ -136,7 +137,7 @@ run_bpkg (tracer& t, if (pr.wait ()) return r; - log += "bpkg " + cmd; + log += name; const process_exit& e (*pr.exit); if (e.normal ()) @@ -153,14 +154,50 @@ run_bpkg (tracer& t, } catch (const process_error& e) { - fail << "unable to execute bpkg " << cmd << ": " << e << endf; + fail << "unable to execute " << name << ": " << e << endf; } catch (const io_error& e) { - fail << "unable to read bpkg " << cmd << " diagnostics: " << e << endf; + fail << "unable to read " << name << " diagnostics: " << e << endf; } } +template +static result_status +run_bpkg (tracer& t, + string& log, const regexes& warn_detect, + const string& cmd, A&&... a) +{ + return run_cmd (t, + log, warn_detect, + "bpkg " + cmd, + "bpkg", "-v", cmd, forward (a)...); +} + +template +static result_status +run_b (tracer& t, + string& log, const regexes& warn_detect, + const V& envvars, + const string& buildspec, A&&... a) +{ + return run_cmd (t, + log, warn_detect, + "b " + buildspec, + process_env ("b", envvars), + "-v", forward (a)..., buildspec); +} + +template +static result_status +run_b (tracer& t, + string& log, const regexes& warn_detect, + const string& buildspec, A&&... a) +{ + const char* const* envvars (nullptr); + return run_b (t, log, warn_detect, envvars, buildspec, forward (a)...); +} + static void build (size_t argc, const char* argv[]) { @@ -171,8 +208,8 @@ build (size_t argc, const char* argv[]) // 1. Parse the task manifest (it should be in CWD). // // 2. Run bpkg to create the configuration, add the repository, and - // configure, build, and test the package all while saving the logs in - // the result manifest. + // configure, build, test, install and uninstall the package all while + // saving the logs in the result manifest. // // 3. Upload the result manifest. // @@ -198,7 +235,7 @@ build (size_t argc, const char* argv[]) return rm.results.back (); }; - dir_path owd; + dir_path rwd; // Root working directory. for (;;) // The "breakout" loop. { @@ -219,29 +256,29 @@ build (size_t argc, const char* argv[]) for (const auto& re: tm.unquoted_warning_regex ()) wre.emplace_back (re, f); + strings config (tm.unquoted_config ()); + const vector_view env (argv + 1, argc - 1); + // Configure. // + // Configuration directory name. + // + dir_path build_dir ("build"); { operation_result& r (add_result ("configure")); // bpkg create // - const vector_view env (argv + 1, argc - 1); - - // Configuration directory name. - // - dir_path dir ("build"); - r.status |= run_bpkg (trace, r.log, wre, "create", - "-d", dir.string (), + "-d", build_dir.string (), "--wipe", - tm.unquoted_config (), + config, env); if (!r.status) break; - owd = change_wd (dir); + rwd = change_wd (build_dir); // bpkg add // @@ -315,13 +352,184 @@ build (size_t argc, const char* argv[]) rm.status |= r.status; } + // Install the package, optionally test the installation and uninstall + // afterwards. + // + // These operations are triggered by presence of config.install.root + // configuration variable having a non-empty value. Passing [null] value + // would be meaningless, so we don't recognize it as a special one. + // + dir_path install_root; + { + size_t n (19); + auto space = [] (char c) {return c == ' ' || c == '\t';}; + + for (const auto& s: reverse_iterate (config)) + { + if (s.compare (0, n, "config.install.root") == 0 && + (s[n] == '=' || space (s[n]))) + { + while (space (s[n])) ++n; // Skip spaces. + if (s[n] == '=') ++n; // Skip the equal sign. + while (space (s[n])) ++n; // Skip spaces. + + install_root = dir_path (s, n, s.size () - n); + break; + } + } + + if (install_root.empty ()) + break; + } + + // Now the overall plan is as follows: + // + // 1. Install the package. + // + // 2. If the package project has the 'tests' subdirectory that is a + // subproject, then configure, build and test it out of the source tree + // against the installed package. + // + // 3. Uninstall the package. + // + // Install. + // + { + operation_result& r (add_result ("install")); + + // bpkg install + // + r.status |= run_bpkg (trace, r.log, wre, "install", tm.name); + + if (!r.status) + break; + + rm.status |= r.status; + } + + // Test installed. + // + // The package tests subdirectory path (may not exist). + // + dir_path tests_dir (tm.name + "-" + tm.version.string ()); + tests_dir /= "tests"; + + // We will consider the tests subdirectory to be a subproject if it + // contains a build2 bootstrap file. + // + if (file_exists (tests_dir / path ("build/bootstrap.build"))) + { + operation_result& r (add_result ("test-installed")); + + change_wd (rwd); + + // Sort environment arguments into modules and configuration variables. + // + string mods; // build2 create meta-operation parameters. + cstrings vars; + + for (const auto& a: env) + { + // Note that we don't check for the argument emptiness, as this is + // already done by 'bpkg create' (see above). + // + if (strchr (a, '=') != nullptr) + { + vars.push_back (a); + } + else + { + mods += mods.empty () ? ", " : " "; + mods += a; + } + } + + // b create(, ) + // + // Amalgamation directory that will contain configuration subdirectory + // for package tests out of source tree build. + // + dir_path out_dir ("build-installed"); + + r.status |= run_b ( + trace, r.log, wre, + "create(" + out_dir.representation () + mods + ")", + config, + vars); + + if (!r.status) + break; + + rm.status |= r.status; + + // Make sure that the installed package executables are properly imported + // when configuring and running tests. + // + // Note that we add the $config.install.root/bin directory at the + // beginning of the PATH environment variable value, so the installed + // executables are found first. + // + string paths ("PATH=" + (install_root / "bin").string ()); + + if (char const* s = getenv ("PATH")) + { + paths += path::traits::path_separator; + paths += s; + } + + small_vector envvars ({move (paths)}); + + // b configure(@) + // + dir_path tests_out_dir (out_dir / dir_path ("tests")); + + r.status |= run_b (trace, r.log, wre, + envvars, + "configure(" + + (build_dir / tests_dir).representation () + '@' + + tests_out_dir.representation () + ")"); + + if (!r.status) + break; + + rm.status |= r.status; + + // b test() + // + r.status |= run_b (trace, r.log, wre, + envvars, + "test(" + tests_out_dir.representation () + ')'); + + if (!r.status) + break; + + rm.status |= r.status; + + change_wd (build_dir); + } + + // Uninstall. + // + { + operation_result& r (add_result ("uninstall")); + + // bpkg uninstall + // + r.status |= run_bpkg (trace, r.log, wre, "uninstall", tm.name); + + if (!r.status) + break; + + rm.status |= r.status; + } + break; } rm.status |= rm.results.back ().status; // Merge last in case of a break. - if (!owd.empty ()) - change_wd (owd); + if (!rwd.empty ()) + change_wd (rwd); // Upload the result. // -- cgit v1.1