aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-05-16 11:31:24 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-05-16 11:31:24 +0200
commit2ef2038b3301916bc8d256c170a8d075012c7aed (patch)
treeeb8f3779e641248c005043a5abdc8f3bc1e2b9ff
parente04fc00e978bb242f2aa6f0d1a6c4075048c9e09 (diff)
Implement dist meta-operation without invoking install (GH issue #190)
The use of install (or another install-like program) can still be forced with config.dist.cmd=install.
-rw-r--r--libbuild2/dist/init.cxx20
-rw-r--r--libbuild2/dist/operation.cxx156
2 files changed, 122 insertions, 54 deletions
diff --git a/libbuild2/dist/init.cxx b/libbuild2/dist/init.cxx
index 26ff86d..2a25992 100644
--- a/libbuild2/dist/init.cxx
+++ b/libbuild2/dist/init.cxx
@@ -133,7 +133,7 @@ namespace build2
//
bool s (specified_config (rs, "dist", {"bootstrap"}));
- // dist.root
+ // config.dist.root
//
{
value& v (rs.assign ("dist.root"));
@@ -145,22 +145,24 @@ namespace build2
}
}
- // dist.cmd
+ // config.dist.cmd
+ //
+ // By default we use in-process code for creating directories and
+ // copying files (for performance, especially on Windows). But an
+ // external program (normally install) can be used if configured.
//
{
- value& v (rs.assign<process_path> ("dist.cmd"));
+ value& v (rs.assign<process_path> ("dist.cmd")); // NULL
if (s)
{
- if (lookup l = lookup_config (rs,
- "config.dist.cmd",
- path ("install")))
+ if (lookup l = lookup_config (rs, "config.dist.cmd", nullptr))
v = run_search (cast<path> (l), true);
}
}
- // dist.archives
- // dist.checksums
+ // config.dist.archives
+ // config.dist.checksums
//
{
value& a (rs.assign ("dist.archives"));
@@ -183,7 +185,7 @@ namespace build2
}
}
- // dist.uncommitted
+ // config.dist.uncommitted
//
// Omit it from the configuration unless specified.
//
diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx
index 6dcc88a..da96215 100644
--- a/libbuild2/dist/operation.cxx
+++ b/libbuild2/dist/operation.cxx
@@ -6,6 +6,8 @@
#include <libbutl/sha1.hxx>
#include <libbutl/sha256.hxx>
+#include <libbutl/filesystem.hxx> // try_mkdir_p(), cpfile()
+
#include <libbuild2/file.hxx>
#include <libbuild2/dump.hxx>
#include <libbuild2/scope.hxx>
@@ -29,14 +31,14 @@ namespace build2
// install -d <dir>
//
static void
- install (const process_path&, context&, const dir_path&);
+ install (const process_path*, context&, const dir_path&);
// install <file> <dir>[/<name>]
//
// Return the destination file path.
//
static path
- install (const process_path&, const file&, const dir_path&, const path&);
+ install (const process_path*, const file&, const dir_path&, const path&);
// tar|zip ... <dir>/<pkg>.<ext> <pkg>
//
@@ -108,9 +110,7 @@ namespace build2
// Figure out if we need out.
//
- dir_path out (rs.src_path () != rs.out_path ()
- ? out_src (d, rs)
- : dir_path ());
+ dir_path out (!rs.out_eq_src () ? out_src (d, rs) : dir_path ());
const T& t (rs.ctx.targets.insert<T> (
move (d),
@@ -241,7 +241,8 @@ namespace build2
const module& mod (*rs.find_module<module> (module::name));
const string& dist_package (cast<string> (l));
- const process_path& dist_cmd (cast<process_path> (rs.vars["dist.cmd"]));
+ const process_path* dist_cmd (
+ cast_null<process_path> (rs.vars["dist.cmd"]));
dir_path td (dist_root / dir_path (dist_package));
@@ -399,7 +400,7 @@ namespace build2
// Add ad hoc files and buildfiles that are not normally loaded as
// part of the project, for example, the export stub. They will still
// be ignored on the next step if the user explicitly marked them
- // dist=false.
+ // with dist=false.
//
auto add_adhoc = [] (const scope& rs)
{
@@ -796,66 +797,131 @@ namespace build2
// install -d <dir>
//
static void
- install (const process_path& cmd, context& ctx, const dir_path& d)
+ install (const process_path* cmd, context& ctx, const dir_path& d)
{
- path reld (relative (d));
+ path reld;
+ cstrings args;
- cstrings args {cmd.recall_string (), "-d"};
+ if (cmd != nullptr || verb >= 2)
+ {
+ reld = relative (d);
- args.push_back ("-m");
- args.push_back ("755");
- args.push_back (reld.string ().c_str ());
- args.push_back (nullptr);
+ args.push_back (cmd != nullptr ? cmd->recall_string () : "install");
+ args.push_back ("-d");
+ args.push_back ("-m");
+ args.push_back ("755");
+ args.push_back (reld.string ().c_str ());
+ args.push_back (nullptr);
- if (verb >= 2)
- print_process (args);
+ if (verb >= 2)
+ print_process (args);
+ }
- run (ctx, cmd, args, 1 /* finish_verbosity */);
+ if (cmd != nullptr)
+ run (ctx, *cmd, args, 1 /* finish_verbosity */);
+ else
+ {
+ try
+ {
+ // Note that mode has no effect on Windows, which is probably for
+ // the best.
+ //
+ try_mkdir_p (d, 0755);
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to create directory " << d << ": " << e;
+ }
+ }
}
// install <file> <dir>[/<name>]
//
static path
- install (const process_path& cmd,
+ install (const process_path* cmd,
const file& t,
const dir_path& d,
const path& n)
{
- path reld (relative (d));
- path relf (relative (t.path ()));
+ const path& f (t.path ());
+ path r (d / (n.empty () ? f.leaf () : n));
- if (!n.empty ())
- reld /= n.string ();
+ // Assume the file is executable if the owner has execute permission,
+ // in which case we make it executable for everyone.
+ //
+ bool exe ((path_perms (f) & permissions::xu) == permissions::xu);
- cstrings args {cmd.recall_string ()};
+ path relf, reld;
+ cstrings args;
- // Preserve timestamps. This could becomes important if, for
- // example, we have pre-generated sources. Note that the
- // install-sh script doesn't support this option, while both
- // Linux and BSD install's do.
- //
- args.push_back ("-p");
+ if (cmd != nullptr || verb >= 2)
+ {
+ relf = relative (f);
+ reld = relative (d);
- // Assume the file is executable if the owner has execute
- // permission, in which case we make it executable for
- // everyone.
- //
- args.push_back ("-m");
- args.push_back (
- (path_perms (t.path ()) & permissions::xu) == permissions::xu
- ? "755"
- : "644");
+ if (!n.empty ()) // Leave as just directory if no custom name.
+ reld /= n;
- args.push_back (relf.string ().c_str ());
- args.push_back (reld.string ().c_str ());
- args.push_back (nullptr);
+ args.push_back (cmd != nullptr ? cmd->recall_string () : "install");
- if (verb >= 2)
- print_process (args);
+ // Preserve timestamps. This could becomes important if, for example,
+ // we have pre-generated sources. Note that the install-sh script
+ // doesn't support this option, while both Linux and BSD install's do.
+ //
+ args.push_back ("-p");
+
+ // Assume the file is executable if the owner has execute permission,
+ // in which case we make it executable for everyone.
+ //
+ args.push_back ("-m");
+ args.push_back (exe ? "755" : "644");
+ args.push_back (relf.string ().c_str ());
+ args.push_back (reld.string ().c_str ());
+ args.push_back (nullptr);
+
+ if (verb >= 2)
+ print_process (args);
+ }
+
+ if (cmd != nullptr)
+ run (t.ctx, *cmd, args, 1 /* finish_verbosity */);
+ else
+ {
+ permissions perm (permissions::ru | permissions::wu |
+ permissions::rg |
+ permissions::ro); // 644
+ if (exe)
+ perm |= permissions::xu | permissions::xg | permissions::xo; // 755
- run (t.ctx, cmd, args, 1 /* finish_verbosity */);
+ try
+ {
+ // Note that we don't pass cpflags::overwrite_content which means
+ // this will fail if the file already exists. Since we clean up the
+ // destination directory, this will detect cases where we have
+ // multiple source files with the same distribution destination.
+ //
+ cpfile (f,
+ r,
+ cpflags::overwrite_permissions | cpflags::copy_timestamps,
+ perm);
+ }
+ catch (const system_error& e)
+ {
+ if (e.code ().category () == generic_category () &&
+ e.code ().value () == EEXIST)
+ {
+ // @@ TMP (added in 0.16.0).
+ //
+ warn << "multiple files are distributed as " << r <<
+ info << "second file is " << f <<
+ info << "this warning will become error in the future";
+ }
+ else
+ fail << "unable to copy " << f << " to " << r << ": " << e;
+ }
+ }
- return d / (n.empty () ? relf.leaf () : n);
+ return r;
}
static path