From 236ad71b105365bedf9d28a5606616fb9aed3168 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 16 Sep 2015 07:16:06 +0200 Subject: Implement pkg-unpack command --- bpkg/pkg-unpack.cxx | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 bpkg/pkg-unpack.cxx (limited to 'bpkg/pkg-unpack.cxx') diff --git a/bpkg/pkg-unpack.cxx b/bpkg/pkg-unpack.cxx new file mode 100644 index 0000000..8b08674 --- /dev/null +++ b/bpkg/pkg-unpack.cxx @@ -0,0 +1,205 @@ +// file : bpkg/pkg-unpack.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include // shared_ptr + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace butl; + +namespace bpkg +{ + static shared_ptr + pkg_unpack (database& db, const dir_path& c, const dir_path& d) + { + tracer trace ("pkg_unpack(dir)"); + + if (!exists (d)) + fail << "package directory " << d << " does not exist"; + + // Verify the directory is a package and get its manifest. + // + package_manifest m (pkg_verify (d)); + level4 ([&]{trace << d << ": " << m.name << " " << m.version;}); + + const auto& n (m.name); + + transaction t (db.begin ()); + + // See if this package already exists in this configuration. + // + if (shared_ptr p = db.find (n)) + fail << "package " << n << " already exists in configuration " << c << + info << "version: " << p->version << ", state: " << p->state; + + // Make the package and configuration paths absolute and normalized. + // If the package is inside the configuration, use the relative path. + // This way we can move the configuration around. + // + dir_path ac (c), ad (d); + ac.complete ().normalize (); + ad.complete ().normalize (); + + if (ad.sub (ac)) + ad = ad.leaf (ac); + + // Add the package to the configuration. + // + shared_ptr p (new package { + move (m.name), + move (m.version), + state::unpacked, + optional (), // No archive + false, // Don't purge archive. + move (ad), + false}); // Don't purge source. + + db.persist (p); + t.commit (); + + return p; + } + + static shared_ptr + pkg_unpack (database& db, const dir_path& c, const string& name) + { + tracer trace ("pkg_unpack(pkg)"); + + transaction t (db.begin ()); + shared_ptr p (db.find (name)); + + if (p == nullptr) + fail << "package " << name << " does not exist in configuration " << c; + + if (p->state != state::fetched) + fail << "package " << name << " is already in " << p->state << " state"; + + level4 ([&]{trace << p->name << " " << p->version;}); + + assert (p->archive); // Should have archive in the fetched state. + + // If the archive path is not absolute, then it must be relative + // to the configuration. + // + path a (*p->archive); + if (a.relative ()) + a = c / a; + + level4 ([&]{trace << "archive: " << a;}); + + // Extract the package directory. Currently we always extract it + // into the configuration directory. But once we support package + // cache, this will need to change. + // + // Also, since we must have verified the archive during fetch, + // here we can just assume what the resulting directory will be. + // + dir_path d (c / dir_path (p->name + '-' + p->version.string ())); + + if (exists (d)) + fail << "package directory " << d << " already exists"; + + const char* args[] { + "tar", + "-C", c.string ().c_str (), // -C/--directory -- change to directory. + "-xf", + a.string ().c_str (), + nullptr}; + + if (verb >= 2) + print_process (args); + + // What should we do if tar or something after it fails? Cleaning + // up the package directory sounds like the right thing to do. + // + auto dg ( + make_exception_guard ( + [&d]() + { + if (exists (d)) + rm_r (d); + })); + + try + { + process pr (args); + + // While it is reasonable to assuming the child process issued + // diagnostics, tar, specifically, doesn't mention the archive + // name. + // + if (!pr.wait ()) + fail << "unable to extract package archive " << a; + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e.what (); + + if (e.child ()) + exit (1); + + throw failed (); + } + + p->source = d.leaf (); // For now assuming to be in configuration. + p->source_purge = true; + + p->state = state::unpacked; + + db.update (p); + t.commit (); + + return p; + } + + void + pkg_unpack (const pkg_unpack_options& o, cli::scanner& args) + { + tracer trace ("pkg_unpack"); + + const dir_path& c (o.directory ()); + level4 ([&]{trace << "configuration: " << c;}); + + database db (open (c)); + + shared_ptr p; + + if (o.existing ()) + { + // The package directory case. + // + if (!args.more ()) + fail << "package directory argument expected" << + info << "run 'bpkg help pkg-unpack' for more information"; + + p = pkg_unpack (db, c, dir_path (args.next ())); + } + else + { + // The package name case. + // + if (!args.more ()) + fail << "package name argument expected" << + info << "run 'bpkg help pkg-unpack' for more information"; + + p = pkg_unpack (db, c, args.next ()); + } + + if (verb) + text << "unpacked " << p->name << " " << p->version; + } +} -- cgit v1.1