aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-03-20 21:50:15 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-03-23 18:43:02 +0300
commitceb8d922d6cadefd835278d20184348b1b1dbf98 (patch)
tree437641411ca6b1104a7b6aa471c2f4065fe9524e
parent1afab626ba5cdfb16dccd9249e4223d16d131c71 (diff)
Don't generate main binary package for header-only libraries on Debian and Fedora
-rw-r--r--bpkg/pkg-build.cxx2
-rw-r--r--bpkg/system-package-manager-archive.cxx7
-rw-r--r--bpkg/system-package-manager-debian.cxx459
-rw-r--r--bpkg/system-package-manager-debian.hxx4
-rw-r--r--bpkg/system-package-manager-fedora.cxx506
-rw-r--r--bpkg/system-package-manager-fedora.hxx4
-rw-r--r--doc/manual.cli31
7 files changed, 722 insertions, 291 deletions
diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx
index f44ba15..cc69b58 100644
--- a/bpkg/pkg-build.cxx
+++ b/bpkg/pkg-build.cxx
@@ -1755,7 +1755,7 @@ namespace bpkg
{
diag_record dr (fail);
- dr << "no installed " << (o.sys_install () ? " or available " : "")
+ dr << "no installed " << (o.sys_install () ? "or available " : "")
<< "system package for " << nm;
if (!o.sys_install ())
diff --git a/bpkg/system-package-manager-archive.cxx b/bpkg/system-package-manager-archive.cxx
index 0df4580..b8df860 100644
--- a/bpkg/system-package-manager-archive.cxx
+++ b/bpkg/system-package-manager-archive.cxx
@@ -313,6 +313,11 @@ namespace bpkg
const package_name& pn (sp->name);
const version& pv (sp->version);
+ // Use version without iteration in paths, etc.
+ //
+ string pvs (pv.string (false /* ignore_revision */,
+ true /* ignore_iteration */));
+
bool lib (pt == "lib");
bool priv (ops->private_ ()); // Private installation.
@@ -497,7 +502,7 @@ namespace bpkg
// libhello-1.2.3-x86_64-windows10-msvc17.4
// libhello-1.2.3-x86_64-debian11-gcc12-rust1.62
//
- string base (pn.string () + '-' + pv.string ());
+ string base (pn.string () + '-' + pvs);
if (ops->archive_build_meta_specified ())
{
diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx
index c956aa9..e0b2bce 100644
--- a/bpkg/system-package-manager-debian.cxx
+++ b/bpkg/system-package-manager-debian.cxx
@@ -916,6 +916,8 @@ namespace bpkg
optional<package_status> system_package_manager_debian::
status (const package_name& pn, const available_packages& aps)
{
+ tracer trace ("system_package_manager_debian::status");
+
// For now we ignore -doc and -dbg package components (but we may want to
// have options controlling this later). Note also that we assume -common
// is pulled automatically by the main package so we ignore it as well
@@ -968,6 +970,12 @@ namespace bpkg
// Keep the main package name empty as an indication that it is to
// be discovered.
//
+ // @@ It seems that quite often the header-only library -dev package
+ // name doesn't start with 'lib'. Here are some randomly chosen
+ // packages: libeigen3-dev, libmdds-dev, rapidjson-dev, etl-dev,
+ // seqan-dev, catch2. Should we implement the fallback similar to
+ // the Fedora implementation? Maybe one day.
+ //
candidates.push_back (package_status ("", n + "-dev"));
}
else
@@ -1014,8 +1022,11 @@ namespace bpkg
}
// Guess unknown main package given the -dev package and its version.
+ // Failed that, assume the package to be a binless library and leave the
+ // main member of the package_status object empty.
//
- auto guess_main = [this, &pn] (package_status& s, const string& ver)
+ auto guess_main = [this, &pn, &trace] (package_status& s,
+ const string& ver)
{
string depends (apt_cache_show (s.dev, ver));
@@ -1023,11 +1034,8 @@ namespace bpkg
if (s.main.empty ())
{
- fail << "unable to guess main " << os_release.name_id
- << " package for " << s.dev << ' ' << ver <<
- info << s.dev << " Depends value: " << depends <<
- info << "consider specifying explicit mapping in " << pn
- << " package manifest";
+ l4 ([&]{trace << "unable to guess main package for " << s.dev << ' '
+ << ver << ", Depends value: " << depends;});
}
};
@@ -1102,9 +1110,13 @@ namespace bpkg
continue;
guess_main (ps, dev.installed_version);
- pps.emplace (pps.begin (), ps.main);
- ps.package_policies_main++;
- apt_cache_policy (pps, 1);
+
+ if (!ps.main.empty ()) // Not a binless library?
+ {
+ pps.emplace (pps.begin (), ps.main);
+ ps.package_policies_main++;
+ apt_cache_policy (pps, 1);
+ }
}
optional<status_type> s (status (pps, ps.package_policies_main));
@@ -1112,7 +1124,7 @@ namespace bpkg
if (!s || *s != package_status::installed)
continue;
- const package_policy& main (pps.front ());
+ const package_policy& main (pps.front ()); // Main/dev.
ps.status = *s;
ps.system_name = main.name;
@@ -1128,17 +1140,20 @@ namespace bpkg
{
dr << fail << "multiple installed " << os_release.name_id
<< " packages for " << pn <<
- info << "candidate: " << r->main << " " << r->system_version;
+ info << "candidate: " << r->system_name << ' ' << r->system_version;
}
- dr << info << "candidate: " << ps.main << " " << ps.system_version;
+ dr << info << "candidate: " << ps.system_name << ' '
+ << ps.system_version;
}
if (!dr.empty ())
dr << info << "consider specifying the desired version manually";
}
- // Next look for available versions if we are allowed to install.
+ // Next look for available versions if we are allowed to install. Indicate
+ // the non-installable candidates by setting both their main and -dev
+ // package names to empty strings.
//
if (!r && install_)
{
@@ -1171,25 +1186,37 @@ namespace bpkg
// Note that this time we use the candidate version.
//
if (dev.candidate_version.empty ())
- continue; // Not installable.
+ {
+ // Not installable.
+ //
+ ps.dev.clear ();
+ continue;
+ }
guess_main (ps, dev.candidate_version);
- pps.emplace (pps.begin (), ps.main);
- ps.package_policies_main++;
- apt_cache_policy (pps, 1);
+
+ if (!ps.main.empty ()) // Not a binless library?
+ {
+ pps.emplace (pps.begin (), ps.main);
+ ps.package_policies_main++;
+ apt_cache_policy (pps, 1);
+ }
}
optional<status_type> s (status (pps, ps.package_policies_main));
if (!s)
{
- ps.main.clear (); // Not installable.
+ // Not installable.
+ //
+ ps.main.clear ();
+ ps.dev.clear ();
continue;
}
assert (*s != package_status::installed); // Sanity check.
- const package_policy& main (pps.front ());
+ const package_policy& main (pps.front ()); // Main/dev.
// Note that if we are installing something for this main package,
// then we always go for the candidate version even though it may
@@ -1227,13 +1254,13 @@ namespace bpkg
dr << fail << "multiple partially installed "
<< os_release.name_id << " packages for " << pn;
- dr << info << "candidate: " << r->main << " " << r->system_version
- << ", missing components:";
+ dr << info << "candidate: " << r->system_name << ' '
+ << r->system_version << ", missing components:";
print_missing (*r);
}
- dr << info << "candidate: " << ps.main << " " << ps.system_version
- << ", missing components:";
+ dr << info << "candidate: " << ps.system_name << ' '
+ << ps.system_version << ", missing components:";
print_missing (ps);
}
@@ -1248,7 +1275,7 @@ namespace bpkg
for (package_status& ps: candidates)
{
- if (ps.main.empty ())
+ if (ps.main.empty () && ps.dev.empty ()) // Not installable?
continue;
assert (ps.status == package_status::not_installed); // Sanity check.
@@ -1263,10 +1290,12 @@ namespace bpkg
{
dr << fail << "multiple available " << os_release.name_id
<< " packages for " << pn <<
- info << "candidate: " << r->main << " " << r->system_version;
+ info << "candidate: " << r->system_name << ' '
+ << r->system_version;
}
- dr << info << "candidate: " << ps.main << " " << ps.system_version;
+ dr << info << "candidate: " << ps.system_name << ' '
+ << ps.system_version;
}
if (!dr.empty ())
@@ -1938,6 +1967,12 @@ namespace bpkg
const package_name& pn (sp->name);
const version& pv (sp->version);
+ // Use version without iteration in paths, etc (`#` breaks dpkg
+ // machinery).
+ //
+ string pvs (pv.string (false /* ignore_revision */,
+ true /* ignore_iteration */));
+
const available_packages& aps (pkgs.front ().available);
bool lib (pt == "lib");
@@ -1967,6 +2002,8 @@ namespace bpkg
// As a first step, figure out the system names and version of the package
// we are generating and all the dependencies, diagnosing anything fishy.
+ // If the main package is not present for a dependency, then set the main
+ // package name to an empty string.
//
// Note that there should be no duplicate dependencies and we can sidestep
// the status cache.
@@ -1983,6 +2020,9 @@ namespace bpkg
package_status s;
if (sp->substate == package_substate::system)
{
+ // Note that for a system dependency the main package name is already
+ // empty if it is not present in the distribution.
+ //
optional<package_status> os (status (sp->name, aps));
if (!os || os->status != package_status::installed)
@@ -2007,35 +2047,28 @@ namespace bpkg
s = move (*os);
}
else
+ {
s = map_package (sp->name, sp->version, aps, build_metadata);
- sdeps.push_back (move (s));
- }
+ // Set the main package name to an empty string if we wouldn't be
+ // generating the main package for this dependency (binless library
+ // without the -common package).
+ //
+ assert (aps.size () == 1);
- if (verb >= 3)
- {
- auto print_status = [] (diag_record& dr, const package_status& s)
- {
- dr << s.main
- << (s.dev.empty () ? "" : " ") << s.dev
- << (s.doc.empty () ? "" : " ") << s.doc
- << (s.dbg.empty () ? "" : " ") << s.dbg
- << (s.common.empty () ? "" : " ") << s.common
- << ' ' << s.system_version;
- };
+ const optional<string>& t (aps.front ().first->type);
- {
- diag_record dr (trace);
- dr << "package: ";
- print_status (dr, st);
- }
+ if (s.common.empty () &&
+ package_manifest::effective_type (t, sp->name) == "lib")
+ {
+ strings sos (package_manifest::effective_type_sub_options (t));
- for (const package_status& st: sdeps)
- {
- diag_record dr (trace);
- dr << "dependency: ";
- print_status (dr, st);
+ if (find (sos.begin (), sos.end (), "binless") != sos.end ())
+ s.main.clear ();
+ }
}
+
+ sdeps.push_back (move (s));
}
if (!st.dbg.empty ())
@@ -2135,7 +2168,139 @@ namespace bpkg
if (p.second.target != nullptr)
dr << " -> " << p.second.target->first; // Symlink.
else
- dr << " " << p.second.mode;
+ dr << ' ' << p.second.mode;
+ }
+ }
+
+ // Installed entry directories for sorting out which files belong where.
+ //
+ // Let's tighten things up and only look in <private>/ (if specified) to
+ // make sure there is nothing stray.
+ //
+ string pd (priv ? pn.string () + '/' : "");
+
+ // NOTE: keep consistent with the config.install.* values above.
+ //
+ dir_path bindir ("/usr/bin/");
+ dir_path sbindir ("/usr/sbin/");
+ dir_path etcdir ("/etc/");
+ dir_path incdir ("/usr/include/" + pd);
+ dir_path incarchdir ("/usr/include/$(DEB_HOST_MULTIARCH)/" + pd);
+ dir_path libdir ("/usr/lib/$(DEB_HOST_MULTIARCH)/" + pd);
+ dir_path pkgdir (libdir / dir_path ("pkgconfig"));
+ dir_path sharedir ("/usr/share/" + pd);
+ dir_path docdir ("/usr/share/doc/" + pd);
+ dir_path mandir ("/usr/share/man/");
+
+ // As an optimization, don't generate the main and -dbgsym packages for a
+ // binless library unless it also specifies the -common package.
+ //
+ // If this is a binless library, then verify that it doesn't install any
+ // executable, library, or configuration files. Also verify that it has
+ // the -dev package.
+ //
+ bool binless (false);
+
+ if (lib)
+ {
+ assert (aps.size () == 1);
+
+ const shared_ptr<available_package>& ap (aps.front ().first);
+ strings sos (package_manifest::effective_type_sub_options (ap->type));
+
+ if (find (sos.begin (), sos.end (), "binless") != sos.end ())
+ {
+ // Verify installed files.
+ //
+ auto bad_install = [&pn, &pv] (const string& w)
+ {
+ fail << "binless library " << pn << ' ' << pv << " installs " << w;
+ };
+
+ auto verify_not_installed = [&ies, &bad_install] (const dir_path& d)
+ {
+ auto p (ies.find_sub (d));
+ if (p.first != p.second)
+ bad_install (p.first->first.string ());
+ };
+
+ verify_not_installed (bindir);
+ verify_not_installed (sbindir);
+
+ // It would probably be better not to fail here but generate the main
+ // package instead (as we do if the -common package is also being
+ // generated). Then, however, it would not be easy to detect if a
+ // dependency has the main package or not (see sdeps initialization
+ // for details).
+ //
+ verify_not_installed (etcdir);
+
+ for (auto p (ies.find_sub (libdir)); p.first != p.second; ++p.first)
+ {
+ const path& f (p.first->first);
+
+ if (!f.sub (pkgdir))
+ bad_install (f.string ());
+ }
+
+ // Verify packages.
+ //
+ if (st.dev.empty ())
+ fail << "binless library " << pn << ' ' << pv << " doesn't have "
+ << os_release.name_id << " -dev package";
+
+ binless = true;
+ }
+ }
+
+ bool gen_main (!binless || !st.common.empty ());
+
+ // If we don't generate the main package (and thus the -common package),
+ // then fail if there are any data files installed. It would probably be
+ // better not to fail but generate the main package instead in this
+ // case. Then, however, it would not be easy to detect if a dependency has
+ // the main package or not.
+ //
+ if (!gen_main)
+ {
+ for (auto p (ies.find_sub (sharedir)); p.first != p.second; ++p.first)
+ {
+ const path& f (p.first->first);
+
+ if (!f.sub (docdir) && !f.sub (mandir))
+ {
+ fail << "binless library " << pn << ' ' << pv << " installs " << f <<
+ info << "consider specifying -common package in explicit "
+ << os_release.name_id << " name mapping in package manifest";
+ }
+ }
+ }
+
+ if (verb >= 3)
+ {
+ auto print_status = [] (diag_record& dr,
+ const package_status& s,
+ const string& main)
+ {
+ dr << (main.empty () ? "" : " ") << main
+ << (s.dev.empty () ? "" : " ") << s.dev
+ << (s.doc.empty () ? "" : " ") << s.doc
+ << (s.dbg.empty () ? "" : " ") << s.dbg
+ << (s.common.empty () ? "" : " ") << s.common
+ << ' ' << s.system_version;
+ };
+
+ {
+ diag_record dr (trace);
+ dr << "package:";
+ print_status (dr, st, gen_main ? st.main : empty_string);
+ }
+
+ for (const package_status& st: sdeps)
+ {
+ diag_record dr (trace);
+ dr << "dependency:";
+ print_status (dr, st, st.main);
}
}
@@ -2159,7 +2324,7 @@ namespace bpkg
// Normally the source directory is called <name>-<upstream-version>
// (e.g., as unpacked from the source archive).
//
- dir_path src (out / dir_path (pn.string () + '-' + pv.string ()));
+ dir_path src (out / dir_path (pn.string () + '-' + pvs));
dir_path deb (src / dir_path ("debian"));
mk_p (deb);
@@ -2289,17 +2454,23 @@ namespace bpkg
string march (arch == "all" || !lib ? "foreign" : "same");
+ if (gen_main)
{
string depends;
- if (!st.common.empty ())
- depends = st.common + " (= ${binary:Version})";
-
- for (const package_status& st: sdeps)
+ auto add_depends = [&depends] (const string& v)
{
if (!depends.empty ())
depends += ", ";
+ depends += v;
+ };
+
+ if (!st.common.empty ())
+ add_depends (st.common + " (= ${binary:Version})");
+
+ for (const package_status& st: sdeps)
+ {
// Note that the constraints will include build metadata (e.g.,
// ~debian10). While it may be tempting to strip it, we cannot since
// the order is inverse. We could just make it empty `~`, though
@@ -2307,7 +2478,8 @@ namespace bpkg
// issues. Also note that the build metadata is part of the revision
// so we could strip the whole thing.
//
- depends += st.main + " (>= " + st.system_version + ')';
+ if (!st.main.empty ())
+ add_depends (st.main + " (>= " + st.system_version + ')');
}
if (ops_->debian_main_langdep_specified ())
@@ -2353,7 +2525,15 @@ namespace bpkg
if (!st.dev.empty ())
{
- string depends (st.main + " (= ${binary:Version})");
+ string depends (gen_main ? st.main + " (= ${binary:Version})" : "");
+
+ auto add_depends = [&depends] (const string& v)
+ {
+ if (!depends.empty ())
+ depends += ", ";
+
+ depends += v;
+ };
for (const package_status& st: sdeps)
{
@@ -2362,14 +2542,14 @@ namespace bpkg
// under-specify.
//
if (!st.dev.empty ())
- depends += ", " + st.dev + " (>= " + st.system_version + ')';
+ add_depends (st.dev + " (>= " + st.system_version + ')');
}
if (ops_->debian_dev_langdep_specified ())
{
if (!ops_->debian_dev_langdep ().empty ())
{
- depends += ", " + ops_->debian_dev_langdep ();
+ add_depends (ops_->debian_dev_langdep ());
}
}
else
@@ -2389,13 +2569,13 @@ namespace bpkg
// C++ (better to over- than under-specify).
//
bool cc (lang ("cc", true));
- if (cc || (cc = lang ("c++", true))) depends += ", libstdc++-dev";
- if (cc || (cc = lang ("c", true))) depends += ", libc-dev";
+ if (cc || (cc = lang ("c++", true))) add_depends ("libstdc++-dev");
+ if (cc || (cc = lang ("c", true))) add_depends ("libc-dev");
}
if (!ops_->debian_dev_extradep ().empty ())
{
- depends += ", " + ops_->debian_dev_extradep ();
+ add_depends (ops_->debian_dev_extradep ());
}
// Feels like the architecture should be the same as for the main
@@ -2427,7 +2607,7 @@ namespace bpkg
// Keep this in case we want to support it in the "starting point" mode.
//
- if (!st.dbg.empty ())
+ if (!st.dbg.empty () && !binless)
{
string depends (st.main + " (= ${binary:Version})");
@@ -2510,7 +2690,7 @@ namespace bpkg
// first and last lines with blank lines.
//
os << '\n'
- << " * New bpkg package release " << pv.string () << '.' << '\n'
+ << " * New bpkg package release " << pvs << '.' << '\n'
<< '\n';
// The last line is the "maintainer signoff" and has the following
@@ -2724,36 +2904,52 @@ namespace bpkg
os << "# *FLAGS (CFLAGS, CXXFLAGS, etc)" << '\n'
<< "#" << '\n'
- << "export DEB_BUILD_MAINT_OPTIONS :=" << mo << '\n'
+ << "export DEB_BUILD_MAINT_OPTIONS :=" << mo << '\n'
<< "include /usr/share/dpkg/buildflags.mk" << '\n'
<< '\n';
- // Fixup -ffile-prefix-map option (if specified) which is used to
- // strip source file path prefix in debug information (besides other
- // places). By default it points to the source directory. We change it
- // to point to the bpkg configuration directory. Note that this won't
- // work for external packages with source out of configuration (e.g.,
- // managed by bdep).
- //
- if (lang_c || lang_cc)
+ if (!binless)
{
- // @@ TODO: OBJCFLAGS.
+ // Fixup -ffile-prefix-map option (if specified) which is used to
+ // strip source file path prefix in debug information (besides other
+ // places). By default it points to the source directory. We change
+ // it to point to the bpkg configuration directory. Note that this
+ // won't work for external packages with source out of configuration
+ // (e.g., managed by bdep).
+ //
+ if (lang_c || lang_cc)
+ {
+ // @@ TODO: OBJCFLAGS.
- os << "CFLAGS := $(patsubst -ffile-prefix-map=%,-ffile-prefix-map="
- << cfg_dir.string () << "=.,$(CFLAGS))" << '\n'
- << '\n';
- }
+ os << "CFLAGS := $(patsubst -ffile-prefix-map=%,-ffile-prefix-map="
+ << cfg_dir.string () << "=.,$(CFLAGS))" << '\n'
+ << '\n';
+ }
- if (lang_cxx || lang_cc)
- {
- // @@ TODO: OBJCXXFLAGS.
+ if (lang_cxx || lang_cc)
+ {
+ // @@ TODO: OBJCXXFLAGS.
- os << "CXXFLAGS := $(patsubst -ffile-prefix-map=%,-ffile-prefix-map="
- << cfg_dir.string () << "=.,$(CXXFLAGS))" << '\n'
- << '\n';
+ os << "CXXFLAGS := $(patsubst -ffile-prefix-map=%,-ffile-prefix-map="
+ << cfg_dir.string () << "=.,$(CXXFLAGS))" << '\n'
+ << '\n';
+ }
}
}
+ // For a binless library the -dbgsym package is not supposed to be
+ // generated. Thus, we disable its automatic generation by adding the
+ // noautodbgsym flag to the DEB_BUILD_OPTIONS variable.
+ //
+ // This doesn't seem to be necessary (probably because there is no
+ // .so/.a).
+ //
+#if 0
+ if (binless)
+ os << "export DEB_BUILD_OPTIONS += noautodbgsym" << '\n'
+ << '\n';
+#endif
+
// The debian/tmp/ subdirectory appears to be the canonical destination
// directory (see dh_auto_install(1) for details).
//
@@ -3011,7 +3207,7 @@ namespace bpkg
}
};
- open (main, st.main);
+ open (main, gen_main ? st.main : empty_string);
open (dev, st.dev);
open (doc, st.doc);
open (dbg, st.dbg);
@@ -3040,37 +3236,24 @@ namespace bpkg
os.second << s << '\n';
};
- // Let's tighten things up and only look in <private>/ (if specified) to
- // make sure there is nothing stray.
- //
- string pd (priv ? pn.string () + '/' : "");
-
- // NOTE: keep consistent with the config.install.* values above.
- //
- dir_path bindir ("/usr/bin/");
- dir_path sbindir ("/usr/sbin/");
- dir_path etcdir ("/etc/");
- dir_path incdir ("/usr/include/" + pd);
- dir_path incarchdir ("/usr/include/$(DEB_HOST_MULTIARCH)/" + pd);
- dir_path libdir ("/usr/lib/$(DEB_HOST_MULTIARCH)/" + pd);
- dir_path pkgdir (libdir / dir_path ("pkgconfig"));
- dir_path sharedir ("/usr/share/" + pd);
- dir_path docdir ("/usr/share/doc/" + pd);
- dir_path mandir ("/usr/share/man/");
-
// The main package contains everything that doesn't go to another
// packages.
//
- if (ies.contains_sub (bindir)) add (main, bindir / "*");
- if (ies.contains_sub (sbindir)) add (main, sbindir / "*");
+ if (gen_main)
+ {
+ if (ies.contains_sub (bindir)) add (main, bindir / "*");
+ if (ies.contains_sub (sbindir)) add (main, sbindir / "*");
- // This could potentially go to -common but it could also be target-
- // specific, who knows. So let's keep it in main for now.
- //
- if (ies.contains_sub (etcdir)) add (main, etcdir / "*");
+ // This could potentially go to -common but it could also be target-
+ // specific, who knows. So let's keep it in main for now.
+ //
+ if (ies.contains_sub (etcdir)) add (main, etcdir / "*");
+ }
if (!is_open (dev))
{
+ assert (gen_main); // Shouldn't be here otherwise.
+
if (ies.contains_sub (incdir)) add (main, incdir / "*");
if (ies.contains_sub (incarchdir)) add (main, incarchdir / "*");
if (ies.contains_sub (libdir)) add (main, libdir / "*");
@@ -3125,6 +3308,8 @@ namespace bpkg
if (l.simple ())
{
+ assert (gen_main); // Shouldn't be here otherwise.
+
string e (l.extension ());
const string& n (l.string ());
@@ -3141,6 +3326,11 @@ namespace bpkg
//
dir_path d (libdir / dir_path (*l.begin ()));
+ // Can only be a subdirectory of pkgdir/ if the main package is
+ // not being generated.
+ //
+ assert (d == pkgdir || gen_main);
+
add (d == pkgdir ? dev : main, d / "*");
// Skip all the other entries in this subdirectory (in the prefix
@@ -3156,31 +3346,34 @@ namespace bpkg
// below. So we have to list all the top-level entries in usr/share/
// that are not doc/ or man/.
//
- for (auto p (ies.find_sub (sharedir)); p.first != p.second; )
+ if (gen_main)
{
- const path& f ((p.first++)->first);
+ for (auto p (ies.find_sub (sharedir)); p.first != p.second; )
+ {
+ const path& f ((p.first++)->first);
- if (f.sub (docdir) || f.sub (mandir))
- continue;
+ if (f.sub (docdir) || f.sub (mandir))
+ continue;
- path l (f.leaf (sharedir));
+ path l (f.leaf (sharedir));
- if (l.simple ())
- add (is_open (com) ? com : main, sharedir / l);
- else
- {
- // Let's keep things tidy and use a wildcard rather than listing all
- // the entries in subdirectories verbatim.
- //
- dir_path d (sharedir / dir_path (*l.begin ()));
+ if (l.simple ())
+ add (is_open (com) ? com : main, sharedir / l);
+ else
+ {
+ // Let's keep things tidy and use a wildcard rather than listing
+ // all the entries in subdirectories verbatim.
+ //
+ dir_path d (sharedir / dir_path (*l.begin ()));
- add (is_open (com) ? com : main, d / "*");
+ add (is_open (com) ? com : main, d / "*");
- // Skip all the other entries in this subdirectory (in the prefix
- // map they will all be in a contiguous range).
- //
- while (p.first != p.second && p.first->first.sub (d))
- ++p.first;
+ // Skip all the other entries in this subdirectory (in the prefix
+ // map they will all be in a contiguous range).
+ //
+ while (p.first != p.second && p.first->first.sub (d))
+ ++p.first;
+ }
}
}
@@ -3195,6 +3388,11 @@ namespace bpkg
is_open (dev) ? dev :
main);
+ // We can only add doc files to the main or -common packages if we
+ // generate the main package.
+ //
+ assert ((&os != &main && &os != &com) || gen_main);
+
if (ies.contains_sub (docdir)) add (os, docdir / "*");
if (ies.contains_sub (mandir)) add (os, mandir / "*");
}
@@ -3339,8 +3537,9 @@ namespace bpkg
//
const string& ver (st.system_version);
- add (st.main + '_' + ver + '_' + arch + ".deb");
- add (st.main + "-dbgsym_" + ver + '_' + arch + ".deb", true);
+ if (gen_main) add (st.main + '_' + ver + '_' + arch + ".deb");
+ if (!binless) add (st.main + "-dbgsym_" + ver + '_' + arch + ".deb", true);
+
if (!st.dev.empty ()) add (st.dev + '_' + ver + '_' + arch + ".deb");
if (!st.doc.empty ()) add (st.doc + '_' + ver + "_all.deb");
if (!st.common.empty ()) add (st.common + '_' + ver + "_all.deb");
diff --git a/bpkg/system-package-manager-debian.hxx b/bpkg/system-package-manager-debian.hxx
index 08a2d02..eb8b214 100644
--- a/bpkg/system-package-manager-debian.hxx
+++ b/bpkg/system-package-manager-debian.hxx
@@ -37,7 +37,9 @@ namespace bpkg
// libcurl3-gnutls libcurl4-gnutls-dev libcurl4-doc (yes, 3 and 4)
//
// Note that while most library package names in Debian start with lib (per
- // the policy), there are exceptions (e.g., zlib1g zlib1g-dev).
+ // the policy), there are exceptions (e.g., zlib1g zlib1g-dev). The
+ // header-only library package names may or may not start with lib and end
+ // with -dev (e.g., libeigen3-dev, rapidjson-dev, catch2).
//
// Also note that manual -dbg packages are obsolete in favor of automatic
// -dbgsym packages from Debian 9. So while we support -dbg for consumption,
diff --git a/bpkg/system-package-manager-fedora.cxx b/bpkg/system-package-manager-fedora.cxx
index 9c75618..4d12360 100644
--- a/bpkg/system-package-manager-fedora.cxx
+++ b/bpkg/system-package-manager-fedora.cxx
@@ -1121,6 +1121,8 @@ namespace bpkg
optional<package_status> system_package_manager_fedora::
status (const package_name& pn, const available_packages& aps)
{
+ tracer trace ("system_package_manager_fedora::status");
+
// For now we ignore -doc and -debug* package components (but we may want
// to have options controlling this later). Note also that we assume
// -common is pulled automatically by the base package so we ignore it as
@@ -1246,12 +1248,13 @@ namespace bpkg
}
// Guess unknown main package given the -devel package, its version, and
- // architecture.
+ // architecture. Failed that, assume the package to be a binless library
+ // and leave the main member of the package_status object empty.
//
- auto guess_main = [this, &pn] (package_status& s,
- const string& ver,
- const string& qarch,
- bool installed)
+ auto guess_main = [this, &pn, &trace] (package_status& s,
+ const string& ver,
+ const string& qarch,
+ bool installed)
{
vector<pair<string, string>> depends (
dnf_repoquery_requires (s.devel, ver, qarch, installed));
@@ -1260,19 +1263,21 @@ namespace bpkg
if (s.main.empty ())
{
- diag_record dr (fail);
- dr << "unable to guess main " << os_release.name_id
- << " package for " << s.devel << ' ' << ver <<
- info << "depends on";
+ if (verb >= 4)
+ {
+ diag_record dr (trace);
+ dr << "unable to guess main package for " << s.devel << ' ' << ver;
+ if (!depends.empty ())
+ {
+ dr << ", depends on";
- for (auto b (depends.begin ()), i (b); i != depends.end (); ++i)
- {
- dr << (i == b ? " " : ", ") << i->first << ' ' << i->second;
+ for (auto b (depends.begin ()), i (b); i != depends.end (); ++i)
+ dr << (i == b ? " " : ", ") << i->first << ' ' << i->second;
+ }
+ else
+ dr << ", has no dependencies";
}
-
- dr << info << "consider specifying explicit mapping in " << pn
- << " package manifest";
}
};
@@ -1403,9 +1408,12 @@ namespace bpkg
devel.installed_arch,
true /* installed */);
- pis.emplace (pis.begin (), ps.main);
- ps.package_infos_main++;
- dnf_list (pis, 1);
+ if (!ps.main.empty ()) // Not a binless library?
+ {
+ pis.emplace (pis.begin (), ps.main);
+ ps.package_infos_main++;
+ dnf_list (pis, 1);
+ }
}
optional<status_type> s (status (pis, ps.package_infos_main));
@@ -1413,7 +1421,7 @@ namespace bpkg
if (!s || *s != package_status::installed)
continue;
- const package_info& main (pis.front ());
+ const package_info& main (pis.front ()); // Main/devel.
ps.status = *s;
ps.system_name = main.name;
@@ -1429,17 +1437,20 @@ namespace bpkg
{
dr << fail << "multiple installed " << os_release.name_id
<< " packages for " << pn <<
- info << "candidate: " << r->main << " " << r->system_version;
+ info << "candidate: " << r->system_name << ' ' << r->system_version;
}
- dr << info << "candidate: " << ps.main << " " << ps.system_version;
+ dr << info << "candidate: " << ps.system_name << ' '
+ << ps.system_version;
}
if (!dr.empty ())
dr << info << "consider specifying the desired version manually";
}
- // Next look for available versions if we are allowed to install.
+ // Next look for available versions if we are allowed to install. Indicate
+ // the non-installable candidates by setting both their main and -devel
+ // package names to empty strings.
//
if (!r && install_)
{
@@ -1476,11 +1487,13 @@ namespace bpkg
//
if (mp.candidate_version.empty ())
{
+ string& main (!ps.main.empty () ? ps.main : ps.devel);
+
if (!fp.candidate_version.empty ())
{
// Use the fallback.
//
- (ps.main.empty () ? ps.devel : ps.main) = move (ps.fallback);
+ main = move (ps.fallback);
mp = move (fp);
}
else
@@ -1493,7 +1506,7 @@ namespace bpkg
// Main/devel package is not installable.
//
- ps.main.clear ();
+ main.clear ();
continue;
}
}
@@ -1514,29 +1527,40 @@ namespace bpkg
// Note that this time we use the candidate version.
//
if (devel.candidate_version.empty ())
- continue; // Not installable.
+ {
+ // Not installable.
+ //
+ ps.devel.clear ();
+ continue;
+ }
guess_main (ps,
devel.candidate_version,
devel.candidate_arch,
devel.candidate_version == devel.installed_version);
- pis.emplace (pis.begin (), ps.main);
- ps.package_infos_main++;
- dnf_list (pis, 1);
+ if (!ps.main.empty ()) // Not a binless library?
+ {
+ pis.emplace (pis.begin (), ps.main);
+ ps.package_infos_main++;
+ dnf_list (pis, 1);
+ }
}
optional<status_type> s (status (pis, ps.package_infos_main));
if (!s)
{
- ps.main.clear (); // Not installable.
+ // Not installable.
+ //
+ ps.main.clear ();
+ ps.devel.clear ();
continue;
}
assert (*s != package_status::installed); // Sanity check.
- const package_info& main (pis.front ());
+ const package_info& main (pis.front ()); // Main/devel.
// Note that if we are installing something for this main package,
// then we always go for the candidate version even though it may
@@ -1575,13 +1599,13 @@ namespace bpkg
dr << fail << "multiple partially installed "
<< os_release.name_id << " packages for " << pn;
- dr << info << "candidate: " << r->main << " " << r->system_version
- << ", missing components:";
+ dr << info << "candidate: " << r->system_name << ' '
+ << r->system_version << ", missing components:";
print_missing (*r);
}
- dr << info << "candidate: " << ps.main << " " << ps.system_version
- << ", missing components:";
+ dr << info << "candidate: " << ps.system_name << ' '
+ << ps.system_version << ", missing components:";
print_missing (ps);
}
@@ -1596,7 +1620,7 @@ namespace bpkg
for (package_status& ps: candidates)
{
- if (ps.main.empty ())
+ if (ps.main.empty () && ps.devel.empty ()) // Not installable?
continue;
assert (ps.status == package_status::not_installed); // Sanity check.
@@ -1611,10 +1635,12 @@ namespace bpkg
{
dr << fail << "multiple available " << os_release.name_id
<< " packages for " << pn <<
- info << "candidate: " << r->main << " " << r->system_version;
+ info << "candidate: " << r->system_name << ' '
+ << r->system_version;
}
- dr << info << "candidate: " << ps.main << " " << ps.system_version;
+ dr << info << "candidate: " << ps.system_name << ' '
+ << ps.system_version;
}
if (!dr.empty ())
@@ -2292,6 +2318,11 @@ namespace bpkg
const package_name& pn (sp->name);
const version& pv (sp->version);
+ // Use version without iteration in paths, etc.
+ //
+ string pvs (pv.string (false /* ignore_revision */,
+ true /* ignore_iteration */));
+
const available_packages& aps (pkgs.front ().available);
bool lib (pt == "lib");
@@ -2321,6 +2352,8 @@ namespace bpkg
// As a first step, figure out the system names and version of the package
// we are generating and all the dependencies, diagnosing anything fishy.
+ // If the main package is not present for a dependency, then set the main
+ // package name to an empty string.
//
// Note that there should be no duplicate dependencies and we can sidestep
// the status cache.
@@ -2337,6 +2370,9 @@ namespace bpkg
package_status s;
if (sp->substate == package_substate::system)
{
+ // Note that for a system dependency the main package name is already
+ // empty if it is not present in the distribution.
+ //
optional<package_status> os (status (sp->name, aps));
if (!os || os->status != package_status::installed)
@@ -2374,37 +2410,28 @@ namespace bpkg
v.resize (p);
}
else
+ {
s = map_package (sp->name, sp->version, aps);
- sdeps.push_back (move (s));
- }
+ // Set the main package name to an empty string if we wouldn't be
+ // generating the main package for this dependency (binless library
+ // without the -common sub-package).
+ //
+ assert (aps.size () == 1);
- if (verb >= 3)
- {
- auto print_status = [] (diag_record& dr, const package_status& s)
- {
- dr << s.main
- << (s.devel.empty () ? "" : " ") << s.devel
- << (s.static_.empty () ? "" : " ") << s.static_
- << (s.doc.empty () ? "" : " ") << s.doc
- << (s.debuginfo.empty () ? "" : " ") << s.debuginfo
- << (s.debugsource.empty () ? "" : " ") << s.debugsource
- << (s.common.empty () ? "" : " ") << s.common
- << ' ' << s.system_version;
- };
+ const optional<string>& t (aps.front ().first->type);
- {
- diag_record dr (trace);
- dr << "package: ";
- print_status (dr, st);
- }
+ if (s.common.empty () &&
+ package_manifest::effective_type (t, sp->name) == "lib")
+ {
+ strings sos (package_manifest::effective_type_sub_options (t));
- for (const package_status& st: sdeps)
- {
- diag_record dr (trace);
- dr << "dependency: ";
- print_status (dr, st);
+ if (find (sos.begin (), sos.end (), "binless") != sos.end ())
+ s.main.clear ();
+ }
}
+
+ sdeps.push_back (move (s));
}
// We only allow the standard -debug* sub-package names.
@@ -2412,12 +2439,12 @@ namespace bpkg
if (!st.debuginfo.empty () && st.debuginfo != st.main + "-debuginfo")
fail << "generation of -debuginfo packages with custom names not "
<< "supported" <<
- info << "use " << st.main + "-debuginfo name instead";
+ info << "use " << st.main << "-debuginfo name instead";
if (!st.debugsource.empty () && st.debuginfo != st.main + "-debugsource")
fail << "generation of -debugsource packages with custom names not "
<< "supported" <<
- info << "use " << st.main + "-debugsource name instead";
+ info << "use " << st.main << "-debugsource name instead";
// Prepare the common extra options that need to be passed to both
// rpmbuild and rpm.
@@ -2559,8 +2586,8 @@ namespace bpkg
//
strings expansions;
- // These are used for sorting out the installed files into the %files
- // sections of the sub-packages.
+ // Installed entry directories for sorting out the installed files into
+ // the %files sections of the sub-packages.
//
dir_path bindir;
dir_path sbindir;
@@ -2760,7 +2787,126 @@ namespace bpkg
if (p.second.target != nullptr)
dr << " -> " << p.second.target->first; // Symlink.
else
- dr << " " << p.second.mode;
+ dr << ' ' << p.second.mode;
+ }
+ }
+
+ // As an optimization, don't generate the main and -debug* packages for a
+ // binless library unless it also specifies the -common sub-package.
+ //
+ // If this is a binless library, then verify that it doesn't install any
+ // executable, library, or configuration files. Also verify that it has
+ // the -devel sub-package but doesn't specify the -static sub-package.
+ //
+ bool binless (false);
+
+ if (lib)
+ {
+ assert (aps.size () == 1);
+
+ const shared_ptr<available_package>& ap (aps.front ().first);
+ strings sos (package_manifest::effective_type_sub_options (ap->type));
+
+ if (find (sos.begin (), sos.end (), "binless") != sos.end ())
+ {
+ // Verify installed files.
+ //
+ auto bad_install = [&pn, &pv] (const string& w)
+ {
+ fail << "binless library " << pn << ' ' << pv << " installs " << w;
+ };
+
+ auto verify_not_installed = [&ies, &bad_install] (const dir_path& d)
+ {
+ auto p (ies.find_sub (d));
+ if (p.first != p.second)
+ bad_install (p.first->first.string ());
+ };
+
+ verify_not_installed (bindir);
+ verify_not_installed (sbindir);
+ verify_not_installed (libexecdir);
+
+ // It would probably be better not to fail here but generate the main
+ // package instead (as we do if the -common sub-package is also being
+ // generated). Then, however, it would not be easy to detect if a
+ // dependency has the main package or not (see sdeps initialization
+ // for details).
+ //
+ verify_not_installed (confdir);
+
+ for (auto p (ies.find_sub (libdir)); p.first != p.second; ++p.first)
+ {
+ const path& f (p.first->first);
+
+ if (!f.sub (pkgdir))
+ bad_install (f.string ());
+ }
+
+ // Verify sub-packages.
+ //
+ if (st.devel.empty ())
+ fail << "binless library " << pn << ' ' << pv << " doesn't have "
+ << os_release.name_id << " -devel package";
+
+ if (!st.static_.empty ())
+ fail << "binless library " << pn << ' ' << pv << " has "
+ << os_release.name_id << ' ' << st.static_ << " package";
+
+ binless = true;
+ }
+ }
+
+ bool gen_main (!binless || !st.common.empty ());
+
+ // If we don't generate the main package (and thus the -common
+ // sub-package), then fail if there are any data files installed. It would
+ // probably be better not to fail but generate the main package instead in
+ // this case. Then, however, it would not be easy to detect if a
+ // dependency has the main package or not.
+ //
+ if (!gen_main)
+ {
+ for (auto p (ies.find_sub (sharedir)); p.first != p.second; ++p.first)
+ {
+ const path& f (p.first->first);
+
+ if (!f.sub (docdir) && !f.sub (mandir) && !f.sub (licensedir))
+ {
+ fail << "binless library " << pn << ' ' << pv << " installs " << f <<
+ info << "consider specifying -common package in explicit "
+ << os_release.name_id << " name mapping in package manifest";
+ }
+ }
+ }
+
+ if (verb >= 3)
+ {
+ auto print_status = [] (diag_record& dr,
+ const package_status& s,
+ const string& main)
+ {
+ dr << (main.empty () ? "" : " ") << main
+ << (s.devel.empty () ? "" : " ") << s.devel
+ << (s.static_.empty () ? "" : " ") << s.static_
+ << (s.doc.empty () ? "" : " ") << s.doc
+ << (s.debuginfo.empty () ? "" : " ") << s.debuginfo
+ << (s.debugsource.empty () ? "" : " ") << s.debugsource
+ << (s.common.empty () ? "" : " ") << s.common
+ << ' ' << s.system_version;
+ };
+
+ {
+ diag_record dr (trace);
+ dr << "package:";
+ print_status (dr, st, gen_main ? st.main : empty_string);
+ }
+
+ for (const package_status& s: sdeps)
+ {
+ diag_record dr (trace);
+ dr << "dependency:";
+ print_status (dr, s, s.main);
}
}
@@ -3025,10 +3171,11 @@ namespace bpkg
// (it's commonly the contents of the README.md file).
//
// We will disable automatic dependency discovery for all sub-packages
- // using the `AutoReqProv: no` directive.
+ // using the `AutoReqProv: no` directive since we have an accurate set
+ // and some of them may not be system packages.
//
- // The main package.
+ // The common information and the main package.
//
{
os << "Name: " << st.main << '\n'
@@ -3045,9 +3192,6 @@ namespace bpkg
if (!packager.empty ())
os << "Packager: " << packager << '\n';
- if (!build_arch.empty ())
- os << "BuildArch: " << build_arch << '\n';
-
#if 0
os << "#Source: https://pkg.cppget.org/1/???/"
<< pm.effective_project () << '/' << sp->name << '-'
@@ -3059,32 +3203,54 @@ namespace bpkg
os << '\n'
<< "%global evr %{?epoch:%{epoch}:}%{version}-%{release}" << '\n';
- os << '\n'
- << "# " << st.main << '\n'
- << "#" << '\n'
- << "AutoReqProv: no" << '\n';
-
- // Requires directives.
- //
+ if (gen_main)
{
- bool first (true);
- if (!st.common.empty ())
- add_requires (first, st.common + " = %{evr}");
+ os << '\n'
+ << "# " << st.main << '\n'
+ << "#" << '\n';
- for (const package_status& s: sdeps)
- add_requires (first, s.main + " >= " + s.system_version);
+ if (!build_arch.empty ())
+ os << "BuildArch: " << build_arch << '\n';
- add_lang_requires (first,
- "" /* suffix */,
- ops_->fedora_main_langreq ());
+ os << "AutoReqProv: no" << '\n';
- if (ops_->fedora_main_extrareq_specified ())
- add_requires_list (first, ops_->fedora_main_extrareq ());
+ // Requires directives.
+ //
+ {
+ bool first (true);
+ if (!st.common.empty ())
+ add_requires (first, st.common + " = %{evr}");
+
+ for (const package_status& s: sdeps)
+ {
+ if (!s.main.empty ())
+ add_requires (first, s.main + " >= " + s.system_version);
+ }
+
+ add_lang_requires (first,
+ "" /* suffix */,
+ ops_->fedora_main_langreq ());
+
+ if (ops_->fedora_main_extrareq_specified ())
+ add_requires_list (first, ops_->fedora_main_extrareq ());
+ }
}
- os << '\n'
- << "%description" << '\n'
- << "This package contains the runtime files." << '\n';
+ // Note that we need to add the %description directive regardless if
+ // the main package is being generated or not.
+ //
+ if (!binless)
+ {
+ os << '\n'
+ << "%description" << '\n'
+ << "This package contains the runtime files." << '\n';
+ }
+ else
+ {
+ os << '\n'
+ << "%description" << '\n'
+ << "This package contains the development files." << '\n';
+ }
}
// The -devel sub-package.
@@ -3113,7 +3279,8 @@ namespace bpkg
// Dependency on the main package.
//
- add_requires (first, "%{name}" + isa + " = %{evr}");
+ if (gen_main)
+ add_requires (first, "%{name}" + isa + " = %{evr}");
for (const package_status& s: sdeps)
{
@@ -3205,6 +3372,9 @@ namespace bpkg
// use. Thus, add dependency on the -devel or main sub-package, if
// not being generated.
//
+ // Note that if there is no -devel package, then this cannot be a
+ // binless library and thus the main package is being generated.
+ //
add_requires (
first,
(!st.devel.empty () ? st.devel : "%{name}") + isa + " = %{evr}");
@@ -3329,37 +3499,46 @@ namespace bpkg
// Also note that we disable generating the -debugsource sub-packages
// (see the generate() description above for the reasoning).
//
- os << "%undefine _debugsource_packages" << '\n';
-
- // Append the -ffile-prefix-map option (if specified) which is used to
- // strip source file path prefix in debug information (besides other
- // places). By default it is not used, presumably since we disable
- // generating the -debugsource sub-packages. We change it to point to
- // the bpkg configuration directory. Note that this won't work for
- // external packages with source out of configuration (e.g., managed
- // by bdep).
- //
- // @@ Supposedly this code won't be necessary when we add support for
- // -debugsource sub-packages.
- //
- // Note that adding this option may also result in notification
- // messages for probably all the source files as following:
- //
- // cpio: libfoo-1.2.3/foo.cxx: Cannot stat: No such file or directory
+ // For a binless library no -debug* packages are supposed to be
+ // generated. Thus, we just drop their definitions by redefining the
+ // %{debug_package} macro as an empty string.
//
- // This bloats the rpmbuild output but doesn't seem to break
- // anything.
- //
- if (ops_->fedora_buildflags () != "ignore")
+ if (!binless)
{
- if (lang_c || lang_cc)
- os << "%global build_cflags %{?build_cflags} -ffile-prefix-map="
- << cfg_dir.string () << "=." << '\n';
+ os << "%undefine _debugsource_packages" << '\n';
+
+ // Append the -ffile-prefix-map option (if specified) which is used
+ // to strip source file path prefix in debug information (besides
+ // other places). By default it is not used, presumably since we
+ // disable generating the -debugsource sub-packages. We change it to
+ // point to the bpkg configuration directory. Note that this won't
+ // work for external packages with source out of configuration
+ // (e.g., managed by bdep).
+ //
+ // @@ Supposedly this code won't be necessary when we add support for
+ // -debugsource sub-packages.
+ //
+ // Note that adding this option may also result in notification
+ // messages for probably all the source files as following:
+ //
+ // cpio: libfoo-1.2.3/foo.cxx: Cannot stat: No such file or directory
+ //
+ // This bloats the rpmbuild output but doesn't seem to break
+ // anything.
+ //
+ if (ops_->fedora_buildflags () != "ignore")
+ {
+ if (lang_c || lang_cc)
+ os << "%global build_cflags %{?build_cflags} -ffile-prefix-map="
+ << cfg_dir.string () << "=." << '\n';
- if (lang_cxx || lang_cc)
- os << "%global build_cxxflags %{?build_cxxflags} -ffile-prefix-map="
- << cfg_dir.string () << "=." << '\n';
+ if (lang_cxx || lang_cc)
+ os << "%global build_cxxflags %{?build_cxxflags} -ffile-prefix-map="
+ << cfg_dir.string () << "=." << '\n';
+ }
}
+ else
+ os << "%global debug_package %{nil}" << '\n';
// Common arguments for build2 commands.
//
@@ -3372,8 +3551,8 @@ namespace bpkg
cstrings verb_args; string verb_arg;
map_verb_b (*ops_, verb_b::normal, verb_args, verb_arg);
- os << '\n'
- << "%global build2 " << search_b (*ops_).effect_string ();
+ os << '\n'
+ << "%global build2 " << search_b (*ops_).effect_string ();
for (const char* o: verb_args) os << ' ' << o;
for (const string& o: ops_->build_option ()) os << ' ' << o;
@@ -3581,25 +3760,28 @@ namespace bpkg
// The main package contains everything that doesn't go to another
// packages.
//
- if (ies.contains_sub (bindir)) main += "%{_bindir}/*\n";
- if (ies.contains_sub (sbindir)) main += "%{_sbindir}/*\n";
+ if (gen_main)
+ {
+ if (ies.contains_sub (bindir)) main += "%{_bindir}/*\n";
+ if (ies.contains_sub (sbindir)) main += "%{_sbindir}/*\n";
- if (ies.contains_sub (libexecdir))
- main += "%{_libexecdir}/" + (priv ? pd : "*") + '\n';
+ if (ies.contains_sub (libexecdir))
+ main += "%{_libexecdir}/" + (priv ? pd : "*") + '\n';
- // This could potentially go to -common but it could also be target-
- // specific, who knows. So let's keep it in main for now.
- //
- // Let's also specify that the confdir/ sub-entries are non-replacable
- // configuration files. This, in particular, means that if edited they
- // will not be replaced/removed on the package upgrade or
- // uninstallation (see RPM Packaging Guide for more details on the
- // %config(noreplace) directive). Also note that the binary package
- // configuration files can later be queried by the user via the `rpm
- // --query --configfiles` command.
- //
- if (ies.contains_sub (confdir))
- main += "%config(noreplace) %{_sysconfdir}/*\n";
+ // This could potentially go to -common but it could also be target-
+ // specific, who knows. So let's keep it in main for now.
+ //
+ // Let's also specify that the confdir/ sub-entries are
+ // non-replacable configuration files. This, in particular, means
+ // that if edited they will not be replaced/removed on the package
+ // upgrade or uninstallation (see RPM Packaging Guide for more
+ // details on the %config(noreplace) directive). Also note that the
+ // binary package configuration files can later be queried by the
+ // user via the `rpm --query --configfiles` command.
+ //
+ if (ies.contains_sub (confdir))
+ main += "%config(noreplace) %{_sysconfdir}/*\n";
+ }
if (ies.contains_sub (incdir))
(!st.devel.empty () ? devel : main) +=
@@ -3607,6 +3789,8 @@ namespace bpkg
if (st.devel.empty () && st.static_.empty ())
{
+ assert (gen_main); // Shouldn't be here otherwise.
+
if (ies.contains_sub (libdir))
main += "%{_libdir}/" + (priv ? pd : "*") + '\n';
}
@@ -3751,17 +3935,20 @@ namespace bpkg
else
{
fs = &devel;
- *fs += "%{_libdir}/" + pd + sd.string () + '/';
+ *fs += "%{_libdir}/" + pd + sd.string () + (priv ? "/" : "/*");
}
}
else
*fs += "%{_libdir}/" + pd + sd.string () + '/';
- // In the case of the directory (has the trailing slash) skip
- // all the other entries in this subdirectory (in the prefix map
- // they will all be in a contiguous range).
+ // In the case of the directory (has the trailing slash) or
+ // wildcard (has the trailing asterisk) skip all the other
+ // entries in this subdirectory (in the prefix map they will all
+ // be in a contiguous range).
//
- if (fs->back () == '/')
+ char c (fs->back ());
+
+ if (c == '/' || c == '*')
{
while (p.first != p.second && p.first->first.sub (d))
++p.first;
@@ -3770,6 +3957,10 @@ namespace bpkg
*fs += '\n';
}
+ // We can only add files to the main package if we generate it.
+ //
+ assert (fs != &main || gen_main);
+
// Update the index of a sub-package which should own
// libdir/<private>/.
//
@@ -3783,13 +3974,15 @@ namespace bpkg
*owners[*private_owner] += "%dir %{_libdir}/" + pd + '\n';
if (pkgconfig_owner)
- *owners[*pkgconfig_owner] += "%dir %{_libdir}/" + pd + "pkgconfig/" + '\n';
+ *owners[*pkgconfig_owner] +=
+ "%dir %{_libdir}/" + pd + "pkgconfig/" + '\n';
}
// We cannot just do usr/share/* since it will clash with doc/, man/,
// and licenses/ below. So we have to list all the top-level entries
// in usr/share/ that are not doc/, man/, or licenses/.
//
+ if (gen_main)
{
// Note that if <private>/ is specified, then we also need to
// establish ownership of the sharedir/<private>/ directory (similar
@@ -3819,7 +4012,7 @@ namespace bpkg
//
dir_path sd (*l.begin ());
- fs += "%{_datadir}/" + pd + sd.string () + '\n';
+ fs += "%{_datadir}/" + pd + sd.string () + '/' + '\n';
// Skip all the other entries in this subdirectory (in the prefix
// map they will all be in a contiguous range).
@@ -3853,6 +4046,11 @@ namespace bpkg
!st.devel.empty () ? devel :
main);
+ // We can only add doc files to the main or -common packages if we
+ // generate the main package.
+ //
+ assert ((&fs != &main && &fs != &common) || gen_main);
+
// Let's specify that the docdir/ sub-entries are documentation
// files. Note that the binary package documentation files can later
// be queried by the user via the `rpm --query --docfiles` command.
@@ -3877,12 +4075,15 @@ namespace bpkg
// the user via the `rpm --query --licensefiles` command.
//
if (ies.contains_sub (licensedir))
- main += "%license %{_licensedir}/" + (priv ? pd : "*") + '\n';
+ (gen_main ? main : devel) +=
+ "%license %{_licensedir}/" + (priv ? pd : "*") + '\n';
// Finally, write the %files sections.
//
if (!main.empty ())
{
+ assert (gen_main); // Shouldn't be here otherwise.
+
os << '\n'
<< "# " << st.main << " files." << '\n'
<< "#" << '\n'
@@ -3970,7 +4171,7 @@ namespace bpkg
os << sys_version.epoch << ':';
os << sys_version.version << '-' << sys_version.release << '\n'
- << "- New bpkg package release " << pv.string () << '.' << '\n';
+ << "- New bpkg package release " << pvs << '.' << '\n';
}
os.close ();
@@ -4118,7 +4319,8 @@ namespace bpkg
return np++;
};
- add_package (st.main, package_arch);
+ if (gen_main)
+ add_package (st.main, package_arch);
if (!st.devel.empty ())
add_package (st.devel, package_arch);
@@ -4132,7 +4334,9 @@ namespace bpkg
if (!st.common.empty ())
add_package (st.common, "noarch");
- size_t di (add_package (st.main + "-debuginfo", arch));
+ optional<size_t> di (!binless
+ ? add_package (st.main + "-debuginfo", arch)
+ : optional<size_t> ());
// Strip the trailing newline since rpm adds one.
//
@@ -4161,7 +4365,7 @@ namespace bpkg
//
if (exists (p))
r.push_back (move (p));
- else if (i != di) // Not a -debuginfo sub-package?
+ else if (!di || i != *di) // Not a -debuginfo sub-package?
fail << "expected output file " << p << " does not exist";
}
catch (const invalid_path& e)
diff --git a/bpkg/system-package-manager-fedora.hxx b/bpkg/system-package-manager-fedora.hxx
index 609df48..672b2a1 100644
--- a/bpkg/system-package-manager-fedora.hxx
+++ b/bpkg/system-package-manager-fedora.hxx
@@ -50,6 +50,10 @@ namespace bpkg
//
// zlib zlib-devel zlib-static
//
+ // catch-devel
+ //
+ // eigen3-devel eigen3-doc
+ //
// xerces-c xerces-c-devel xerces-c-doc
//
// libsigc++20 libsigc++20-devel libsigc++20-doc
diff --git a/doc/manual.cli b/doc/manual.cli
index 29e5535..a7c1336 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -1137,7 +1137,7 @@ information.
[type]: <type>
[language]: <lang>
-<type> = <name>
+<type> = <name>[,<sub-options>]
<lang> = <name>[=impl]
\
@@ -1148,7 +1148,14 @@ the type is not specified, then if the package name starts with \c{lib}, then
it is assumed to be \c{lib} and \c{exe} otherwise (see \l{#package-name
Package Name} for details). Other package types may be added in the future and
code that does not recognize a certain package type should treat it as
-\c{other}.
+\c{other}. The type name can be followed by a comma-separated list of
+sub-options. Currently, the only recognized sub-option is \c{binless} which
+applies to the \c{lib} type indicating a header-only (or equivalent) library.
+For example:
+
+\
+type: lib,binless
+\
The package language must be in the lower case, for example, \c{c}, \c{c++},
\c{rust}, \c{bash}. If the language is not specified, then if the package name
@@ -3014,9 +3021,11 @@ libcurl3-gnutls libcurl4-gnutls-dev libcurl4-doc
\
Note that while most library package names in Debian start with \c{lib} (per
-the policy), there are exceptions (e.g., \c{zlib1g} \c{zlib1g-dev}). Also note
-that manual \c{-dbg} packages are obsolete in favor of automatic \c{-dbgsym}
-packages from Debian 9.
+the policy), there are exceptions (e.g., \c{zlib1g} \c{zlib1g-dev}). The
+header-only library package names may or may not start with \c{lib} and end
+with \c{-dev} (e.g., \c{libeigen3-dev}, \c{rapidjson-dev}, \c{catch2}). Also
+note that manual \c{-dbg} packages are obsolete in favor of automatic
+\c{-dbgsym} packages from Debian 9.
For executable packages there is normally no \c{-dev} packages but \c{-dbg},
\c{-doc}, and \c{-common} are plausible.
@@ -3072,7 +3081,9 @@ are also used to derive the package names for production except here we have
the option to specify alternative non-native package names using the special
\c{debian_0-name} (or alike) value. If only the \c{-dev} package is specified,
then the main package name is derived from that by removing the \c{-dev}
-suffix.
+suffix. Note that regardless of whether the main package name is specified or
+not, the \l{bpkg-pkg-bindist(1)} command may omit generating the main package
+for a binless library.
The generated binary package version can be specified with the
\c{debian-version} (or alike) manifest value. If it's not specified, then the
@@ -3240,6 +3251,10 @@ libpq libpq-devel
zlib zlib-devel zlib-static
+catch-devel
+
+eigen3-devel eigen3-doc
+
xerces-c xerces-c-devel xerces-c-doc
libsigc++20 libsigc++20-devel libsigc++20-doc
@@ -3324,7 +3339,9 @@ are also used to derive the package names for production except here we have
the option to specify alternative non-native package names using the special
\c{fedora_0-name} (or alike) value. If only the \c{-devel} package is
specified, then the main package name is derived from that by removing the
-\c{-devel} suffix.
+\c{-devel} suffix. Note that regardless of whether the main package name is
+specified or not, the \l{bpkg-pkg-bindist(1)} command may omit generating the
+main package for a binless library.
The generated binary package version can be specified with the
\c{fedora-version} (or alike) manifest value. If it's not specified, then the