summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2019-03-07 09:06:37 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2019-03-07 09:06:37 +0200
commit1845141809aa91b03718066a6f46863885a6a887 (patch)
treea3f542ec7c1781e65aa16a9b0d5c31eae4c4d757
parente0002617846755fb5f199f40a677e16d6f69e5ec (diff)
downloadbuild2-1845141809aa91b03718066a6f46863885a6a887.zip
build2-1845141809aa91b03718066a6f46863885a6a887.tar.gz
build2-1845141809aa91b03718066a6f46863885a6a887.tar.xz
Add support for alternative build file/directory naming scheme
Now the build/*.build, buildfile, and .buildignore filesystem entries in a project can alternatively (but consistently) be called build2/*.build2, build2file, and .build2ignore. See a note at the beginning of the Project Structure section in the manual for details (motivation, restrictions, etc).
-rw-r--r--build2/b-options.cxx17
-rw-r--r--build2/b.cli14
-rw-r--r--build2/b.cxx89
-rw-r--r--build2/cc/compile-rule.cxx8
-rw-r--r--build2/cc/init.cxx8
-rw-r--r--build2/cc/utility.cxx2
-rw-r--r--build2/cc/utility.hxx8
-rw-r--r--build2/config/init.cxx2
-rw-r--r--build2/config/operation.cxx41
-rw-r--r--build2/config/utility.cxx10
-rw-r--r--build2/config/utility.hxx9
-rw-r--r--build2/dist/operation.cxx4
-rw-r--r--build2/file.cxx381
-rw-r--r--build2/file.hxx43
-rw-r--r--build2/file.ixx15
-rw-r--r--build2/filesystem.cxx23
-rw-r--r--build2/filesystem.hxx14
-rw-r--r--build2/parser.cxx31
-rw-r--r--build2/scope.hxx25
-rw-r--r--build2/search.cxx2
-rw-r--r--build2/target-key.hxx12
-rw-r--r--build2/target-type.hxx31
-rw-r--r--build2/target.cxx82
-rw-r--r--build2/target.hxx19
-rw-r--r--build2/target.txx2
-rw-r--r--build2/test/rule.cxx13
-rw-r--r--build2/test/script/runner.cxx24
-rw-r--r--build2/test/script/script.cxx6
-rw-r--r--build2/test/script/script.hxx5
-rw-r--r--build2/test/target.cxx2
-rw-r--r--doc/manual.cli35
31 files changed, 657 insertions, 320 deletions
diff --git a/build2/b-options.cxx b/build2/b-options.cxx
index d9a9866..8306176 100644
--- a/build2/b-options.cxx
+++ b/build2/b-options.cxx
@@ -618,7 +618,7 @@ namespace build2
match_only_ (),
no_column_ (),
no_line_ (),
- buildfile_ ("buildfile"),
+ buildfile_ (),
buildfile_specified_ (false),
config_guess_ (),
config_guess_specified_ (false),
@@ -835,13 +835,14 @@ namespace build2
os << std::endl
<< "\033[1m--buildfile\033[0m \033[4mpath\033[0m The alternative file to read build information from. The" << ::std::endl
- << " default is \033[1mbuildfile\033[0m. If \033[4mpath\033[0m is '\033[1m-\033[0m', then read from" << ::std::endl
- << " \033[1mSTDIN\033[0m. Note that this option only affects the files read" << ::std::endl
- << " as part of the buildspec processing. Specifically, it has" << ::std::endl
- << " no effect on the \033[1msource\033[0m and \033[1minclude\033[0m directives. As a" << ::std::endl
- << " result, this option is primarily intended for testing" << ::std::endl
- << " rather than changing the build file names in real" << ::std::endl
- << " projects." << ::std::endl;
+ << " default is \033[1mbuildfile\033[0m or \033[1mbuild2file\033[0m, depending on the" << ::std::endl
+ << " project's build file/directory naming scheme. If \033[4mpath\033[0m is" << ::std::endl
+ << " '\033[1m-\033[0m', then read from \033[1mSTDIN\033[0m. Note that this option only" << ::std::endl
+ << " affects the files read as part of the buildspec" << ::std::endl
+ << " processing. Specifically, it has no effect on the \033[1msource\033[0m" << ::std::endl
+ << " and \033[1minclude\033[0m directives. As a result, this option is" << ::std::endl
+ << " primarily intended for testing rather than changing the" << ::std::endl
+ << " build file names in real projects." << ::std::endl;
os << std::endl
<< "\033[1m--config-guess\033[0m \033[4mpath\033[0m The path to the \033[1mconfig.guess(1)\033[0m script that should be used" << ::std::endl
diff --git a/build2/b.cli b/build2/b.cli
index a88693d..35d5e2e 100644
--- a/build2/b.cli
+++ b/build2/b.cli
@@ -561,15 +561,17 @@ namespace build2
"Don't print line and column numbers in diagnostics."
}
- path --buildfile = "buildfile"
+ path --buildfile
{
"<path>",
"The alternative file to read build information from. The default is
- \cb{buildfile}. If <path> is '\cb{-}', then read from \cb{STDIN}. Note
- that this option only affects the files read as part of the buildspec
- processing. Specifically, it has no effect on the \cb{source} and
- \cb{include} directives. As a result, this option is primarily intended
- for testing rather than changing the build file names in real projects."
+ \cb{buildfile} or \cb{build2file}, depending on the project's build
+ file/directory naming scheme. If <path> is '\cb{-}', then read from
+ \cb{STDIN}. Note that this option only affects the files read as part
+ of the buildspec processing. Specifically, it has no effect on the
+ \cb{source} and \cb{include} directives. As a result, this option is
+ primarily intended for testing rather than changing the build file
+ names in real projects."
}
path --config-guess
diff --git a/build2/b.cxx b/build2/b.cxx
index 0698e18..a7207b9 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -519,23 +519,59 @@ main (int argc, char* argv[])
// continuing in the parent directories until root. Return empty path if
// not found.
//
- auto find_buildfile = [] (const dir_path& d, const dir_path& root)
+ auto find_buildfile = [] (const dir_path& sd,
+ const dir_path& root,
+ optional<bool>& altn)
{
- const path& n (ops.buildfile ());
+ const path& n (ops.buildfile_specified () ? ops.buildfile () : path ());
if (n.string () == "-")
return n;
- for (path f (d / n);; )
+ path f;
+ dir_path p;
+
+ for (;;)
{
- if (exists (f))
+ const dir_path& d (p.empty () ? sd : p.directory ());
+
+ // Note that we don't attempt to derive the project's naming scheme
+ // from the buildfile name specified by the user.
+ //
+ bool e;
+ if (!n.empty () || altn)
+ {
+ f = d / (!n.empty () ? n : (*altn
+ ? alt_buildfile_file
+ : std_buildfile_file));
+ e = exists (f);
+ }
+ else
+ {
+ // Note: this case seems to be only needed for simple projects.
+ //
+
+ // Check the alternative name first since it is more specific.
+ //
+ f = d / alt_buildfile_file;
+
+ if ((e = exists (f)))
+ altn = true;
+ else
+ {
+ f = d / std_buildfile_file;
+
+ if ((e = exists (f)))
+ altn = false;
+ }
+ }
+
+ if (e)
return f;
- dir_path p (f.directory ());
+ p = f.directory ();
if (p == root)
break;
-
- f = p.directory () / n;
}
return path ();
@@ -751,6 +787,10 @@ main (int argc, char* argv[])
dir_path src_root;
dir_path out_root;
+ // Standard/alternative build file/directory naming.
+ //
+ optional<bool> altn;
+
// Update these in buildspec.
//
bool& forwarded (ts.forwarded);
@@ -782,7 +822,7 @@ main (int argc, char* argv[])
// If the src_base was explicitly specified, search for src_root.
//
- src_root = find_src_root (src_base);
+ src_root = find_src_root (src_base, altn);
// If not found, assume this is a simple project with src_root
// being the same as src_base.
@@ -812,7 +852,7 @@ main (int argc, char* argv[])
{
// If no src_base was explicitly specified, search for out_root.
//
- auto p (find_out_root (out_base));
+ auto p (find_out_root (out_base, altn));
if (p.second) // Also src_root.
{
@@ -821,7 +861,7 @@ main (int argc, char* argv[])
// Handle a forwarded configuration. Note that if we've changed
// out_root then we also have to remap out_base.
//
- out_root = bootstrap_fwd (src_root);
+ out_root = bootstrap_fwd (src_root, altn);
if (src_root != out_root)
{
out_base = out_root / out_base.leaf (src_root);
@@ -840,12 +880,12 @@ main (int argc, char* argv[])
// try to look for outer buildfiles since we don't have the root
// to stop at. However, this shouldn't be an issue since simple
// project won't normally have targets in subdirectories (or, in
- // other words, we are not very interested "complex simple
+ // other words, we are not very interested in "complex simple
// projects").
//
if (out_root.empty ())
{
- if (find_buildfile (out_base, out_base).empty ())
+ if (find_buildfile (out_base, out_base, altn).empty ())
{
fail << "no buildfile in " << out_base <<
info << "consider explicitly specifying its src_base";
@@ -873,7 +913,7 @@ main (int argc, char* argv[])
if (!bstrapped)
{
- bootstrap_out (rs);
+ bootstrap_out (rs, altn);
// See if the bootstrap process set/changed src_root.
//
@@ -928,21 +968,28 @@ main (int argc, char* argv[])
// Now that we have src_root, load the src_root bootstrap file,
// if there is one.
//
- bootstrap_pre (rs);
- bootstrap_src (rs);
+ bootstrap_pre (rs, altn);
+ bootstrap_src (rs, altn);
// bootstrap_post() delayed until after create_bootstrap_outer().
}
else
{
- if (src_root.empty ())
- src_root = rs.src_path ();
-
// Note that we only "upgrade" the forwarded value since the same
// project root can be arrived at via multiple paths (think
// command line and import).
//
if (forwarded)
rs.assign (var_forwarded) = true;
+
+ // Sync local variable that are used below with actual values.
+ //
+ if (src_root.empty ())
+ src_root = rs.src_path ();
+
+ if (!altn)
+ altn = rs.root_extra->altn;
+ else
+ assert (*altn == rs.root_extra->altn);
}
// At this stage we should have both roots and out_base figured
@@ -1201,7 +1248,7 @@ main (int argc, char* argv[])
// data from the cash (we don't really care about the negative
// answer since this is a degenerate case).
//
- path bf (find_buildfile (src_base, src_base));
+ path bf (find_buildfile (src_base, src_base, altn));
if (bf.empty ())
{
// If the target is a directory and the implied buildfile is
@@ -1210,12 +1257,12 @@ main (int argc, char* argv[])
//
if ((tn.directory () || tn.type == "dir") &&
exists (src_base) &&
- dir::check_implied (src_base))
+ dir::check_implied (rs, src_base))
; // Leave bf empty.
else
{
if (src_base != src_root)
- bf = find_buildfile (src_base.directory (), src_root);
+ bf = find_buildfile (src_base.directory (), src_root, altn);
if (bf.empty ())
fail << "no buildfile in " << src_base << " or parent "
diff --git a/build2/cc/compile-rule.cxx b/build2/cc/compile-rule.cxx
index eacce7b..e0faf7f 100644
--- a/build2/cc/compile-rule.cxx
+++ b/build2/cc/compile-rule.cxx
@@ -3922,7 +3922,10 @@ namespace build2
// So the first step is to check if the project has already been created
// and/or loaded and if not, then to go ahead and do so.
//
- dir_path pd (as->out_path () / modules_sidebuild_dir /= x);
+ dir_path pd (as->out_path () /
+ as->root_extra->build_dir /
+ modules_sidebuild_dir /=
+ x);
{
const scope* ps (&scopes.find (pd));
@@ -3942,7 +3945,8 @@ namespace build2
// The project might already be created in which case we just need
// to load it.
//
- if (!is_src_root (pd))
+ optional<bool> altn (false); // Standard naming scheme.
+ if (!is_src_root (pd, altn))
{
// Copy our standard and force modules.
//
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index edb9961..d9c1371 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -27,7 +27,9 @@ namespace build2
static target_state
clean_module_sidebuilds (action, const scope& rs, const dir&)
{
- dir_path d (rs.out_path () / modules_sidebuild_dir);
+ const dir_path& out_root (rs.out_path ());
+
+ dir_path d (out_root / rs.root_extra->build_dir / modules_sidebuild_dir);
if (exists (d))
{
@@ -35,7 +37,7 @@ namespace build2
{
// Clean up cc/ if it became empty.
//
- d = rs.out_path () / module_dir;
+ d = out_root / rs.root_extra->build_dir / module_dir;
if (empty (d))
{
rmdir (d);
@@ -43,7 +45,7 @@ namespace build2
// And build/ if it also became empty (e.g., in case of a build
// with a transient configuration).
//
- d = rs.out_path () / build_dir;
+ d = out_root / rs.root_extra->build_dir;
if (empty (d))
rmdir (d);
}
diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx
index 7e5ce53..39f5c35 100644
--- a/build2/cc/utility.cxx
+++ b/build2/cc/utility.cxx
@@ -19,7 +19,7 @@ namespace build2
{
using namespace bin;
- const dir_path module_dir (dir_path ("build") /= "cc");
+ const dir_path module_dir ("cc");
const dir_path modules_sidebuild_dir (dir_path (module_dir) /= "modules");
lorder
diff --git a/build2/cc/utility.hxx b/build2/cc/utility.hxx
index bf96949..6b629de 100644
--- a/build2/cc/utility.hxx
+++ b/build2/cc/utility.hxx
@@ -19,8 +19,12 @@ namespace build2
namespace cc
{
- extern const dir_path module_dir; // build/cc/
- extern const dir_path modules_sidebuild_dir; // build/cc/modules/
+ // To form the complete path do:
+ //
+ // root.out_path () / root.root_extra->build_dir / module_dir
+ //
+ extern const dir_path module_dir; // cc/
+ extern const dir_path modules_sidebuild_dir; // cc/modules/
// Compile output type.
//
diff --git a/build2/config/init.cxx b/build2/config/init.cxx
index 25c7022..9dc3a3c 100644
--- a/build2/config/init.cxx
+++ b/build2/config/init.cxx
@@ -88,7 +88,7 @@ namespace build2
const variable& c_v (vp.insert<uint64_t> ("config.version", false));
{
- path f (out_root / config_file);
+ path f (config_file (rs));
if (exists (f))
{
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index 5acc42b..1064ebb 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -28,9 +28,12 @@ namespace build2
// configure
//
static void
- save_src_root (const dir_path& out_root, const dir_path& src_root)
+ save_src_root (const scope& root)
{
- path f (out_root / src_root_file);
+ const dir_path& out_root (root.out_path ());
+ const dir_path& src_root (root.src_path ());
+
+ path f (out_root / root.root_extra->src_root_file);
if (verb >= 2)
text << "cat >" << f;
@@ -54,9 +57,12 @@ namespace build2
}
static void
- save_out_root (const dir_path& out_root, const dir_path& src_root)
+ save_out_root (const scope& root)
{
- path f (src_root / out_root_file);
+ const dir_path& out_root (root.out_path ());
+ const dir_path& src_root (root.src_path ());
+
+ path f (src_root / root.root_extra->out_root_file);
if (verb)
text << (verb >= 2 ? "cat >" : "save ") << f;
@@ -84,8 +90,7 @@ namespace build2
static void
save_config (const scope& root, const project_set& projects)
{
- const dir_path& out_root (root.out_path ());
- path f (out_root / config_file);
+ path f (config_file (root));
if (verb)
text << (verb >= 2 ? "cat >" : "save ") << f;
@@ -319,8 +324,8 @@ namespace build2
//
if (out_root != src_root)
{
- mkdir_p (out_root / build_dir);
- mkdir (out_root / bootstrap_dir, 2);
+ mkdir_p (out_root / root.root_extra->build_dir);
+ mkdir (out_root / root.root_extra->bootstrap_dir, 2);
}
// We distinguish between a complete configure and operation-
@@ -333,7 +338,7 @@ namespace build2
// Save src-root.build unless out_root is the same as src.
//
if (out_root != src_root)
- save_src_root (out_root, src_root);
+ save_src_root (root);
// Save config.build.
//
@@ -378,8 +383,8 @@ namespace build2
return;
}
- mkdir (src_root / bootstrap_dir, 2); // Make sure exists.
- save_out_root (out_root, src_root);
+ mkdir (src_root / root.root_extra->bootstrap_dir, 2); // Make sure exists.
+ save_out_root (root);
// Configure subprojects. Since we don't load buildfiles if configuring
// a forward, we do it for all known subprojects.
@@ -636,19 +641,19 @@ namespace build2
{
l5 ([&]{trace << "completely disfiguring " << out_root;});
- r = rmfile (out_root / config_file) || r;
+ r = rmfile (config_file (root)) || r;
if (out_root != src_root)
{
- r = rmfile (out_root / src_root_file, 2) || r;
+ r = rmfile (out_root / root.root_extra->src_root_file, 2) || r;
// Clean up the directories.
//
// Note: try to remove the root/ hooks directory if it is empty.
//
- r = rmdir (out_root / root_dir, 2) || r;
- r = rmdir (out_root / bootstrap_dir, 2) || r;
- r = rmdir (out_root / build_dir, 2) || r;
+ r = rmdir (out_root / root.root_extra->root_dir, 2) || r;
+ r = rmdir (out_root / root.root_extra->bootstrap_dir, 2) || r;
+ r = rmdir (out_root / root.root_extra->build_dir, 2) || r;
switch (rmdir (out_root))
{
@@ -712,8 +717,8 @@ namespace build2
// Remove the out-root.build file and try to remove the bootstrap/
// directory if it is empty.
//
- r = rmfile (src_root / out_root_file) || r;
- r = rmdir (src_root / bootstrap_dir, 2) || r;
+ r = rmfile (src_root / root.root_extra->out_root_file) || r;
+ r = rmdir (src_root / root.root_extra->bootstrap_dir, 2) || r;
return r;
}
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
index 2fee5f7..d2e8699 100644
--- a/build2/config/utility.cxx
+++ b/build2/config/utility.cxx
@@ -183,12 +183,14 @@ namespace build2
// Create the build/ subdirectory.
//
- mkdir (d / build_dir, verbosity);
+ // Note that for now we use the standard build file/directory scheme.
+ //
+ mkdir (d / std_build_dir, verbosity);
// Write build/bootstrap.build.
//
{
- path f (d / bootstrap_file);
+ path f (d / std_bootstrap_file);
if (verb >= verbosity)
text << (verb >= 2 ? "cat >" : "save ") << f;
@@ -232,7 +234,7 @@ namespace build2
// Write build/root.build.
//
{
- path f (d / root_file);
+ path f (d / std_root_file);
if (verb >= verbosity)
text << (verb >= 2 ? "cat >" : "save ") << f;
@@ -281,7 +283,7 @@ namespace build2
//
if (buildfile)
{
- path f (d / buildfile_file);
+ path f (d / std_buildfile_file);
if (verb >= verbosity)
text << (verb >= 2 ? "cat >" : "save ") << f;
diff --git a/build2/config/utility.hxx b/build2/config/utility.hxx
index 6f384f5..eff1a02 100644
--- a/build2/config/utility.hxx
+++ b/build2/config/utility.hxx
@@ -8,6 +8,7 @@
#include <build2/types.hxx>
#include <build2/utility.hxx>
+#include <build2/scope.hxx>
#include <build2/variable.hxx>
#include <build2/diagnostics.hxx>
@@ -160,6 +161,14 @@ namespace build2
bool buildfile, // Create root buildfile.
const char* who, // Who is creating it.
uint16_t verbosity = 1); // Diagnostic verbosity.
+
+ inline path
+ config_file (const scope& root)
+ {
+ return (root.out_path () /
+ root.root_extra->build_dir /
+ "config." + root.root_extra->build_ext);
+ }
}
}
diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx
index 75a5793..b227bbd 100644
--- a/build2/dist/operation.cxx
+++ b/build2/dist/operation.cxx
@@ -220,7 +220,7 @@ namespace build2
}
};
- add_adhoc (*rs, export_file);
+ add_adhoc (*rs, rs->root_extra->export_file);
// The same for subprojects that have been loaded.
//
@@ -238,7 +238,7 @@ namespace build2
if (!nrs.src_path ().sub (src_root)) // Not a strong amalgamation.
continue;
- add_adhoc (nrs, export_file);
+ add_adhoc (nrs, nrs.root_extra->export_file);
}
}
diff --git a/build2/file.cxx b/build2/file.cxx
index 7f51ebb..e205f27 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -24,23 +24,37 @@ using namespace butl;
namespace build2
{
- const dir_path build_dir ("build");
- const dir_path root_dir (dir_path (build_dir) /= "root");
- const dir_path bootstrap_dir (dir_path (build_dir) /= "bootstrap");
-
- const path root_file (build_dir / "root.build");
- const path bootstrap_file (build_dir / "bootstrap.build");
- const path src_root_file (bootstrap_dir / "src-root.build");
- const path out_root_file (bootstrap_dir / "out-root.build");
- const path export_file (build_dir / "export.build");
-
- // While strictly speaking it belongs in, say, config/module.cxx, the static
- // initialization order strikes again. If we ever make the config module
- // loadable, then we can move it there.
+ // Standard and alternative build file/directory naming schemes.
//
- const path config_file (build_dir / "config.build");
+ const dir_path std_build_dir ("build");
+ const dir_path std_root_dir (dir_path (std_build_dir) /= "root");
+ const dir_path std_bootstrap_dir (dir_path (std_build_dir) /= "bootstrap");
- const path buildfile_file ("buildfile");
+ const path std_root_file (std_build_dir / "root.build");
+ const path std_bootstrap_file (std_build_dir / "bootstrap.build");
+ const path std_src_root_file (std_bootstrap_dir / "src-root.build");
+ const path std_out_root_file (std_bootstrap_dir / "out-root.build");
+ const path std_export_file (std_build_dir / "export.build");
+
+ const string std_build_ext ("build");
+ const path std_buildfile_file ("buildfile");
+ const path std_buildignore_file (".buildignore");
+
+ //
+
+ const dir_path alt_build_dir ("build2");
+ const dir_path alt_root_dir (dir_path (alt_build_dir) /= "root");
+ const dir_path alt_bootstrap_dir (dir_path (alt_build_dir) /= "bootstrap");
+
+ const path alt_root_file (alt_build_dir / "root.build2");
+ const path alt_bootstrap_file (alt_build_dir / "bootstrap.build2");
+ const path alt_src_root_file (alt_bootstrap_dir / "src-root.build2");
+ const path alt_out_root_file (alt_bootstrap_dir / "out-root.build2");
+ const path alt_export_file (alt_build_dir / "export.build2");
+
+ const string alt_build_ext ("build2");
+ const path alt_buildfile_file ("build2file");
+ const path alt_buildignore_file (".build2ignore");
ostream&
operator<< (ostream& os, const subprojects& sps)
@@ -60,26 +74,61 @@ namespace build2
return os;
}
+ // Check if the standard/alternative file/directory exists, returning empty
+ // path if it does not.
+ //
+ template <typename T>
+ static T
+ exists (const dir_path& d, const T& s, const T& a, optional<bool>& altn)
+ {
+ T p;
+ bool e;
+
+ if (altn)
+ {
+ p = d / (*altn ? a : s);
+ e = exists (p);
+ }
+ else
+ {
+ // Check the alternative name first since it is more specific.
+ //
+ p = d / a;
+
+ if ((e = exists (p)))
+ altn = true;
+ else
+ {
+ p = d / s;
+
+ if ((e = exists (p)))
+ altn = false;
+ }
+ }
+
+ return e ? p : T ();
+ }
+
bool
- is_src_root (const dir_path& d)
+ is_src_root (const dir_path& d, optional<bool>& altn)
{
- // We can't have root without bootstrap.
+ // We can't have root without bootstrap.build.
//
- return exists (d / bootstrap_file);
+ return !exists (d, std_bootstrap_file, alt_bootstrap_file, altn).empty ();
}
bool
- is_out_root (const dir_path& d)
+ is_out_root (const dir_path& d, optional<bool>& altn)
{
- return exists (d / src_root_file);
+ return !exists (d, std_src_root_file, alt_src_root_file, altn).empty ();
}
dir_path
- find_src_root (const dir_path& b)
+ find_src_root (const dir_path& b, optional<bool>& altn)
{
for (dir_path d (b); !d.root () && d != home; d = d.directory ())
{
- if (is_src_root (d))
+ if (is_src_root (d, altn))
return d;
}
@@ -87,12 +136,12 @@ namespace build2
}
pair<dir_path, bool>
- find_out_root (const dir_path& b)
+ find_out_root (const dir_path& b, optional<bool>& altn)
{
for (dir_path d (b); !d.root () && d != home; d = d.directory ())
{
bool s;
- if ((s = is_src_root (d)) || is_out_root (d))
+ if ((s = is_src_root (d, altn)) || is_out_root (d, altn))
return make_pair (move (d), s);
}
@@ -167,17 +216,12 @@ namespace build2
}
// Source (once) pre-*.build (pre is true) or post-*.build (otherwise) hooks
- // from the specified subdirectory (build/bootstrap/ or build/root/) of
- // out_root/.
+ // from the specified directory (build/{bootstrap,root}/ of out_root) which
+ // must exist.
//
- void
- source_hooks (scope& root, const dir_path& sd, bool pre)
+ static void
+ source_hooks (scope& root, const dir_path& d, bool pre)
{
- dir_path d (root.out_path () / sd);
-
- if (!exists (d))
- return;
-
// While we could have used the wildcard pattern matching functionality,
// our needs are pretty basic and performance is quite important, so let's
// handle this ourselves.
@@ -196,7 +240,7 @@ namespace build2
if (n.string ().compare (0,
pre ? 4 : 5,
pre ? "pre-" : "post-") != 0 ||
- n.extension () != "build")
+ n.extension () != root.root_extra->build_ext)
continue;
path f (d / n);
@@ -387,20 +431,20 @@ namespace build2
}
dir_path
- bootstrap_fwd (const dir_path& src_root)
+ bootstrap_fwd (const dir_path& src_root, optional<bool>& altn)
{
- // We cannot just source the buildfile since there is no scope to do
- // this on yet.
- //
- path bf (src_root / out_root_file);
+ path f (exists (src_root, std_out_root_file, alt_out_root_file, altn));
- if (!exists (bf))
+ if (f.empty ())
return src_root;
- auto p (extract_variable (bf, *var_out_root));
+ // We cannot just source the buildfile since there is no scope to do
+ // this on yet.
+ //
+ auto p (extract_variable (f, *var_out_root));
if (!p.second)
- fail << "variable out_root expected as first line in " << bf;
+ fail << "variable out_root expected as first line in " << f;
try
{
@@ -408,25 +452,50 @@ namespace build2
}
catch (const invalid_argument& e)
{
- fail << "invalid out_root value in " << bf << ": " << e << endf;
+ fail << "invalid out_root value in " << f << ": " << e << endf;
}
}
+ static void
+ setup_root_extra (scope& root, optional<bool>& altn)
+ {
+ assert (altn && root.root_extra == nullptr);
+ bool a (*altn);
+
+ root.root_extra = unique_ptr<scope::root_data> (
+ new scope::root_data {
+ a,
+ a ? alt_build_ext : std_build_ext,
+ a ? alt_build_dir : std_build_dir,
+ a ? alt_buildfile_file : std_buildfile_file,
+ a ? alt_buildignore_file : std_buildignore_file,
+ a ? alt_root_dir : std_root_dir,
+ a ? alt_bootstrap_dir : std_bootstrap_dir,
+ a ? alt_root_file : std_root_file,
+ a ? alt_export_file : std_export_file,
+ a ? alt_src_root_file : std_src_root_file,
+ a ? alt_out_root_file : std_out_root_file});
+ }
+
void
- bootstrap_out (scope& root)
+ bootstrap_out (scope& root, optional<bool>& altn)
{
- path bf (root.out_path () / src_root_file);
+ const dir_path& out_root (root.out_path ());
- if (!exists (bf))
+ path f (exists (out_root, std_src_root_file, alt_src_root_file, altn));
+
+ if (f.empty ())
return;
- //@@ TODO: if bootstrap files can source other bootstrap files
- // (the way to express dependecies), then we need a way to
- // prevent multiple sourcing. We handle it here but we still
- // need something like source_once (once [scope] source) in
- // buildfiles.
+ if (root.root_extra == nullptr)
+ setup_root_extra (root, altn);
+
+ //@@ TODO: if bootstrap files can source other bootstrap files (for
+ // example, as a way to express dependecies), then we need a way to
+ // prevent multiple sourcing. We handle it here but we still need
+ // something like source_once (once [scope] source) in buildfiles.
//
- source_once (root, root, bf);
+ source_once (root, root, f);
}
pair<value, bool>
@@ -470,7 +539,8 @@ namespace build2
static project_name
find_project_name (const dir_path& out_root,
const dir_path& fallback_src_root,
- bool* src_hint = nullptr)
+ optional<bool> out_src, // True if out_root is src_root.
+ optional<bool>& altn)
{
tracer trace ("find_project_name");
@@ -482,6 +552,14 @@ namespace build2
if (s.root_scope () == &s && s.out_path () == out_root)
{
+ if (s.root_extra != nullptr)
+ {
+ if (!altn)
+ altn = s.root_extra->altn;
+ else
+ assert (*altn == s.root_extra->altn);
+ }
+
if (lookup l = s.vars[var_project])
return cast<project_name> (l);
@@ -496,14 +574,22 @@ namespace build2
if (src_root == nullptr)
{
- if (src_hint != nullptr ? *src_hint : is_src_root (out_root))
+ if (out_src ? *out_src : is_src_root (out_root, altn))
src_root = &out_root;
else
{
- path f (out_root / src_root_file);
+ path f (exists (out_root, std_src_root_file, alt_src_root_file, altn));
+
+ if (f.empty ())
+ {
+ // Note: the same diagnostics as in main().
+ //
+ if (fallback_src_root.empty ())
+ fail << "no bootstrapped src_root for " << out_root <<
+ info << "consider reconfiguring this out_root";
- if (!fallback_src_root.empty () && !exists (f))
src_root = &fallback_src_root;
+ }
else
{
auto p (extract_variable (f, *var_src_root));
@@ -523,7 +609,11 @@ namespace build2
project_name name;
{
- path f (*src_root / bootstrap_file);
+ path f (exists (*src_root, std_bootstrap_file, alt_bootstrap_file, altn));
+
+ if (f.empty ())
+ fail << "no build/bootstrap.build in " << *src_root;
+
auto p (extract_variable (f, *var_project));
if (!p.second)
@@ -559,7 +649,10 @@ namespace build2
dir_path sd (d / path_cast<dir_path> (de.path ()));
bool src (false);
- if (!((out && is_out_root (sd)) || (src = is_src_root (sd))))
+ optional<bool> altn;
+
+ if (!((out && is_out_root (sd, altn)) ||
+ (src = is_src_root (sd, altn))))
{
// We used to scan for subproject recursively but this is probably
// too loose (think of some tests laying around). In the future we
@@ -579,7 +672,7 @@ namespace build2
// Load its name. Note that here we don't use fallback src_root
// since this function is used to scan both out_root and src_root.
//
- project_name name (find_project_name (sd, dir_path (), &src));
+ project_name name (find_project_name (sd, dir_path (), src, altn));
// If the name is empty, then is is an unnamed project. While the
// 'project' variable stays empty, here we come up with a surrogate
@@ -618,7 +711,7 @@ namespace build2
}
bool
- bootstrap_src (scope& root)
+ bootstrap_src (scope& root, optional<bool>& altn)
{
tracer trace ("bootstrap_src");
@@ -627,21 +720,33 @@ namespace build2
const dir_path& out_root (root.out_path ());
const dir_path& src_root (root.src_path ());
- path bf (src_root / bootstrap_file);
+ {
+ path f (exists (src_root, std_bootstrap_file, alt_bootstrap_file, altn));
+
+ if (!f.empty ())
+ {
+ // We assume that bootstrap out cannot load this file explicitly. It
+ // feels wrong to allow this since that makes the whole bootstrap
+ // process hard to reason about. But we may try to bootstrap the same
+ // root scope multiple time.
+ //
+ if (root.buildfiles.insert (f).second)
+ source (root, root, f, true);
+ else
+ l5 ([&]{trace << "skipping already sourced " << f;});
+
+ r = true;
+ }
+ }
- if (exists (bf))
+ if (root.root_extra == nullptr)
{
- // We assume that bootstrap out cannot load this file explicitly. It
- // feels wrong to allow this since that makes the whole bootstrap
- // process hard to reason about. But we may try to bootstrap the
- // same root scope multiple time.
+ // If nothing so far has indicated the naming, assume standard.
//
- if (root.buildfiles.insert (bf).second)
- source (root, root, bf, true);
- else
- l5 ([&]{trace << "skipping already sourced " << bf;});
+ if (!altn)
+ altn = false;
- r = true;
+ setup_root_extra (root, altn);
}
// See if we are a part of an amalgamation. There are two key players: the
@@ -704,7 +809,8 @@ namespace build2
// outer directories is a project's out_root. If so, then
// that's our amalgamation.
//
- const dir_path& ad (find_out_root (out_root.directory ()).first);
+ optional<bool> altn;
+ const dir_path& ad (find_out_root (out_root.directory (), altn).first);
if (!ad.empty ())
{
@@ -816,11 +922,15 @@ namespace build2
//
if (n.empty ())
{
- // Pass fallback src_root since this is a subproject that
- // was specified by the user so it is most likely in our
- // src.
+ optional<bool> altn;
+
+ // Pass fallback src_root since this is a subproject that was
+ // specified by the user so it is most likely in our src.
//
- n = find_project_name (out_root / d, src_root / d);
+ n = find_project_name (out_root / d,
+ src_root / d,
+ nullopt /* out_src */,
+ altn);
// See find_subprojects() for details on unnamed projects.
//
@@ -842,6 +952,40 @@ namespace build2
return r;
}
+ void
+ bootstrap_pre (scope& root, optional<bool>& altn)
+ {
+ const dir_path& out_root (root.out_path ());
+
+ // This test is a bit loose in a sense that there can be a stray
+ // build/bootstrap/ directory that will make us mis-treat a project as
+ // following the standard naming scheme (the other way, while also
+ // possible, is a lot less likely). If this does becomes a problem, we can
+ // always tighten the test by also looking for a hook file with the
+ // correct extension.
+ //
+ dir_path d (exists (out_root, std_bootstrap_dir, alt_bootstrap_dir, altn));
+
+ if (!d.empty ())
+ {
+ if (root.root_extra == nullptr)
+ setup_root_extra (root, altn);
+
+ source_hooks (root, d, true /* pre */);
+ }
+ }
+
+ void
+ bootstrap_post (scope& root)
+ {
+ const dir_path& out_root (root.out_path ());
+
+ dir_path d (out_root / root.root_extra->bootstrap_dir);
+
+ if (exists (d))
+ source_hooks (root, d, false /* pre */);
+ }
+
bool
bootstrapped (scope& root)
{
@@ -859,7 +1003,8 @@ namespace build2
static inline bool
forwarded (const scope& orig,
const dir_path& out_root,
- const dir_path& src_root)
+ const dir_path& src_root,
+ optional<bool>& altn)
{
// The conditions are:
//
@@ -871,7 +1016,7 @@ namespace build2
//
return (out_root != src_root &&
cast_false<bool> (orig.vars[var_forwarded]) &&
- bootstrap_fwd (src_root) == out_root);
+ bootstrap_fwd (src_root, altn) == out_root);
}
void
@@ -900,15 +1045,16 @@ namespace build2
bool bstrapped (bootstrapped (rs));
+ optional<bool> altn;
if (!bstrapped)
{
- bootstrap_out (rs); // #3 happens here (or it can be #1).
+ bootstrap_out (rs, altn); // #3 happens here (or it can be #1).
value& v (rs.assign (var_src_root));
if (!v)
{
- if (is_src_root (out_root)) // #2
+ if (is_src_root (out_root, altn)) // #2
v = out_root;
else // #1
{
@@ -920,14 +1066,16 @@ namespace build2
else
remap_src_root (v); // Remap if inside old_src_root.
- setup_root (rs, forwarded (root, out_root, v.as<dir_path> ()));
- bootstrap_pre (rs);
- bootstrap_src (rs);
+ setup_root (rs, forwarded (root, out_root, v.as<dir_path> (), altn));
+ bootstrap_pre (rs, altn);
+ bootstrap_src (rs, altn);
// bootstrap_post() delayed until after create_bootstrap_outer().
}
else
{
- if (forwarded (root, rs.out_path (), rs.src_path ()))
+ altn = rs.root_extra->altn;
+
+ if (forwarded (root, rs.out_path (), rs.src_path (), altn))
rs.assign (var_forwarded) = true; // Only upgrade (see main()).
}
@@ -960,29 +1108,31 @@ namespace build2
//
scope& rs (create_root (root, out_root, dir_path ())->second);
+ optional<bool> altn;
if (!bootstrapped (rs))
{
- bootstrap_out (rs);
+ bootstrap_out (rs, altn);
value& v (rs.assign (var_src_root));
if (!v)
{
- v = is_src_root (out_root)
+ v = is_src_root (out_root, altn)
? out_root
: (root.src_path () / p.second);
}
else
remap_src_root (v); // Remap if inside old_src_root.
- setup_root (rs, forwarded (root, out_root, v.as<dir_path> ()));
- bootstrap_pre (rs);
- bootstrap_src (rs);
+ setup_root (rs, forwarded (root, out_root, v.as<dir_path> (), altn));
+ bootstrap_pre (rs, altn);
+ bootstrap_src (rs, altn);
bootstrap_post (rs);
}
else
{
- if (forwarded (root, rs.out_path (), rs.src_path ()))
+ altn = rs.root_extra->altn;
+ if (forwarded (root, rs.out_path (), rs.src_path (), altn))
rs.assign (var_forwarded) = true; // Only upgrade (see main()).
}
@@ -1008,12 +1158,15 @@ namespace build2
{
tracer trace ("load_root");
+ const dir_path& out_root (root.out_path ());
+ const dir_path& src_root (root.src_path ());
+
// As an optimization, check if we have already loaded root.build. If
// that's the case, then we have already been called for this project.
//
- path bf (root.src_path () / root_file);
+ path f (src_root / root.root_extra->root_file);
- if (root.buildfiles.find (bf) != root.buildfiles.end ())
+ if (root.buildfiles.find (f) != root.buildfiles.end ())
return;
// First load outer roots, if any.
@@ -1047,9 +1200,12 @@ namespace build2
// however, that one can probably achieve adequate pre-modules behavior
// with a post-bootstrap hook.
//
- source_hooks (root, root_dir, true /* pre */);
- if (exists (bf)) source_once (root, root, bf);
- source_hooks (root, root_dir, false /* pre */);
+ dir_path hd (out_root / root.root_extra->root_dir);
+ bool he (exists (hd));
+
+ if (he) source_hooks (root, hd, true /* pre */);
+ if (exists (f)) source_once (root, root, f);
+ if (he) source_hooks (root, hd, false /* pre */);
}
scope&
@@ -1066,10 +1222,11 @@ namespace build2
if (!bootstrapped (rs))
{
- bootstrap_out (rs);
+ optional<bool> altn;
+ bootstrap_out (rs, altn);
setup_root (rs, forwarded);
- bootstrap_pre (rs);
- bootstrap_src (rs);
+ bootstrap_pre (rs, altn);
+ bootstrap_src (rs, altn);
bootstrap_post (rs);
}
else
@@ -1267,10 +1424,11 @@ namespace build2
// create_bootstrap_inner().
//
bool fwd (false);
- if (is_src_root (out_root))
+ optional<bool> altn;
+ if (is_src_root (out_root, altn))
{
src_root = move (out_root);
- out_root = bootstrap_fwd (src_root);
+ out_root = bootstrap_fwd (src_root, altn);
fwd = (src_root != out_root);
}
@@ -1284,7 +1442,7 @@ namespace build2
if (!bstrapped)
{
- bootstrap_out (*root);
+ bootstrap_out (*root, altn);
// Check that the bootstrap process set src_root.
//
@@ -1305,20 +1463,24 @@ namespace build2
fail (loc) << "unable to determine src_root for imported " << proj <<
info << "consider configuring " << out_root;
- setup_root (
- *root,
- top ? fwd : forwarded (*proot, out_root, l->as<dir_path> ()));
- bootstrap_pre (*root);
- bootstrap_src (*root);
+ setup_root (*root,
+ (top
+ ? fwd
+ : forwarded (*proot, out_root, l->as<dir_path> (), altn)));
+
+ bootstrap_pre (*root, altn);
+ bootstrap_src (*root, altn);
if (!top)
bootstrap_post (*root);
}
else
{
+ altn = root->root_extra->altn;
+
if (src_root.empty ())
src_root = root->src_path ();
- if (top ? fwd : forwarded (*proot, out_root, src_root))
+ if (top ? fwd : forwarded (*proot, out_root, src_root, altn))
root->assign (var_forwarded) = true; // Only upgrade (see main()).
}
@@ -1343,8 +1505,9 @@ namespace build2
if (i != m.end ())
{
const dir_path& d ((*i).second);
+ altn = nullopt;
out_root = root->out_path () / d;
- src_root = is_src_root (out_root) ? out_root : dir_path ();
+ src_root = is_src_root (out_root, altn) ? out_root : dir_path ();
continue;
}
}
@@ -1380,7 +1543,7 @@ namespace build2
// stub will normally switch to the imported root scope at some
// point.
//
- path es (root->src_path () / export_file);
+ path es (root->src_path () / root->root_extra->export_file);
try
{
diff --git a/build2/file.hxx b/build2/file.hxx
index bfbf097..c46d2d5 100644
--- a/build2/file.hxx
+++ b/build2/file.hxx
@@ -24,24 +24,26 @@ namespace build2
ostream&
operator<< (ostream&, const subprojects&); // Print as name@dir sequence.
- extern const dir_path build_dir; // build/
- extern const dir_path root_dir; // build/root/
- extern const dir_path bootstrap_dir; // build/bootstrap/
+ extern const dir_path std_build_dir; // build/
+ extern const path std_root_file; // build/root.build
+ extern const path std_bootstrap_file; // build/bootstrap.build
- extern const path root_file; // build/root.build
- extern const path bootstrap_file; // build/bootstrap.build
- extern const path src_root_file; // build/bootstrap/src-root.build
- extern const path out_root_file; // build/bootstrap/out-root.build
- extern const path export_file; // build/export.build
- extern const path config_file; // build/config.build
-
- extern const path buildfile_file; // buildfile
+ extern const path std_buildfile_file; // buildfile
+ extern const path alt_buildfile_file; // build2file
+ // If the altn argument value is present, then it indicates whether we are
+ // using the standard or the alternative build file/directory naming.
+ //
+ // The overall plan is to run various "file exists" tests using the standard
+ // and the alternative names. The first test that succeeds determines the
+ // naming scheme (by setting altn) and from then on all the remaining tests
+ // only look for things in this scheme.
+ //
bool
- is_src_root (const dir_path&);
+ is_src_root (const dir_path&, optional<bool>& altn);
bool
- is_out_root (const dir_path&);
+ is_out_root (const dir_path&, optional<bool>& altn);
// Given an src_base directory, look for a project's src_root based on the
// presence of known special files. Return empty path if not found. Note
@@ -49,7 +51,7 @@ namespace build2
// well.
//
dir_path
- find_src_root (const dir_path&);
+ find_src_root (const dir_path&, optional<bool>& altn);
// The same as above but for project's out. Note that we also check whether
// a directory happens to be src_root, in case this is an in-tree build with
@@ -57,7 +59,7 @@ namespace build2
// input is normalized/actualized, then the output will be as well.
//
pair<dir_path, bool>
- find_out_root (const dir_path&);
+ find_out_root (const dir_path&, optional<bool>& altn);
// The old/new src_root paths. See main() (where they are set) for details.
//
@@ -126,21 +128,22 @@ namespace build2
bool load = true);
// Bootstrap the project's forward. Return the forwarded-to out_root or
- // src_root if there is no forward.
+ // src_root if there is no forward. See is_{src,out}_root() for the altn
+ // argument semantics.
//
dir_path
- bootstrap_fwd (const dir_path& src_root);
+ bootstrap_fwd (const dir_path& src_root, optional<bool>& altn);
// Bootstrap the project's root scope, the out part.
//
void
- bootstrap_out (scope& root);
+ bootstrap_out (scope& root, optional<bool>& altn);
// Bootstrap the project's root scope, the src part. Return true if we
// loaded anything (which confirms the src_root is not bogus).
//
bool
- bootstrap_src (scope& root);
+ bootstrap_src (scope& root, optional<bool>& altn);
// Return true if this scope has already been bootstrapped, that is, the
// following calls have already been made:
@@ -156,7 +159,7 @@ namespace build2
// only be called once per project bootstrap.
//
void
- bootstrap_pre (scope& root);
+ bootstrap_pre (scope& root, optional<bool>& altn);
void
bootstrap_post (scope& root);
diff --git a/build2/file.ixx b/build2/file.ixx
index 2d6e99d..a94d605 100644
--- a/build2/file.ixx
+++ b/build2/file.ixx
@@ -26,19 +26,4 @@ namespace build2
assert (phase == run_phase::match || phase == run_phase::execute);
return import (pk, true);
}
-
- void
- source_hooks (scope&, const dir_path&, bool);
-
- inline void
- bootstrap_pre (scope& root)
- {
- source_hooks (root, bootstrap_dir, true /* pre */);
- }
-
- inline void
- bootstrap_post (scope& root)
- {
- source_hooks (root, bootstrap_dir, false /* pre */);
- }
}
diff --git a/build2/filesystem.cxx b/build2/filesystem.cxx
index fc34d4d..97f540b 100644
--- a/build2/filesystem.cxx
+++ b/build2/filesystem.cxx
@@ -190,17 +190,15 @@ namespace build2
}
}
- const path buildignore_file (".buildignore");
-
fs_status<mkdir_status>
- mkdir_buildignore (const dir_path& d, uint16_t verbosity)
+ mkdir_buildignore (const dir_path& d, const path& n, uint16_t verbosity)
{
fs_status<mkdir_status> r (mkdir (d, verbosity));
// Create the .buildignore file if the directory was created (and so is
// empty) or the file doesn't exist.
//
- path p (d / buildignore_file);
+ path p (d / n);
if (r || !exists (p))
touch (p, true /* create */, verbosity);
@@ -208,7 +206,7 @@ namespace build2
}
bool
- empty_buildignore (const dir_path& d)
+ empty_buildignore (const dir_path& d, const path& n)
{
try
{
@@ -217,8 +215,7 @@ namespace build2
// The .buildignore filesystem entry should be of the regular file
// type.
//
- if (de.path () != buildignore_file ||
- de.ltype () != entry_type::regular)
+ if (de.path () != n || de.ltype () != entry_type::regular)
return false;
}
}
@@ -231,7 +228,7 @@ namespace build2
}
fs_status<rmdir_status>
- rmdir_buildignore (const dir_path& d, uint16_t verbosity)
+ rmdir_buildignore (const dir_path& d, const path& n, uint16_t verbosity)
{
// We should remove the .buildignore file only if the subsequent rmdir()
// will succeed. In other words if the directory stays after the function
@@ -239,13 +236,13 @@ namespace build2
// first check that the directory is otherwise empty and doesn't contain
// the working directory.
//
- path p (d / buildignore_file);
- if (exists (p) && empty_buildignore (d) && !work.sub (d))
+ path p (d / n);
+ if (exists (p) && empty_buildignore (d, n) && !work.sub (d))
rmfile (p, verbosity);
- // Note that in case of a system error the directory is likely to stay and
- // the .buildfile is already removed. Trying to restore it feels like an
- // overkill here.
+ // Note that in case of a system error the directory is likely to stay with
+ // the .buildignore file already removed. Trying to restore it feels like
+ // an overkill here.
//
return rmdir (d, verbosity);
}
diff --git a/build2/filesystem.hxx b/build2/filesystem.hxx
index b575c4a..ccc5a73 100644
--- a/build2/filesystem.hxx
+++ b/build2/filesystem.hxx
@@ -129,27 +129,25 @@ namespace build2
bool
empty (const dir_path&);
- // Directories containing .buildignore file are automatically ignored by
- // recursive names patterns. For now the file is just a marker and its
- // contents don't matter.
- //
- extern const path buildignore_file; // .buildignore
+ // Directories containing .buildignore (or .build2ignore in the alternative
+ // naming scheme) file are automatically ignored by recursive name patterns.
+ // For now the file is just a marker and its contents don't matter.
// Create a directory containing an empty .buildignore file.
//
fs_status<mkdir_status>
- mkdir_buildignore (const dir_path&, uint16_t verbosity = 1);
+ mkdir_buildignore (const dir_path&, const path&, uint16_t verbosity = 1);
// Return true if the directory is empty or only contains the .buildignore
// file. Fail if the directory doesn't exist.
//
bool
- empty_buildignore (const dir_path&);
+ empty_buildignore (const dir_path&, const path&);
// Remove a directory if it is empty or only contains the .buildignore file.
//
fs_status<rmdir_status>
- rmdir_buildignore (const dir_path&, uint16_t verbosity = 1);
+ rmdir_buildignore (const dir_path&, const path&, uint16_t verbosity = 1);
}
#include <build2/filesystem.txx>
diff --git a/build2/parser.cxx b/build2/parser.cxx
index f703e90..ef6691d 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -1236,15 +1236,24 @@ namespace build2
// 'buildfile'.
//
path p (move (n.dir));
+
+ bool a;
if (n.value.empty ())
- p /= buildfile_file;
+ a = true;
else
{
- bool d (path::traits::is_separator (n.value.back ()));
-
+ a = path::traits::is_separator (n.value.back ());
p /= path (move (n.value));
- if (d)
- p /= buildfile_file;
+ }
+
+ if (a)
+ {
+ // This shouldn't happen but let's make sure.
+ //
+ if (root_->root_extra == nullptr)
+ fail (l) << "build file naming scheme is not yet known";
+
+ p /= root_->root_extra->buildfile_file;
}
l6 ([&]{trace (l) << "relative path " << p;});
@@ -3228,15 +3237,21 @@ namespace build2
include_match (move (v), move (e), a);
};
- auto process = [&e, &appf, sp] (path&& m, const string& p, bool interm)
+ auto process = [this, &e, &appf, sp] (path&& m,
+ const string& p,
+ bool interm)
{
// Ignore entries that start with a dot unless the pattern that
// matched them also starts with a dot. Also ignore directories
- // containing the .buildignore file.
+ // containing the .buildignore file (ignoring the test if we don't
+ // have a sufficiently setup project root).
//
const string& s (m.string ());
if ((p[0] != '.' && s[path::traits::find_leaf (s)] == '.') ||
- (m.to_directory () && exists (*sp / m / buildignore_file)))
+ (root_ != nullptr &&
+ root_->root_extra != nullptr &&
+ m.to_directory () &&
+ exists (*sp / m / root_->root_extra->buildignore_file)))
return !interm;
// Note that we have to make copies of the extension since there will
diff --git a/build2/scope.hxx b/build2/scope.hxx
index 3bb7269..fe7b0b6 100644
--- a/build2/scope.hxx
+++ b/build2/scope.hxx
@@ -279,6 +279,31 @@ namespace build2
public:
loaded_module_map modules; // Only on root scope.
+ // Extra root scope-only data.
+ //
+ public:
+ struct root_data
+ {
+ bool altn; // True if using alternative build file/directory naming.
+
+ // Build file/directory naming scheme used by this project.
+ //
+ const string& build_ext; // build or build2 (no dot)
+ const dir_path& build_dir; // build/ or build2/
+ const path& buildfile_file; // buildfile or build2file
+ const path& buildignore_file; // buildignore or build2ignore
+
+ const dir_path& root_dir; // build[2]/root/
+ const dir_path& bootstrap_dir; // build[2]/bootstrap/
+
+ const path& root_file; // build[2]/root.build[2]
+ const path& export_file; // build[2]/export.build[2]
+ const path& src_root_file; // build[2]/bootstrap/src-root.build[2]
+ const path& out_root_file; // build[2]/bootstrap/src-root.build[2]
+ };
+
+ unique_ptr<root_data> root_extra;
+
public:
// RW access.
//
diff --git a/build2/search.cxx b/build2/search.cxx
index 05e2680..c4b9ae3 100644
--- a/build2/search.cxx
+++ b/build2/search.cxx
@@ -115,7 +115,7 @@ namespace build2
if (!ext)
{
if (auto f = ctk.type->fixed_extension)
- ext = f (ctk);
+ ext = f (ctk, s->root_scope ());
else if (auto f = ctk.type->default_extension)
ext = f (ctk, *s, nullptr, true);
diff --git a/build2/target-key.hxx b/build2/target-key.hxx
index 4877bbd..ed9a0ed 100644
--- a/build2/target-key.hxx
+++ b/build2/target-key.hxx
@@ -50,8 +50,16 @@ namespace build2
return !x.ext || !y.ext || *x.ext == *y.ext;
else
{
- const char* xe (x.ext ? x.ext->c_str () : tt.fixed_extension (x));
- const char* ye (y.ext ? y.ext->c_str () : tt.fixed_extension (y));
+ // Note that for performance reasons here we use the specified extension
+ // without calling fixed_extension().
+ //
+ const char* xe (x.ext
+ ? x.ext->c_str ()
+ : tt.fixed_extension (x, nullptr /* root scope */));
+
+ const char* ye (y.ext
+ ? y.ext->c_str ()
+ : tt.fixed_extension (y, nullptr /* root scope */));
return strcmp (xe, ye) == 0;
}
diff --git a/build2/target-type.hxx b/build2/target-type.hxx
index d9e53a5..aec1bcf 100644
--- a/build2/target-type.hxx
+++ b/build2/target-type.hxx
@@ -25,14 +25,19 @@ namespace build2
//
// If the extension derivation functions are NULL, then it means this target
// type does not use extensions. Note that this is relied upon when deciding
- // whether to print the extension; if the target does use extensions but the
- // defaults couldn't (and its ok), couldn't (and its not ok), or shouldn't
- // (logically) be obtained, then use target_extension_{null,fail,assert}(),
- // respectively. The fixed extension function should return the fixed
- // extension (which can point to the key's ext member if the explicit
- // extension specificaton is allowed). If the default extension function
- // returns NULL, then it means the default extension for this target could
- // not be derived.
+ // whether to print the extension.
+ //
+ // The fixed extension function should return the fixed extension (which can
+ // point to the key's ext member; note that for performance reasons we
+ // currently only verify the explicitly specified extension on target
+ // insersion -- see target_key comparison for details).
+ //
+ // The root scope argument to the fixed extension function may be NULL which
+ // means the root scope is not known. A target type that relies on this must
+ // be prepared to resolve the root scope itself and handle the cases where
+ // the target is not (yet) in any project (this is currently only used to
+ // handle the alternative build file/directory naming scheme and hopefully
+ // it will stay that way).
//
// The default extension is used in two key (there are others) places:
// search_existing_file() (called for a prerequisite with the last argument
@@ -41,7 +46,8 @@ namespace build2
// The third argument is the default extension that is supplied (e.g., by a
// rule) to derive_extension(), if any. The implementation can decide which
// takes precedence, etc (see the exe{} target type for some interesting
- // logic).
+ // logic). If the default extension function returns NULL, then it means the
+ // default extension for this target could not be derived.
//
// If the pattern function is not NULL, then it is used to amend a pattern
// or match (reverse is false) and then, if the amendment call returned
@@ -56,14 +62,15 @@ namespace build2
target* (*factory) (const target_type&, dir_path, dir_path, string);
- const char* (*fixed_extension) (const target_key&);
+ const char* (*fixed_extension) (const target_key&,
+ const scope* root);
optional<string> (*default_extension) (const target_key&,
- const scope&,
+ const scope& base,
const char*,
bool search);
bool (*pattern) (const target_type&,
- const scope&,
+ const scope& base,
string& name,
optional<string>& extension,
const location&,
diff --git a/build2/target.cxx b/build2/target.cxx
index b3dd29d..b8e2a60 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -371,9 +371,10 @@ namespace build2
//
assert (phase != run_phase::execute);
- optional<string> e (tt.fixed_extension != nullptr
- ? string (tt.fixed_extension (tk))
- : move (tk.ext));
+ optional<string> e (
+ tt.fixed_extension != nullptr
+ ? string (tt.fixed_extension (tk, nullptr /* root scope */))
+ : move (tk.ext));
t = tt.factory (tt, move (dir), move (out), move (name));
@@ -680,19 +681,6 @@ namespace build2
return search_existing_file (pk);
}
- optional<string>
- target_extension_null (const target_key&, const scope&, const char*, bool)
- {
- return nullopt;
- }
-
- optional<string>
- target_extension_assert (const target_key&, const scope&, const char*, bool)
- {
- assert (false); // Attempt to obtain the default extension.
- throw failed ();
- }
-
void
target_print_0_ext_verb (ostream& os, const target_key& k)
{
@@ -796,7 +784,7 @@ namespace build2
// dir
//
bool dir::
- check_implied (const dir_path& d)
+ check_implied (const scope& rs, const dir_path& d)
{
try
{
@@ -806,14 +794,14 @@ namespace build2
{
case entry_type::directory:
{
- if (check_implied (d / path_cast<dir_path> (e.path ())))
+ if (check_implied (rs, d / path_cast<dir_path> (e.path ())))
return true;
break;
}
case entry_type::regular:
{
- if (e.path () == buildfile_file)
+ if (e.path () == rs.root_extra->buildfile_file)
return true;
break;
@@ -938,7 +926,7 @@ namespace build2
const dir_path& src_base (base.src_path ());
- path bf (src_base / buildfile_file);
+ path bf (src_base / root.root_extra->buildfile_file);
if (exists (bf))
{
@@ -1091,17 +1079,43 @@ namespace build2
};
static const char*
- buildfile_target_extension (const target_key& tk)
+ buildfile_target_extension (const target_key& tk, const scope* root)
{
- // If the name is special 'buildfile', then there is no extension,
- // otherwise it is .build.
+ // If the name is the special 'buildfile', then there is no extension,
+ // otherwise it is 'build' (or 'build2file' and 'build2' in the
+ // alternative naming scheme).
+
+ // Let's try hard not to need the root scope by trusting the extensions
+ // we were given.
+ //
+ // BTW, one way to get rid of all this root scope complication is to
+ // always require explicit extension specification for buildfiles. Since
+ // they are hardly ever mentioned explicitly, this should probably be ok.
//
- return *tk.name == "buildfile" ? "" : "build";
+ if (tk.ext)
+ return tk.ext->c_str ();
+
+ if (root == nullptr)
+ {
+ // The same login as in target::root_scope().
+ //
+ // Note: we are guaranteed the scope is never NULL for prerequisites
+ // (where out/dir could be relative and none of this will work).
+ //
+ root = scopes.find (tk.out->empty () ? *tk.dir : *tk.out).root_scope ();
+
+ if (root == nullptr || root->root_extra == nullptr)
+ fail << "unable to determine extension for buildfile target " << tk;
+ }
+
+ return *tk.name == root->root_extra->buildfile_file.string ()
+ ? ""
+ : root->root_extra->build_ext.c_str ();
}
static bool
buildfile_target_pattern (const target_type&,
- const scope&,
+ const scope& base,
string& v,
optional<string>& e,
const location& l,
@@ -1116,10 +1130,18 @@ namespace build2
{
e = target::split_name (v, l);
- if (!e && v != "buildfile")
+ if (!e)
{
- e = "build";
- return true;
+ const scope* root (base.root_scope ());
+
+ if (root == nullptr || root->root_extra == nullptr)
+ fail (l) << "unable to determine extension for buildfile pattern";
+
+ if (v != root->root_extra->buildfile_file.string ())
+ {
+ e = root->root_extra->build_ext;
+ return true;
+ }
}
}
@@ -1153,7 +1175,7 @@ namespace build2
};
static const char*
- man_extension (const target_key& tk)
+ man_extension (const target_key& tk, const scope*)
{
if (!tk.ext)
fail << "man target " << tk << " must include extension (man section)";
@@ -1190,7 +1212,7 @@ namespace build2
};
static const char*
- manifest_target_extension (const target_key& tk)
+ manifest_target_extension (const target_key& tk, const scope*)
{
// If the name is special 'manifest', then there is no extension,
// otherwise it is .manifest.
diff --git a/build2/target.hxx b/build2/target.hxx
index d2d4b1c..c86a10b 100644
--- a/build2/target.hxx
+++ b/build2/target.hxx
@@ -1669,11 +1669,12 @@ namespace build2
search_implied (const scope&, const K&, tracer&);
// Return true if the implied buildfile is plausible for the specified
- // directory, that is, there is a buildfile in at least one of its
- // subdirectories. Note that the directory must exist.
+ // subdirectory of a project with the specified root scope. That is, there
+ // is a buildfile in at least one of its subdirectories. Note that the
+ // directory must exist.
//
static bool
- check_implied (const dir_path&);
+ check_implied (const scope& root, const dir_path&);
private:
static prerequisites_type
@@ -1813,7 +1814,7 @@ namespace build2
//
template <const char* ext>
const char*
- target_extension_fix (const target_key&);
+ target_extension_fix (const target_key&, const scope*);
template <const char* ext>
bool
@@ -1834,16 +1835,6 @@ namespace build2
string&, optional<string>&, const location&,
bool);
- // Always return NULL extension.
- //
- optional<string>
- target_extension_null (const target_key&, const scope&, const char*, bool);
-
- // Assert if called.
- //
- optional<string>
- target_extension_assert (const target_key&, const scope&, const char*, bool);
-
// Target print functions.
//
diff --git a/build2/target.txx b/build2/target.txx
index eb570a0..3cc249b 100644
--- a/build2/target.txx
+++ b/build2/target.txx
@@ -43,7 +43,7 @@ namespace build2
//
template <const char* ext>
const char*
- target_extension_fix (const target_key& tk)
+ target_extension_fix (const target_key& tk, const scope*)
{
// A generic file target type doesn't imply any extension while a very
// specific one (say man1) may have a fixed extension. So if one wasn't
diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx
index 2ff7ebf..941609d 100644
--- a/build2/test/rule.cxx
+++ b/build2/test/rule.cxx
@@ -437,9 +437,11 @@ namespace build2
// backlink_*() in algorithm.cxx for details.)
//
const scope& bs (t.base_scope ());
+ const scope& rs (*bs.root_scope ());
+ const path& buildignore_file (rs.root_extra->buildignore_file);
dir_path bl;
- if (cast_false<bool> (bs.root_scope ()->vars[var_forwarded]))
+ if (cast_false<bool> (rs.vars[var_forwarded]))
{
bl = bs.src_path () / wd.leaf (bs.out_path ());
clean_backlink (bl, verb_never);
@@ -471,10 +473,11 @@ namespace build2
bool fail (before == output_before::fail);
(fail ? error : warn) << "working directory " << wd << " exists "
- << (empty_buildignore (wd)
+ << (empty_buildignore (wd, buildignore_file)
? ""
: "and is not empty ")
<< "at the beginning of the test";
+
if (fail)
throw failed ();
}
@@ -513,7 +516,7 @@ namespace build2
{
if (mk)
{
- mkdir_buildignore (wd, 2);
+ mkdir_buildignore (wd, buildignore_file, 2);
mk = false;
}
@@ -568,11 +571,11 @@ namespace build2
//
if (!bad && !one && !mk && after == output_after::clean)
{
- if (!empty_buildignore (wd))
+ if (!empty_buildignore (wd, buildignore_file))
fail << "working directory " << wd << " is not empty at the "
<< "end of the test";
- rmdir_buildignore (wd, 2);
+ rmdir_buildignore (wd, buildignore_file, 2);
}
// Backlink if the working directory exists.
diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx
index c881031..0d3716f 100644
--- a/build2/test/script/runner.cxx
+++ b/build2/test/script/runner.cxx
@@ -719,9 +719,13 @@ namespace build2
// alike utility functions so the failure message can contain
// location info?
//
- fs_status<mkdir_status> r (sp.parent == nullptr
- ? mkdir_buildignore (sp.wd_path, 2)
- : mkdir (sp.wd_path, 2));
+ fs_status<mkdir_status> r (
+ sp.parent == nullptr
+ ? mkdir_buildignore (
+ sp.wd_path,
+ sp.root->target_scope.root_scope ()->root_extra->buildignore_file,
+ 2)
+ : mkdir (sp.wd_path, 2));
if (r == mkdir_status::already_exists)
fail << "working directory " << sp.wd_path << " already exists" <<
@@ -914,11 +918,15 @@ namespace build2
// a file cleanup when try to rmfile() directory instead of
// file.
//
- rmdir_status r (recursive
- ? rmdir_r (d, !wd, static_cast <uint16_t> (v))
- : wd && sp.parent == nullptr
- ? rmdir_buildignore (d, v)
- : rmdir (d, v));
+ rmdir_status r (
+ recursive
+ ? rmdir_r (d, !wd, static_cast <uint16_t> (v))
+ : (wd && sp.parent == nullptr
+ ? rmdir_buildignore (
+ d,
+ sp.root->target_scope.root_scope ()->root_extra->buildignore_file,
+ v)
+ : rmdir (d, v)));
if (r == rmdir_status::success ||
(r == rmdir_status::not_exist && t == cleanup_type::maybe))
diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx
index f2a8d03..94d6d8b 100644
--- a/build2/test/script/script.cxx
+++ b/build2/test/script/script.cxx
@@ -517,6 +517,7 @@ namespace build2
const dir_path& rwd)
: group (st.name == "testscript" ? string () : st.name, this),
test_target (tt),
+ target_scope (tt.base_scope ()),
script_target (st)
{
// Set the script working dir ($~) to $out_base/test/<id> (id_path
@@ -563,7 +564,7 @@ namespace build2
//
// @@ OUT: what if this is a @-qualified pair of names?
//
- t = search_existing (*n, tt.base_scope ());
+ t = search_existing (*n, target_scope);
if (t == nullptr)
fail << "unknown target '" << *n << "' in test variable";
@@ -653,8 +654,7 @@ namespace build2
if (p.first)
{
if (var.override != nullptr)
- p = s.test_target.base_scope ().find_override (
- var, move (p), true);
+ p = s.target_scope.find_override (var, move (p), true);
return p.first;
}
diff --git a/build2/test/script/script.hxx b/build2/test/script/script.hxx
index 479001b..4da9d97 100644
--- a/build2/test/script/script.hxx
+++ b/build2/test/script/script.hxx
@@ -536,8 +536,9 @@ namespace build2
script& operator= (const script&) = delete;
public:
- const target& test_target; // Target we are testing.
- const testscript& script_target; // Target of the testscript file.
+ const target& test_target; // Target we are testing.
+ const build2::scope& target_scope; // Base scope of test target.
+ const testscript& script_target; // Target of the testscript file.
// Pre-parse data.
//
diff --git a/build2/test/target.cxx b/build2/test/target.cxx
index c440325..f75b556 100644
--- a/build2/test/target.cxx
+++ b/build2/test/target.cxx
@@ -12,7 +12,7 @@ namespace build2
namespace test
{
static const char*
- testscript_target_extension (const target_key& tk)
+ testscript_target_extension (const target_key& tk, const scope*)
{
// If the name is special 'testscript', then there is no extension,
// otherwise it is .testscript.
diff --git a/doc/manual.cli b/doc/manual.cli
index 2ac3b8f..15d7c31 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -459,6 +459,41 @@ $ bdep new --no-init -t exe -l c++,cpp hello
|
+\N|It is also possible to use an alternative build file/directory naming
+scheme where every instance of the word \i{build} is replaced with \i{build2},
+for example:
+
+\
+hello/
+├── build2/
+│ ├── bootstrap.build2
+│ └── root.build2
+├── ...
+└── build2file
+\
+
+Note that the naming must be consistent within a project with all the
+filesystem entries either following \i{build} or \i{build2} scheme. In
+other words, we cannot call the directory \c{build2/} while still using
+\c{buildfile}.
+
+The alternative naming scheme is primarily useful when adding \c{build2}
+support to an existing project along with other build systems. In this case,
+the fairly generic standard names might already be in use. For example, it is
+customary to have \c{build/} in \c{.gitignore}. Plus more specific naming will
+make it easier to identify files and directories as belonging to the
+\c{build2} support. For new projects as well as for existing projects that are
+switching exclusively to \c{build2} the standard naming scheme is recommended.
+
+To create a project with the alternative naming using \l{bdep-new(1)} pass
+the \c{alt-naming} project type sub-option. For example:
+
+\
+$ bdep new -t exe,alt-naming ...
+\
+
+|
+
To support lazy loading of subprojects (discussed later), reading of the
project's build information is split into two phases: bootstrapping and
loading. During bootstrapping the project's \c{build/bootstrap.build} file is