aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-11-04 23:33:28 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-11-06 16:47:05 +0300
commitdd591dae0f04035f4c4697cdd0d2c58461cddf06 (patch)
tree4750c4b648cb75ee20baf5cd3f1e359bf434a699
parent0076593f8dfaf5ed15cfd42c963bde4e038d42bd (diff)
Add support for testing build system modules to worker
-rw-r--r--bbot/worker/worker.cxx478
-rw-r--r--doc/manual.cli33
-rw-r--r--tests/integration/testscript12
3 files changed, 383 insertions, 140 deletions
diff --git a/bbot/worker/worker.cxx b/bbot/worker/worker.cxx
index 70dbac2..6872234 100644
--- a/bbot/worker/worker.cxx
+++ b/bbot/worker/worker.cxx
@@ -54,6 +54,17 @@ namespace bbot
}
static dir_path
+current_directory ()
+try
+{
+ return dir_path::current_directory ();
+}
+catch (const system_error& e)
+{
+ fail << "unable to obtain current directory: " << e << endf;
+}
+
+static dir_path
change_wd (tracer& t, string* log, const dir_path& d, bool create = false)
try
{
@@ -68,7 +79,7 @@ try
try_mkdir_p (d);
}
- dir_path r (dir_path::current_directory ());
+ dir_path r (current_directory ());
if (verb >= 3)
t << "cd " << d;
@@ -312,8 +323,18 @@ build (size_t argc, const char* argv[])
operation_results {}
};
+ // Reserve storage large enough to hold all the potential operation results
+ // without reallocations. Note that this is not an optimization but is
+ // required to make sure that element references are not invalidated when
+ // new results are added.
+ //
+ const size_t max_results (6);
+ rm.results.reserve (max_results);
+
auto add_result = [&rm] (string o) -> operation_result&
{
+ assert (rm.results.size () < max_results);
+
rm.results.push_back (
operation_result {move (o), result_status::success, ""});
@@ -488,14 +509,35 @@ build (size_t argc, const char* argv[])
return r;
};
- // Configure.
- //
// Search for config.install.root variable. If it is present and has a
// non-empty value, then test the package installation and uninstall. Note
// that passing [null] value would be meaningless, so we don't recognize it
- // as a special one.
+ // as a special one. While at it, cache the bpkg.configure.create args for
+ // later use.
//
dir_path install_root;
+ strings cargs (step_args (config_args, step_id::bpkg_configure_create));
+ {
+ size_t n (19);
+ auto space = [] (char c) {return c == ' ' || c == '\t';};
+
+ for (const string& s: reverse_iterate (cargs))
+ {
+ 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.
+
+ // Note that the config.install.root variable value may
+ // potentially be quoted.
+ //
+ install_root = dir_path (unquote (string (s, n, s.size () - n)));
+ break;
+ }
+ }
+ }
// bpkg-rep-fetch trust options.
//
@@ -517,64 +559,248 @@ build (size_t argc, const char* argv[])
trust_ops.push_back (t);
}
- // Configuration directory name.
+ const string& pkg (tm.name.string ());
+ const version& ver (tm.version);
+ const string repo (tm.repository.string ());
+ const dir_path pkg_dir (pkg + '-' + ver.string ());
+
+ // Specify the revision explicitly for the bpkg-build command not to end
+ // up with a race condition building the latest revision rather than the
+ // zero revision.
//
- dir_path build_dir ("build");
+ const string pkg_rev (pkg +
+ '/' +
+ version (ver.epoch,
+ ver.upstream,
+ ver.release,
+ ver.effective_revision (),
+ ver.iteration).string ());
+
+ // Query the project's build system information with `b info`.
+ //
+ auto prj_info = [&trace] (const dir_path& d, const char* what)
{
- strings cargs (step_args (config_args, step_id::bpkg_configure_create));
+ // Note that the `b info` diagnostics won't be copied into any of the
+ // build logs. This is fine as this is likely to be an infrastructure
+ // problem, given that the project distribution has been successfully
+ // created. It's actually not quite clear which log this diagnostics
+ // could go into.
+ //
+ try
{
- size_t n (19);
- auto space = [] (char c) {return c == ' ' || c == '\t';};
-
- for (const string& s: reverse_iterate (cargs))
- {
- 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.
+ return b_info (d, verb, trace);
+ }
+ catch (const b_error& e)
+ {
+ if (e.normal ())
+ throw failed (); // Assume the build2 process issued diagnostics.
- // Note that the config.install.root variable value may
- // potentially be quoted.
- //
- install_root = dir_path (unquote (string (s, n, s.size () - n)));
- break;
- }
- }
+ fail << "unable to query " << what << ' ' << d << " info: " << e
+ << endf;
}
+ };
+
+ b_project_info prj; // Package project information.
- operation_result& r (add_result ("configure"));
+ // If this is a build system module, perform a "pre-step" by building it
+ // in a separate configuration reproducing the one used to build build2
+ // itself. Note that the configuration and the environment options and
+ // variables are not passed to commands that may affect this
+ // configuration.
+ //
+ bool module (pkg.compare (0, 10, "libbuild2-") == 0);
+ dir_path module_dir ("build-module");
+
+ // Note that we don't consider the module configuring and testing during
+ // this pre-step separate operations and share the operation logs with the
+ // "main" configure and test steps (see below). Thus, we save pointers to
+ // the added result objects for the subsequent use.
+ //
+ operation_result* configure_result (nullptr);
+ operation_result* test_result (nullptr);
- // Noop, just for the log record.
+ rwd = current_directory ();
+
+ if (module)
+ {
+ // Configure.
//
- try
{
- change_wd (trace, &r.log, dir_path::current_directory ());
+ operation_result& r (add_result ("configure"));
+ configure_result = &r;
+
+ // Noop, just for the log record.
+ //
+ change_wd (trace, &r.log, rwd);
+
+ // b create(<dir>) config.import=~host
+ //
+ r.status |= run_b (
+ trace, r.log, wre,
+ "-V",
+ "create(" + module_dir.representation () + ",cc)",
+ "config.import=~host");
+
+ if (!r.status)
+ break;
+
+ change_wd (trace, &r.log, module_dir);
+
+ // bpkg create --existing
+ //
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "create",
+ "--existing");
+
+ if (!r.status)
+ break;
+
+ // bpkg add <env-config-args> <config-args> <repository-url>
+ //
+ // bpkg.configure.add
+ //
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "add",
+ step_args (env_args, step_id::bpkg_configure_add),
+ step_args (config_args, step_id::bpkg_configure_add),
+ repo);
+
+ if (!r.status)
+ break;
+
+ // bpkg fetch <env-config-args> <config-args> <trust-options>
+ //
+ // bpkg.configure.fetch
+ //
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "fetch",
+ step_args (env_args, step_id::bpkg_configure_fetch),
+ step_args (config_args, step_id::bpkg_configure_fetch),
+ trust_ops);
+
+ if (!r.status)
+ break;
+
+ // bpkg build --configure-only <package-name>/<package-version>
+ //
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "build",
+ "--configure-only",
+ "--yes",
+ pkg_rev);
+
+ if (!r.status)
+ break;
+
+ rm.status |= r.status;
}
- catch (const system_error& e)
+
+ // Update.
+ //
{
- fail << "unable to obtain current directory: " << e;
+ operation_result& r (add_result ("update"));
+
+ // Noop, just for the log record to reduce the potential confusion for
+ // the combined log reader due to the configure operation log sharing
+ // (see above for details).
+ //
+ change_wd (trace, &r.log, current_directory ());
+
+ // bpkg update <package-name>
+ //
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "update",
+ pkg);
+
+ if (!r.status)
+ break;
+
+ rm.status |= r.status;
}
+ // Run the package internal tests if the test operation is supported by
+ // the project.
+ //
+ prj = prj_info (pkg_dir, "project");
+
+ if (find (prj.operations.begin (), prj.operations.end (), "test") !=
+ prj.operations.end ())
+ {
+ operation_result& r (add_result ("test"));
+ test_result = &r;
+
+ // Use --package-cwd to help ported to build2 third-party packages a
+ // bit (see bpkg-pkg-test(1) for details).
+ //
+ // bpkg test <package-name>
+ //
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "test",
+ "--package-cwd",
+ pkg);
+
+ if (!r.status)
+ break;
+
+ rm.status |= r.status;
+ }
+ }
+
+ // The "main" step.
+ //
+
+ // Configure.
+ //
+ dir_path build_dir ("build"); // Configuration directory name.
+ dir_path pkg_config (rwd / (module ? module_dir : build_dir));
+ {
+ operation_result& r (configure_result != nullptr
+ ? *configure_result
+ : add_result ("configure"));
+
+ change_wd (trace, &r.log, rwd);
+
// bpkg create <env-modules> <env-config-args> <config-args>
//
// bpkg.configure.create
//
- r.status |= run_bpkg (
- trace, r.log, wre,
- "-V",
- "create",
- "-d", build_dir.string (),
- "--wipe",
- step_args (modules, step_id::bpkg_configure_create),
- step_args (env_args, step_id::bpkg_configure_create),
- cargs);
+ {
+ // If the package is a build system module, then make sure it is
+ // importable in this configuration.
+ //
+ string import (module
+ ? ("config.import." + tm.name.variable () + "=" +
+ module_dir.string ())
+ : "");
- if (!r.status)
- break;
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-V",
+ "create",
+ "-d", build_dir.string (),
+ "--wipe",
+ step_args (modules, step_id::bpkg_configure_create),
+ step_args (env_args, step_id::bpkg_configure_create),
+ cargs,
+ import.empty () ? nullptr : import.c_str ());
+
+ if (!r.status)
+ break;
+ }
- rwd = change_wd (trace, &r.log, build_dir);
+ change_wd (trace, &r.log, build_dir);
// bpkg add <env-config-args> <config-args> <repository-url>
//
@@ -586,7 +812,7 @@ build (size_t argc, const char* argv[])
"add",
step_args (env_args, step_id::bpkg_configure_add),
step_args (config_args, step_id::bpkg_configure_add),
- tm.repository.string ());
+ repo);
if (!r.status)
break;
@@ -611,34 +837,29 @@ build (size_t argc, const char* argv[])
//
// bpkg.configure.build
//
- // Specify the revision explicitly not to end up with a race condition
- // building the latest revision rather than the zero revision.
- //
- version v (tm.version.epoch,
- tm.version.upstream,
- tm.version.release,
- tm.version.effective_revision (),
- tm.version.iteration);
-
- r.status |= run_bpkg (
- trace, r.log, wre,
- "-v",
- "build",
- "--configure-only",
- "--yes",
- step_args (env_args, step_id::bpkg_configure_build),
- step_args (config_args, step_id::bpkg_configure_build),
- "--",
- tm.name.string () + '/' + v.string ());
+ if (!module) // Note: the module is already built in the pre-step.
+ {
+ r.status |= run_bpkg (
+ trace, r.log, wre,
+ "-v",
+ "build",
+ "--configure-only",
+ "--yes",
+ step_args (env_args, step_id::bpkg_configure_build),
+ step_args (config_args, step_id::bpkg_configure_build),
+ "--",
+ pkg_rev);
- if (!r.status)
- break;
+ if (!r.status)
+ break;
+ }
rm.status |= r.status;
}
// Update.
//
+ if (!module) // Note: the module is already built in the pre-step.
{
operation_result& r (add_result ("update"));
@@ -652,7 +873,7 @@ build (size_t argc, const char* argv[])
"update",
step_args (env_args, step_id::bpkg_update_update),
step_args (config_args, step_id::bpkg_update_update),
- tm.name.string ());
+ pkg);
if (!r.status)
break;
@@ -660,43 +881,30 @@ build (size_t argc, const char* argv[])
rm.status |= r.status;
}
- // Query the package's build system information with `b info`.
- //
- dir_path prj_dir (tm.name.string () + '-' + tm.version.string ());
- b_project_info prj;
-
- // Note that `b info` diagnostics will not be copied into any of the build
- // logs. This seems to be fine, as this is likely to be an infrastructural
- // problem, given that the project distribution has been successfully
- // created. It's actually not quite clear which log this diagnostics could
- // go into.
+ // Run the package internal tests if the test operation is supported by
+ // the project, except for the build system module which is taken care of
+ // in the pre-step.
//
- try
- {
- prj = b_info (prj_dir, verb, trace);
- }
- catch (const b_error& e)
+ bool internal_tests;
+
+ if (module)
+ internal_tests = false;
+ else
{
- if (e.normal ())
- throw failed (); // Assume the build2 process issued diagnostics.
+ prj = prj_info (pkg_dir, "project");
- fail << "unable to query project " << prj_dir << " info: " << e;
+ internal_tests = find (prj.operations.begin (),
+ prj.operations.end (),
+ "test") != prj.operations.end ();
}
- // Run the package internal tests if the test operation is supported by
- // the project.
- //
- bool internal_tests (find (prj.operations.begin (),
- prj.operations.end (),
- "test") != prj.operations.end ());
-
// Parse the package manifest.
//
// Note that being unable to parse the package manifest is likely to be an
- // infrastructural problem, given that the package has been successfully
+ // infrastructure problem, given that the package has been successfully
// configured.
//
- path mf (prj_dir / "manifest");
+ path mf (pkg_config / pkg_dir / "manifest");
package_manifest pm (parse_manifest<package_manifest> (mf, "package"));
// Run the package external tests if any of the tests, examples, or
@@ -704,7 +912,8 @@ build (size_t argc, const char* argv[])
//
// Note that we assume that these packages belong to the dependent
// package's repository or its complement repositories, recursively. Thus,
- // we test them in the configuration used to build the dependent package.
+ // we test them in the configuration used to build the dependent package
+ // (except for the build system module).
//
bool external_tests (!pm.tests.empty () ||
!pm.examples.empty () ||
@@ -768,9 +977,6 @@ build (size_t argc, const char* argv[])
// Note that we assume that the package supports the test operation
// since this is its main purpose.
//
- // Use --package-cwd to help ported to build2 third-party packages a
- // bit (see bpkg-pkg-test(1) for details).
- //
// bpkg test <env-config-args> <config-args> <package-name>
//
// bpkg.test.test
@@ -780,7 +986,7 @@ build (size_t argc, const char* argv[])
trace, r.log, wre,
"-v",
"test",
- "--package-cwd",
+ "--package-cwd", // See above for details.
step_args (env_args, step_id::bpkg_test_test),
step_args (config_args, step_id::bpkg_test_test),
pkg);
@@ -794,7 +1000,9 @@ build (size_t argc, const char* argv[])
if (internal_tests || external_tests)
{
- operation_result& r (add_result ("test"));
+ operation_result& r (test_result != nullptr
+ ? *test_result
+ : add_result ("test"));
// Run internal tests.
//
@@ -811,7 +1019,7 @@ build (size_t argc, const char* argv[])
"--package-cwd", // See above for details.
step_args (env_args, step_id::bpkg_test_test),
step_args (config_args, step_id::bpkg_test_test),
- tm.name.string ());
+ pkg);
if (!r.status)
break;
@@ -859,6 +1067,8 @@ build (size_t argc, const char* argv[])
{
operation_result& r (add_result ("install"));
+ change_wd (trace, &r.log, pkg_config);
+
// bpkg install <env-config-args> <config-args> <package-name>
//
// bpkg.install.install
@@ -869,7 +1079,7 @@ build (size_t argc, const char* argv[])
"install",
step_args (env_args, step_id::bpkg_install_install),
step_args (config_args, step_id::bpkg_install_install),
- tm.name.string ());
+ pkg);
if (!r.status)
break;
@@ -880,9 +1090,19 @@ build (size_t argc, const char* argv[])
// Test installed.
//
// Make sure that the installed package executables are properly imported
- // when configuring and running tests.
+ // when configuring and running tests, unless we are testing the build
+ // system module (that supposedly doesn't install any executables).
//
small_vector<string, 1> envvars;
+
+ dir_paths subprj_dirs; // "Testable" package subprojects.
+
+ // We expect the build system modules to not have any testable subprojects
+ // but to have external tests package instead.
+ //
+ if (module)
+ internal_tests = false;
+ else
{
// Note that we add the $config.install.root/bin directory at the
// beginning of the PATH environment variable value, so the installed
@@ -897,43 +1117,27 @@ build (size_t argc, const char* argv[])
}
envvars.push_back (move (paths));
- }
-
- // Collect the "testable" subprojects.
- //
- dir_paths subprj_dirs;
- for (const b_project_info::subproject& sp: prj.subprojects)
- {
- // Retrieve the subproject information similar to how we've done it for
- // the package.
- //
- dir_path sd (prj_dir / sp.path);
- // Note that `b info` diagnostics will not be logged (see above for
- // details).
+ // Collect the "testable" subprojects.
//
- try
+ for (const b_project_info::subproject& sp: prj.subprojects)
{
- b_project_info si (b_info (sd, verb, trace));
+ // Retrieve the subproject information similar to how we've done it
+ // for the package.
+ //
+ b_project_info si (prj_info (pkg_dir / sp.path, "subproject"));
const strings& ops (si.operations);
if (find (ops.begin (), ops.end (), "test") != ops.end ())
subprj_dirs.push_back (sp.path);
}
- catch (const b_error& e)
- {
- if (e.normal ())
- throw failed (); // Assume the build2 process issued diagnostics.
- fail << "unable to query subproject " << sd << " info: " << e;
- }
+ // If there are any "testable" subprojects, then configure them
+ // (sequentially) and test/build in parallel afterwards.
+ //
+ internal_tests = !subprj_dirs.empty ();
}
- // If there are any "testable" subprojects, then configure them
- // (sequentially) and test/build in parallel afterwards.
- //
- internal_tests = !subprj_dirs.empty ();
-
if (internal_tests || external_tests)
{
operation_result& r (add_result ("test-installed"));
@@ -946,8 +1150,8 @@ build (size_t argc, const char* argv[])
{
string mods; // build2 create meta-operation parameters.
- for (const string& m:
- step_args (modules, step_id::b_test_installed_create))
+ for (const string& m: step_args (modules,
+ step_id::b_test_installed_create))
{
mods += mods.empty () ? ", " : " ";
mods += m;
@@ -982,7 +1186,7 @@ build (size_t argc, const char* argv[])
//
// b.test-installed.configure
//
- dir_path subprj_src_dir (build_dir / prj_dir / d);
+ dir_path subprj_src_dir (build_dir / pkg_dir / d);
dir_path subprj_out_dir (out_dir / d);
r.status |= run_b (
@@ -1048,7 +1252,7 @@ build (size_t argc, const char* argv[])
if (!r.status)
break;
- dir_path owd (change_wd (trace, &r.log, config_dir));
+ change_wd (trace, &r.log, config_dir);
// bpkg add <env-config-args> <config-args> <repository-url>
//
@@ -1060,7 +1264,7 @@ build (size_t argc, const char* argv[])
"add",
step_args (env_args, step_id::bpkg_configure_add),
step_args (config_args, step_id::bpkg_configure_add),
- tm.repository.string ());
+ repo);
if (!r.status)
break;
@@ -1086,12 +1290,8 @@ build (size_t argc, const char* argv[])
!test (pm.examples, r, envvars) ||
!test (pm.benchmarks, r, envvars))
break;
-
- change_wd (trace, &r.log, owd);
}
- change_wd (trace, &r.log, build_dir);
-
rm.status |= r.status;
}
@@ -1100,6 +1300,8 @@ build (size_t argc, const char* argv[])
{
operation_result& r (add_result ("uninstall"));
+ change_wd (trace, &r.log, pkg_config);
+
// bpkg uninstall <env-config-args> <config-args> <package-name>
//
// bpkg.uninstall.uninstall
@@ -1110,7 +1312,7 @@ build (size_t argc, const char* argv[])
"uninstall",
step_args (env_args, step_id::bpkg_uninstall_uninstall),
step_args (config_args, step_id::bpkg_uninstall_uninstall),
- tm.name.string ());
+ pkg);
if (!r.status)
break;
diff --git a/doc/manual.cli b/doc/manual.cli
index 66c2b02..43a2709 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -881,6 +881,39 @@ bpkg -v test <package-name>
For details on configuring and testing installation refer to
\l{#arch-controller Controller Logic}.
+If the package is a build system module, then it is built and tested (using
+the bundled tests) in a separate configuration that mimics the one used to
+build \c{build2} itself. Note that the configuration and environment options
+and variables are not passed to commands that may affect this configuration.
+Such commands, therefore, have no associated \i{step id}:
+
+\
+#
+#
+b -V create config.import=~host
+bpkg -v create --existing
+
+# bpkg.configure.add
+#
+bpkg -v add <repository-url>
+
+# bpkg.configure.fetch
+#
+bpkg -v fetch --trust <repository-fp>
+
+#
+#
+bpkg -v build --yes --configure-only <package-name>/<package-version>
+
+#
+#
+bpkg -v update <package-name>
+
+# if the test operation is supported by the package:
+#
+bpkg -v test <package-name>
+\
+
As an example, the following POSIX shell script can be used to setup the
environment for building C and C++ packages with GCC 9 on most Linux
distributions.
diff --git a/tests/integration/testscript b/tests/integration/testscript
index d06b931..5475b12 100644
--- a/tests/integration/testscript
+++ b/tests/integration/testscript
@@ -73,6 +73,14 @@ rep_type = git
rfp = yes
#\
+#\
+pkg = libbuild2-hello
+ver = 0.1.0-a.0.20191106105744.1ad196b43c42
+rep_url = "https://github.com/build2/libbuild2-hello.git#master"
+rep_type = git
+rfp = yes
+#\
+
# Note that we also need to make sure that the installed package libraries are
# properly imported when configuring and running tests, and that the installed
# executables are runnable.
@@ -137,6 +145,6 @@ a = $0
chmod ugo+x $env;
sleep $wait;
$w --verbose 3 --startup --tftp-host $tftp --environments $~ \
- &build/*** &?build-installed/*** &?build-installed-bpkg/*** \
- &task.manifest 2>|
+ &?build-module/*** &build/*** &?build-installed/*** \
+ &?build-installed-bpkg/*** &task.manifest 2>|
}