// file : bpkg/pkg-unpack.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #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, bool purge) { tracer trace ("pkg_unpack(dir)"); tracer_guard tg (db, trace); 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. Use the special root // repository as the repository of this package. // shared_ptr p (new selected_package { move (m.name), move (m.version), package_state::unpacked, repository_location (), nullopt, // No archive false, // Don't purge archive. move (ad), purge, nullopt, // No output directory yet. {} // No prerequisites captured yet. }); db.persist (p); t.commit (); return p; } static shared_ptr pkg_unpack (const common_options& co, database& db, const dir_path& c, const string& name) { tracer trace ("pkg_unpack(pkg)"); tracer_guard tg (db, trace); 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 != package_state::fetched) fail << "package " << name << " is " << p->state << info << "expected it to be fetched"; 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->absolute () ? *p->archive : c / *p->archive); 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"; // What should we do if tar or something after it fails? Cleaning // up the package directory sounds like the right thing to do. // auto_rm_r arm (d); cstrings args {co.tar ().string ().c_str ()}; // Add extra options. // for (const string& o: co.tar_option ()) args.push_back (o.c_str ()); // -C/--directory -- change to directory. // args.push_back ("-C"); args.push_back (c.string ().c_str ()); args.push_back ("-xf"); args.push_back (a.string ().c_str ()); args.push_back (nullptr); if (verb >= 2) print_process (args); try { process pr (args.data ()); // 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->src_root = d.leaf (); // For now assuming to be in configuration. p->purge_src = true; p->state = package_state::unpacked; db.update (p); t.commit (); arm.cancel (); 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, trace)); 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 ()), o.purge ()); } 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 (o, db, c, args.next ()); } if (verb) text << "unpacked " << p->name << " " << p->version; } }