aboutsummaryrefslogtreecommitdiff
path: root/build/b.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-09 08:43:58 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-09 08:43:58 +0200
commit2e98d3ec3aa57c7b1776d3bf5e7e219a9a3cb3af (patch)
tree9a7fd8701853c5df17587be0ef00c61a32d6fc40 /build/b.cxx
parent7de6f6f275d840e8d9523c72d8f4309c51b4dcd3 (diff)
Build according to buildspec
At this stage operations are still ignored.
Diffstat (limited to 'build/b.cxx')
-rw-r--r--build/b.cxx305
1 files changed, 216 insertions, 89 deletions
diff --git a/build/b.cxx b/build/b.cxx
index de76071..0a1d8d2 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -19,6 +19,7 @@
#include <system_error>
#include <build/spec>
+#include <build/name>
#include <build/scope>
#include <build/target>
#include <build/prerequisite>
@@ -171,83 +172,146 @@ main (int argc, char* argv[])
}
}
- if (verb >= 4)
- trace << "buildspec: " << bspec;
+ level4 ([&]{trace << "buildspec: " << bspec;});
- // Figure out {src,out}_{root,base}. Note that all the paths must be
- // normalized.
- //
- //@@ Must be normalized.
+ // Load all the buildfiles.
//
- path out_base (work);
- path src_base (out_base); //@@ TMP
+ if (bspec.empty ())
+ bspec.push_back (metaopspec ()); // Default meta-operation.
- path src_root;
- path out_root;
-
- // The project's root directory is the one that contains the build/
- // sub-directory which contains the pre.build file.
- //
- for (path d (src_base); !d.root () && d != home; d = d.directory ())
+ for (metaopspec& ms: bspec)
{
- if (path_mtime (d / path ("build/pre.build")) != timestamp_nonexistent)
+ if (ms.empty ())
+ ms.push_back (opspec ()); // Default operation.
+
+ for (opspec& os: ms)
{
- src_root = d;
- break;
+ if (os.empty ())
+ // Default target: dir{}.
+ //
+ os.push_back (targetspec (name ("dir", path (), string ())));
+
+ for (targetspec& ts: os)
+ {
+ name& tn (ts.target);
+
+ // First figure out the out_base of this target. The logic
+ // is as follows: if a directory was specified in any form,
+ // then that's the out_base. Otherwise, we check if the name
+ // value has a directory prefix. This has a good balance of
+ // control and the expected result in most cases.
+ //
+ path out_base (tn.dir);
+ if (out_base.empty ())
+ {
+ // See if there is a directory part in value. We cannot
+ // assume it is a valid filesystem name so we will have
+ // to do the splitting manually.
+ //
+ path::size_type i (path::traits::rfind_separator (tn.value));
+
+ if (i != string::npos)
+ out_base = path (tn.value, i != 0 ? i : 1); // Special case: "/".
+ }
+
+ if (out_base.relative ())
+ out_base = work / out_base;
+
+ out_base.normalize ();
+
+ path& src_base (ts.src_base);
+ if (src_base.empty ())
+ {
+ //@@ TODO: Configured case: find out_root (looking for
+ // "build/bootstrap.build" or some such), then src_root
+ // (stored in this file). Need to also detect the in-tree
+ // build.
+ //
+
+ // If that doesn't work out (e.g., the first build), then
+ // default to the working directory as src_base.
+ //
+ src_base = work;
+ }
+
+ if (src_base.relative ())
+ src_base = work / src_base;
+
+ src_base.normalize ();
+
+ path src_root;
+ path out_root;
+
+ // The project's root directory is the one that contains the build/
+ // sub-directory which contains the pre.build file.
+ //
+ for (path d (src_base), f ("build/pre.build");
+ !d.root () && d != home;
+ d = d.directory ())
+ {
+ if (path_mtime (d / f) != timestamp_nonexistent)
+ {
+ src_root = d;
+ break;
+ }
+ }
+
+ // If there is no such sub-directory, assume this is a simple
+ // project with src_root being the same as src_base.
+ //
+ if (src_root.empty ())
+ {
+ src_root = src_base;
+ out_root = out_base;
+ }
+ else
+ out_root = out_base.directory (src_base.leaf (src_root));
+
+ if (verb >= 4)
+ {
+ trace << tn;
+ trace << " out_base: " << out_base.string ();
+ trace << " src_base: " << src_base.string ();
+ trace << " out_root: " << out_root.string ();
+ trace << " src_root: " << src_root.string ();
+ }
+
+ // Create project root and base scopes, set the corresponding
+ // variables. Note that we might already have all of this set
+ // up as a result of one of the preceding target processing.
+ //
+ scope& proot_scope (scopes[out_root]);
+ scope& pbase_scope (scopes[out_base]);
+
+ proot_scope.variables["out_root"] = move (out_root);
+ proot_scope.variables["src_root"] = move (src_root);
+
+ pbase_scope.variables["out_base"] = out_base;
+ pbase_scope.variables["src_base"] = src_base;
+
+ // Parse the buildfile.
+ //
+ path bf (src_base / path ("buildfile"));
+
+ ifstream ifs (bf.string ());
+ if (!ifs.is_open ())
+ fail << "unable to open " << bf;
+
+ ifs.exceptions (ifstream::failbit | ifstream::badbit);
+ parser p;
+
+ try
+ {
+ p.parse_buildfile (ifs, bf, pbase_scope);
+ }
+ catch (const std::ios_base::failure&)
+ {
+ fail << "failed to read from " << bf;
+ }
+ }
}
}
- // If there is no such sub-directory, assume this is a simple project
- // with src_root being the same as src_base.
- //
- if (src_root.empty ())
- {
- src_root = src_base;
- out_root = out_base;
- }
- else
- out_root = out_base.directory (src_base.leaf (src_root));
-
- if (verb >= 4)
- {
- trace << "out_base: " << out_base.string ();
- trace << "src_base: " << src_base.string ();
- trace << "out_root: " << out_root.string ();
- trace << "src_root: " << src_root.string ();
- }
-
- // Create project root and base scopes, set the corresponding
- // variables.
- //
- scope& proot_scope (scopes[out_root]);
- scope& pbase_scope (scopes[out_base]);
-
- proot_scope.variables["out_root"] = move (out_root);
- proot_scope.variables["src_root"] = move (src_root);
-
- pbase_scope.variables["out_base"] = out_base;
- pbase_scope.variables["src_base"] = src_base;
-
- // Parse buildfile.
- //
- path bf ("buildfile");
-
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << bf;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
- parser p;
-
- try
- {
- p.parse_buildfile (ifs, bf, pbase_scope);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << bf;
- }
-
dump_scopes ();
dump ();
@@ -268,30 +332,93 @@ main (int argc, char* argv[])
path_rule path_r;
rules[typeid (path_target)].emplace ("path", path_r);
- // Build.
+ // Do the operations. We do meta-operations and operations sequentially
+ // (no parallelism).
//
- auto i (targets.find (dir::static_type.id, out_base, "", nullptr, trace));
- if (i == targets.end ())
- fail << "no targets in " << bf;
-
- target& t (**i);
-
- match (t);
-
- dump ();
-
- switch (update (t))
+ for (metaopspec& ms: bspec)
{
- case target_state::uptodate:
+ for (opspec& os: ms)
{
- info << "target " << t << " is up to date";
- break;
+ // But multiple targets in the same operation can be done in
+ // parallel.
+ //
+ vector<reference_wrapper<target>> tgs;
+ tgs.reserve (os.size ());
+
+ // First resolve and match all the targets. We don't want to
+ // start building before we know how for all the targets in
+ // this operation.
+ //
+ for (targetspec& ts: os)
+ {
+ name& tn (ts.target);
+ const location l ("<buildspec>", 1, 0); //@@ TODO
+
+ const string* e;
+ const target_type* ti (target_types.find (tn, e));
+
+ if (ti == nullptr)
+ fail (l) << "unknown target type " << tn.type;
+
+ // If the directory is relative, assume it is relative to work
+ // (must be consistent with how we derive out_base).
+ //
+ path& d (tn.dir);
+
+ if (d.relative ())
+ d = work / d;
+
+ d.normalize ();
+
+ target_set::key tk {ti, &d, &tn.value, &e};
+ auto i (targets.find (tk, trace));
+ if (i == targets.end ())
+ fail (l) << "unknown target " << tk;
+
+ target& t (**i);
+
+ if (!t.recipe ())
+ {
+ level4 ([&]{trace << "matching target " << t;});
+ match (t);
+ }
+
+ tgs.push_back (t);
+ }
+
+ dump ();
+
+ // Now build.
+ //
+ for (target& t: tgs)
+ {
+ // The target might have already been updated indirectly. We
+ // still want to inform the user about its status since they
+ // requested its update explicitly.
+ //
+ target_state s (t.state ());
+ if (s == target_state::unknown)
+ {
+ level4 ([&]{trace << "updating target " << t;});
+ s = update (t);
+ }
+
+ switch (s)
+ {
+ case target_state::uptodate:
+ {
+ info << "target " << t << " is up to date";
+ break;
+ }
+ case target_state::updated:
+ break;
+ case target_state::failed:
+ //@@ This could probably happen in a parallel build.
+ case target_state::unknown:
+ assert (false);
+ }
+ }
}
- case target_state::updated:
- break;
- case target_state::failed:
- case target_state::unknown:
- assert (false);
}
}
catch (const failed&)