From c6196464780fbdb1a2dbdda92061189395e5072e Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 12 Mar 2018 19:22:42 +0300 Subject: Add support for --keep-out option --- bpkg/pkg-build.cli | 6 + bpkg/pkg-build.cxx | 86 ++++++++-- bpkg/pkg-configure.cxx | 4 +- bpkg/pkg-disfigure.cli | 11 +- bpkg/pkg-disfigure.cxx | 28 +++- bpkg/pkg-disfigure.hxx | 3 +- bpkg/pkg-drop.cxx | 5 +- bpkg/pkg-unpack.cli | 4 +- tests/common.test | 3 +- tests/pkg-build.test | 348 ++++++++++++++++++++++++++++++++++------- tests/pkg-build/libhello-1.0.0 | 1 + tests/pkg-configure.test | 11 +- 12 files changed, 422 insertions(+), 88 deletions(-) create mode 120000 tests/pkg-build/libhello-1.0.0 diff --git a/bpkg/pkg-build.cli b/bpkg/pkg-build.cli index a4d49ef..4bc55a7 100644 --- a/bpkg/pkg-build.cli +++ b/bpkg/pkg-build.cli @@ -142,6 +142,12 @@ namespace bpkg anything." } + bool --keep-out + { + "Keep output directories of external packages between upgrades and + downgrades. Refer to \l{bpkg-pkg-disfigure(1)} for details." + } + bool --fetch-shallow { "Do not re-fetch complement and prerequisite repositories. Refer to diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 8f80ff5..a8e90aa 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -175,6 +175,11 @@ namespace bpkg ? pkg_verify (options, a->absolute () ? *a : cd / *a, true) : pkg_verify (d->absolute () ? *d : cd / *d, true)); + // Copy the possibly fixed up version from the selected package. + // + if (sp->external ()) + m.version = sp->version; + return make_pair (make_shared (move (m)), move (ar)); } @@ -243,6 +248,12 @@ namespace bpkg // bool system; + // If the flag is set and the external package is being replaced with an + // external one, then keep its output directory between upgrades and + // downgrades. + // + bool keep_out; + const version& available_version () const { @@ -438,6 +449,11 @@ namespace bpkg (p2->hold_version && *p2->hold_version > *p1->hold_version)) p1->hold_version = p2->hold_version; + // Save the 'keep output directory' flag if specified by the user. + // + if (p2->user_selection () && p2->keep_out) + p1->keep_out = true; + // Note that we don't copy the build_package::system flag. If it was // set from the command line ("strong system") then we will also have // the '== 0' constraint which means that this build_package object @@ -673,6 +689,7 @@ namespace bpkg nullopt, // Hold version. {}, // Constraints. system, // System. + false, // Keep output directory. {name}, // Required by. false}; // Reconfigure. @@ -1029,6 +1046,7 @@ namespace bpkg nullopt, // Hold version. {}, // Constraints. system, + false, // Keep output directory. {n}, // Required by. true} // Reconfigure. }).first; @@ -1435,6 +1453,14 @@ namespace bpkg package_manifest m (pkg_verify (d, true, diag)); + // Fix-up the package version to properly decide if we need to + // upgrade/downgrade the package. + // + optional pv (package_version (o, d)); + + if (pv) + m.version = move (*pv); + // This is a package directory (note that we shouldn't throw // failed from here on). // @@ -1638,18 +1664,27 @@ namespace bpkg if (v.empty () && sys) v = wildcard_version; + // We will keep the output directory only if the external package is + // replaced with an external one. Note, however, that at this stage + // the available package is not settled down yet, as we still need to + // satisfy all the constraints. Thus the available package check is + // postponed until the package disfiguring. + // + bool keep_out (o.keep_out () && sp->external ()); + // Finally add this package to the list. // build_package p { move (sp), move (ap), move (ar), - true, // Hold package. - !v.empty (), // Hold version. - {}, // Constraints. + true, // Hold package. + !v.empty (), // Hold version. + {}, // Constraints. sys, - {""}, // Required by (command line). - false}; // Reconfigure. + keep_out, + {""}, // Required by (command line). + false}; // Reconfigure. l4 ([&]{trace << "collect " << p.available_name ();}); @@ -1890,7 +1925,37 @@ namespace bpkg } } - pkg_disfigure (c, o, t, sp); // Commits the transaction. + // Reset the flag if the package being unpacked is not an external one. + // + if (p.keep_out) + { + const shared_ptr& ap (p.available); + const package_location& pl (ap->locations[0]); + + if (pl.repository.object_id () == "") // Special root. + p.keep_out = !exists (pl.location); // Directory case. + else + { + p.keep_out = false; + + // See if the package comes from the directory-based repository, and + // so is external. + // + // Note that such repositories are always preferred over others (see + // below). + // + for (const package_location& l: ap->locations) + { + if (l.repository.load ()->location.directory_based ()) + { + p.keep_out = true; + break; + } + } + } + } + + pkg_disfigure (c, o, t, sp, !p.keep_out); // Commits the transaction. assert (sp->state == package_state::unpacked || sp->state == package_state::transient); @@ -2092,6 +2157,9 @@ namespace bpkg { transaction t (db.begin ()); sp = pkg_unpack (o, c, t, ap->id.name); // Commits the transaction. + + if (verb) + text << "unpacked " << *sp; } else { @@ -2105,12 +2173,12 @@ namespace bpkg path_cast (pl.location), true, // Replace. false); // Don't purge; commits the transaction. + + if (verb) + text << "using " << *sp << " (external)"; } assert (sp->state == package_state::unpacked); - - if (verb) - text << "unpacked " << *sp; } break; // Get out from the breakout loop. diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx index aaa426f..19b64d9 100644 --- a/bpkg/pkg-configure.cxx +++ b/bpkg/pkg-configure.cxx @@ -175,7 +175,9 @@ namespace bpkg p->out_root = out_root.leaf (); p->state = package_state::broken; - pkg_disfigure (c, o, t, p); // Commits the transaction. + // Commits the transaction. + // + pkg_disfigure (c, o, t, p, true /* clean */); throw; } diff --git a/bpkg/pkg-disfigure.cli b/bpkg/pkg-disfigure.cli index e7708e0..34e7ad2 100644 --- a/bpkg/pkg-disfigure.cli +++ b/bpkg/pkg-disfigure.cli @@ -22,11 +22,20 @@ namespace bpkg The \cb{pkg-disfigure} command disfigures the previously configured (via \l{bpkg-pkg-build(1)} or \l{bpkg-pkg-configure(1)}) package. A source code package is returned to the \cb{unpacked} state. A system - package is removed from the configuration." + package is removed from the configuration. + + By default \cb{pkg-disfigure} will also clean the package's output + directory. This behavior can be suppressed with the \cb{--keep-out} + option, for example, if the package is to be reconfigured." } class pkg_disfigure_options: configuration_options { "\h|PKG-DISFIGURE OPTIONS|" + + bool --keep-out + { + "Don't clean the package's output directory." + } }; } diff --git a/bpkg/pkg-disfigure.cxx b/bpkg/pkg-disfigure.cxx index 4c32d53..9153a6a 100644 --- a/bpkg/pkg-disfigure.cxx +++ b/bpkg/pkg-disfigure.cxx @@ -18,7 +18,8 @@ namespace bpkg pkg_disfigure (const dir_path& c, const common_options& o, transaction& t, - const shared_ptr& p) + const shared_ptr& p, + bool clean) { assert (p->state == package_state::configured || p->state == package_state::broken); @@ -91,7 +92,10 @@ namespace bpkg if (p->state == package_state::configured) { - bspec = "clean('" + rep + "') " "disfigure('" + rep + "')"; + if (clean) + bspec = "clean('" + rep + "') "; + + bspec += "disfigure('" + rep + "')"; } else { @@ -113,11 +117,23 @@ namespace bpkg try { if (exists (out_root)) - run_b (o, c, bspec, true); // Run quiet. + { + // Note that for external packages this is just the output directory. + // It is also possible that the buildfiles in the source directory + // have changed in a way that they don't clean everything. So in this + // case we just remove the output directory manually rather then + // running 'b clean disfigure'. + // + if (clean && p->external ()) + rm_r (out_root); + else + run_b (o, c, bspec, true); // Run quiet. + } - // Make sure the out directory is gone unless it is the same as src. + // Make sure the out directory is gone unless it is the same as src, or + // we didn't clean it. // - if (out_root != src_root && exists (out_root)) + if (out_root != src_root && clean && exists (out_root)) fail << "package output directory " << out_root << " still exists"; } catch (const failed&) @@ -167,7 +183,7 @@ namespace bpkg fail << "package " << n << " is " << p->state << info << "expected it to be configured"; - pkg_disfigure (c, o, t, p); // Commits the transaction. + pkg_disfigure (c, o, t, p, !o.keep_out ()); // Commits the transaction. assert (p->state == package_state::unpacked || p->state == package_state::transient); diff --git a/bpkg/pkg-disfigure.hxx b/bpkg/pkg-disfigure.hxx index 920fc7c..d475f03 100644 --- a/bpkg/pkg-disfigure.hxx +++ b/bpkg/pkg-disfigure.hxx @@ -26,7 +26,8 @@ namespace bpkg pkg_disfigure (const dir_path& configuration, const common_options&, transaction&, - const shared_ptr&); + const shared_ptr&, + bool clean); } #endif // BPKG_PKG_DISFIGURE_HXX diff --git a/bpkg/pkg-drop.cxx b/bpkg/pkg-drop.cxx index 4309b2e..6e28c37 100644 --- a/bpkg/pkg-drop.cxx +++ b/bpkg/pkg-drop.cxx @@ -354,7 +354,10 @@ namespace bpkg // leave the configuration in a valid state. // transaction t (db.begin ()); - pkg_disfigure (c, o, t, p); // Commits the transaction. + + // Commits the transaction. + // + pkg_disfigure (c, o, t, p, true /* clean */); assert (p->state == package_state::unpacked || p->state == package_state::transient); diff --git a/bpkg/pkg-unpack.cli b/bpkg/pkg-unpack.cli index 56682d9..dc3a1c3 100644 --- a/bpkg/pkg-unpack.cli +++ b/bpkg/pkg-unpack.cli @@ -50,9 +50,7 @@ namespace bpkg example, during development). To support this, \cb{bpkg} implements the \i{package iteration} mechanism which may result in iteration numbers to be shown as part of the package version, for example, \cb{1.2.3#1} (see - \l{bpkg#package-version Package Version}). Finally, it is possible to - replace one external package version with another while preserving its - output directory by specifying the \cb{--keep-out} option." + \l{bpkg#package-version Package Version})." } class pkg_unpack_options: configuration_options diff --git a/tests/common.test b/tests/common.test index 4002df3..21c7801 100644 --- a/tests/common.test +++ b/tests/common.test @@ -8,7 +8,8 @@ # Use the same build system driver as the one running the tests (as opposed # to one that may or may not be found via PATH). # -test.options += --build $recall($build.path) +build = $recall($build.path) +test.options += --build $build # Helper commands that can be used by tests to prepare the testing environment # or validate an outcome of the command being tested. They are likely to get diff --git a/tests/pkg-build.test b/tests/pkg-build.test index f4e77cd..fefaf00 100644 --- a/tests/pkg-build.test +++ b/tests/pkg-build.test @@ -26,12 +26,12 @@ # | `-- repositories.manifest # | # |-- t2 -# | |-- libbar-1.0.0.tar.gz -> libfoo +# | |-- libbar-1.0.0.tar.gz -> libfoo # | |-- libfoo-1.0.0.tar.gz # | `-- repositories.manifest # | -# |-- t3 -> t2 (prerequisite) -# | |-- libbaz-1.0.0.tar.gz -> libbar +# |-- t3 -> t2 (prerequisite) +# | |-- libbaz-1.0.0.tar.gz -> libbar # | |-- libfox-1.0.0.tar.gz # | `-- repositories.manifest # | @@ -39,17 +39,17 @@ # | |-- libfoo-1.1.0.tar.gz # | `-- repositories.manifest # | -# |-- t4b -> t4a (prerequisite) -# | |-- libbar-1.1.0.tar.gz -> libfoo == 1.1.0 +# |-- t4b -> t4a (prerequisite) +# | |-- libbar-1.1.0.tar.gz -> libfoo == 1.1.0 # | `-- repositories.manifest # | -# |-- t4c -> t4b (prerequisite) -# | |-- libbaz-1.1.0.tar.gz -> libfoo, libbar +# |-- t4c -> t4b (prerequisite) +# | |-- libbaz-1.1.0.tar.gz -> libfoo, libbar # | |-- libfoo-1.0.0.tar.gz # | `-- repositories.manifest # | -# |-- t4d -> t4c (complement) -# | |-- libbiz-1.0.0.tar.gz -> libfox, libfoo, libbaz +# |-- t4d -> t4c (complement) +# | |-- libbiz-1.0.0.tar.gz -> libfox, libfoo, libbaz # | |-- libfox-1.0.0.tar.gz # | `-- repositories.manifest # | @@ -58,9 +58,33 @@ # | `-- repositories.manifest # | # `-- git -# |-- libbar.git -> style-basic.git (prerequisite) -# |-- libbaz.git -# `-- style-basic.git +# | |-- libbar.git -> style-basic.git (prerequisite) +# | |-- libbaz.git +# | `-- style-basic.git +# | +# `-- libhello-1.0.0 +# |-- build +# | |-- bootstrap.build +# | |-- export.build +# | `-- root.build +# |-- buildfile +# |-- hello +# | |-- buildfile +# | |-- export +# | |-- hello +# | `-- hello.cxx +# |-- INSTALL +# |-- manifest +# |-- tests +# | |-- build +# | | |-- bootstrap.build +# | | `-- root.build +# | |-- buildfile +# | `-- test +# | |-- buildfile +# | |-- driver.cxx +# | `-- test.out +# `-- version # Prepare repositories used by tests if running in the local mode. # @@ -779,7 +803,7 @@ rep_fetch += -d cfg --auth all --trust-yes 2>! $* $src/libfoo-1.1.0/ 2>>~%EOE%; disfigured libfoo/1.0.0 - unpacked libfoo/1.1.0 + using libfoo/1.1.0 (external) configured libfoo/1.1.0 %info: .+dir\{libfoo-1.1.0.\} is up to date% updated libfoo/1.1.0 @@ -1074,14 +1098,14 @@ rep_fetch += -d cfg --auth all --trust-yes 2>! %.*fetched libfoo/1.1.0% unpacked libfoo/1.1.0 configured libfoo/1.1.0 - unpacked libbar/1.1.0 + using libbar/1.1.0 (external) configured libbar/1.1.0 %info: .+dir\{libbar.\} is up to date% updated libbar/1.1.0 EOE $* ./libbaz/ 2>>~%EOE%; - unpacked libbaz/1.1.0 + using libbaz/1.1.0 (external) configured libbaz/1.1.0 %info: .+dir\{libbaz.\} is up to date% updated libbaz/1.1.0 @@ -1496,6 +1520,186 @@ rep_fetch += -d cfg --auth all --trust-yes 2>! } } +: keep-out +: +{ + +$cfg_create cxx "config.cxx=$config.cxx" -d cfg 2>- &cfg/*** + + # Build libhello as an external package. + # + +cp -r $src/libhello-1.0.0 ./libhello + +$rep_add libhello --type dir + +$rep_fetch + +$* libhello 2>! + + # Move libhello version ahead. + # + +sed -i -e 's/(version: 1.0).0/\1.1/' libhello/manifest + +$rep_fetch + + test.arguments += --yes # Is a command-specific option. + + : unspecified + : + : Test that libhello is fully rebuilt. + : + { + $clone_cfg; + $rep_fetch; + + $* libhello 2>>~%EOE% + disfigured libhello/1.0.0 + using libhello/1.0.1 (external) + configured libhello/1.0.1 + %mkdir .+%+ + %c\+\+ .+%+ + %ld .+%+ + updated libhello/1.0.1 + EOE + } + + : specified + : + { + test.arguments += --keep-out + + +$clone_cfg + + : dir-repo + : + : Test that libhello is built incrementally. May re-link due to the + : configuration copying. + : + { + $clone_cfg; + + $* libhello 2>>~%EOE%; + disfigured libhello/1.0.0 + using libhello/1.0.1 (external) + configured libhello/1.0.1 + %ld .+%* + updated libhello/1.0.1 + EOE + + test -d cfg/libhello/; + test -d cfg/libhello-1.0.1/ == 1 + } + + : directory + : + : Test that libhello is built incrementally. May re-link due to the + : configuration copying. + : + { + +$clone_cfg + + : arg + : + { + $clone_cfg; + + $* ../../../libhello/ 2>>~%EOE%; + disfigured libhello/1.0.0 + using libhello/1.0.1 (external) + configured libhello/1.0.1 + %ld .+%* + updated libhello/1.0.1 + EOE + + test -d cfg/libhello/; + test -d cfg/libhello-1.0.1/ == 1 + } + + : unpacked + : + { + $clone_cfg; + $pkg_disfigure libhello --keep-out 2>!; + $pkg_unpack -r -e ../../../libhello; + + $* libhello 2>>~%EOE%; + configured libhello/1.0.1 + %ld .+%* + updated libhello/1.0.1 + EOE + + test -d cfg/libhello/; + test -d cfg/libhello-1.0.1/ == 1 + } + } + + : archive + : + : Test that libhello is fully rebuilt, as the resulted package is not + : external. + : + : @@ On Windows the dist operation fails as tar can not find gzip for some + : reason. We will disable the test for Windows until the issue is fixed. + : + if ($cxx.target.class != 'windows') + { + clone_cfg = cp --no-cleanup -r ../../cfg ./ + + +$build 'dist(../../libhello/@./out/)' \ + "config.cxx=$config.cxx" config.dist.root=./ \ + config.dist.archives=tar.gz 2>! \ + &out/*** &libhello-1.0.1/*** &libhello-1.0.1.tar.gz + + : arg + : + { + $clone_cfg; + + $* ../libhello-1.0.1.tar.gz 2>>~%EOE%; + disfigured libhello/1.0.0 + fetched libhello/1.0.1 + unpacked libhello/1.0.1 + configured libhello/1.0.1 + %c\+\+ .+%+ + %ld .+%+ + updated libhello/1.0.1 + EOE + + test -d cfg/libhello-1.0.1/; + test -d cfg/libhello/ == 1; + + $pkg_disfigure libhello 2>'disfigured libhello/1.0.1'; + $pkg_purge libhello 2>'purged libhello/1.0.1'; + + rm -r cfg/ + } + + : unpacked + : + { + $clone_cfg; + + $pkg_disfigure libhello 2>!; + $pkg_fetch -r -e ../libhello-1.0.1.tar.gz; + $pkg_unpack libhello; + + $* libhello 2>>~%EOE%; + configured libhello/1.0.1 + %c\+\+ .+%+ + %ld .+%+ + updated libhello/1.0.1 + EOE + + test -d cfg/libhello-1.0.1/; + test -d cfg/libhello/ == 1; + + $pkg_disfigure libhello 2>'disfigured libhello/1.0.1'; + $pkg_purge libhello 2>'purged libhello/1.0.1'; + + rm -r cfg/ + } + } + } + + -$pkg_disfigure libhello 2>'disfigured libhello/1.0.0' + -$pkg_purge libhello 2>'purged libhello/1.0.0' +} + : git-rep : if ($git_supported != true) @@ -1518,27 +1722,29 @@ else : Prerequisite package of the dependent being built is in the prerequisite : repository. : - $clone_root_cfg; - $rep_add "$rep0/libbar.git#master"; - $rep_add "$rep0/style-basic.git#master"; - $rep_fetch &cfg/.bpkg/repos/*/***; - - $* libmbar 2>>~%EOE%; - %dist style-basic-.+% - %checked out style-basic/.+% - %configured style-basic/.+% - dist libmbar-1.0.0 - checked out libmbar/1.0.0 - configured libmbar/1.0.0 - %info: .+dir\{libmbar-.+\} is up to date% - updated libmbar/1.0.0 - EOE - - $pkg_disfigure libmbar; - $pkg_disfigure style-basic; - - $pkg_purge libmbar; - $pkg_purge style-basic + { + $clone_root_cfg; + $rep_add "$rep0/libbar.git#master"; + $rep_add "$rep0/style-basic.git#master"; + $rep_fetch &cfg/.bpkg/repos/*/***; + + $* libmbar 2>>~%EOE%; + %dist style-basic-.+% + %checked out style-basic/.+% + %configured style-basic/.+% + dist libmbar-1.0.0 + checked out libmbar/1.0.0 + configured libmbar/1.0.0 + %info: .+dir\{libmbar-.+\} is up to date% + updated libmbar/1.0.0 + EOE + + $pkg_disfigure libmbar; + $pkg_disfigure style-basic; + + $pkg_purge libmbar; + $pkg_purge style-basic + } : no-prerequisite-repos : @@ -1546,25 +1752,53 @@ else : dependency is picked up via the root repository that complements its : repository (see rep_fetch() function implementation for details). : - $clone_root_cfg; - $rep_add "$rep1/libbaz.git#master"; - $rep_add "$rep0/style-basic.git#master"; - $rep_fetch &cfg/.bpkg/repos/*/***; - - $* libbaz 2>>~%EOE%; - %dist style-basic-.+% - %checked out style-basic/.+% - %configured style-basic/.+% - dist libbaz-1.0.0 - checked out libbaz/1.0.0 - configured libbaz/1.0.0 - %info: .+dir\{libbaz-.+\} is up to date% - updated libbaz/1.0.0 - EOE - - $pkg_disfigure libbaz; - $pkg_disfigure style-basic; - - $pkg_purge libbaz; - $pkg_purge style-basic + { + $clone_root_cfg; + $rep_add "$rep1/libbaz.git#master"; + $rep_add "$rep0/style-basic.git#master"; + $rep_fetch &cfg/.bpkg/repos/*/***; + + $* libbaz 2>>~%EOE%; + %dist style-basic-.+% + %checked out style-basic/.+% + %configured style-basic/.+% + dist libbaz-1.0.0 + checked out libbaz/1.0.0 + configured libbaz/1.0.0 + %info: .+dir\{libbaz-.+\} is up to date% + updated libbaz/1.0.0 + EOE + + $pkg_disfigure libbaz; + $pkg_disfigure style-basic; + + $pkg_purge libbaz; + $pkg_purge style-basic + } + + : build-unpacked + : + : Test that the unpacked external package is properly built for the first + : time and is not rebuilt afterwards via the directory argument. + : + if ($remote != true) + { + $clone_root_cfg; + + d = $canonicalize([dir_path] $out_git/state0/style-basic.git); + $pkg_unpack -e $d; + + $* style-basic 2>>~%EOE%; + %configured style-basic/1\.1\.0-a\.0\.\d+\..+% + %info: .+style-basic.+ is up to date% + %updated style-basic/1\.1\.0-a\.0\.\d+\..+% + EOE + + $* $d 2>>~%EOE%; + %info: .+style-basic.+ is up to date% + %updated style-basic/1\.1\.0-a\.0\.\d+\..+% + EOE + + $pkg_disfigure style-basic + } } diff --git a/tests/pkg-build/libhello-1.0.0 b/tests/pkg-build/libhello-1.0.0 new file mode 120000 index 0000000..614ff24 --- /dev/null +++ b/tests/pkg-build/libhello-1.0.0 @@ -0,0 +1 @@ +../common/libhello-1.0.0/ \ No newline at end of file diff --git a/tests/pkg-configure.test b/tests/pkg-configure.test index 3b4834e..c82593c 100644 --- a/tests/pkg-configure.test +++ b/tests/pkg-configure.test @@ -188,14 +188,9 @@ $* libhello libhello 2>>EOE != 0 $* libhello 2>'configured libhello/1.0.0'; touch cfg/libhello/stray &!cfg/libhello/stray; - $pkg_disfigure libhello 2>>/EOE != 0; - warning: directory cfg/libhello/ is not empty, not removing - error: package output directory cfg/libhello/ still exists - info: package libhello is now broken; use 'pkg-purge' to remove - EOE + $pkg_disfigure libhello 2>'disfigured libhello/1.0.0'; - $pkg_status libhello/1.0.0 >'broken'; - rm -r cfg/libhello; + $pkg_status libhello/1.0.0 >'unpacked'; $pkg_purge -f libhello 2>'purged libhello/1.0.0'; $pkg_status libhello/1.0.0 1>'unknown' @@ -255,7 +250,7 @@ if ($cxx.target.class != 'windows') $* libhello 2>>/~%EOE% != 0; %error: unable to create directory cfg/libhello/build/.+% - %error: unable to remove directory cfg/libhello/build/.+% + %error: unable to remove directory cfg/libhello/.+% info: package libhello is now broken; use 'pkg-purge' to remove EOE -- cgit v1.1