diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2023-05-16 11:31:24 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2023-05-16 11:31:24 +0200 |
commit | 2ef2038b3301916bc8d256c170a8d075012c7aed (patch) | |
tree | eb8f3779e641248c005043a5abdc8f3bc1e2b9ff /libbuild2/dist | |
parent | e04fc00e978bb242f2aa6f0d1a6c4075048c9e09 (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.
Diffstat (limited to 'libbuild2/dist')
-rw-r--r-- | libbuild2/dist/init.cxx | 20 | ||||
-rw-r--r-- | libbuild2/dist/operation.cxx | 156 |
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 |