aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-11-20 12:06:08 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-11-20 12:06:08 +0200
commitd282f0b8d41b7b8f584dff215fc892367895af3b (patch)
treebd7c4d68940c148077c06f02c352469959bc112d
parent096362fa82e7ab390078089e4dbbf04da66c79e0 (diff)
Handle config.import.*.<name>.lib in cc::search_library()
In particular, this provides support for using prefixed/suffixed installed libraries (GH issue #449).
-rw-r--r--NEWS29
-rw-r--r--libbuild2/cc/common.cxx56
-rw-r--r--libbuild2/file.cxx86
3 files changed, 114 insertions, 57 deletions
diff --git a/NEWS b/NEWS
index 1c285c7..bc00177 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,32 @@
+Version 0.18.0
+
+ * Ability to override imported installed library path/name.
+
+ Specifically, the config.import.<proj>.<name>.{lib,liba,libs} variables
+ can be used to specify alternative location and/or name for an installed
+ library. This, in particular, can be useful when trying to use a library
+ installed with a prefix or suffix. For example, if the library was
+ installed with the `D` suffix:
+
+ config.import.libfoo.foo.lib=fooD
+
+ For the config.*.lib variable, the valid values are a simple path (which
+ is used as a library name base), an absolute directory (which is used as a
+ library location), or both. For example:
+
+ config.import.libfoo.foo.lib=/tmp/lib/
+ config.import.libfoo.foo.lib=/tmp/lib/fooD
+
+ For the config.*.{liba,libs} variables, the valid values are a simple name
+ (which is used as a library name), or an absolute path (which is used as a
+ library location and name). For example:
+
+ config.import.libfoo.foo.libs=libfooD.so
+ config.import.libfoo.foo.liba=/tmp/lib/libfooD.a
+
+ Note that on Windows, the shared library name/path should refer to the
+ import library, not the DLL.
+
Version 0.17.0
* C++20 modules support improvements:
diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx
index 0589c30..ae89a85 100644
--- a/libbuild2/cc/common.cxx
+++ b/libbuild2/cc/common.cxx
@@ -950,22 +950,35 @@ namespace build2
const prerequisite_key& p,
bool exist) const
{
+ assert (p.scope != nullptr && (!exist || act));
+
tracer trace (x, "search_library");
- assert (p.scope != nullptr && (!exist || act));
+ context& ctx (p.scope->ctx);
+ const scope& rs (*p.scope->root_scope ());
- // Import phase 1 may pass us a user-specified path with a relative
- // directory (same semantics as in lookup_import() below).
+ // Note: since we are searching for a (presumably) installed library,
+ // utility libraries do not apply.
//
- {
- const dir_path& d (*p.tk.dir);
+ bool l (p.is_a<lib> ());
+ const string& name (*p.tk.name);
+ const optional<string>& ext (l ? nullopt : p.tk.ext); // Only liba/libs.
- if (!d.empty ())
- fail << "relative path in imported " << p;
- }
+ // Import phase 1 may pass us a path specified by the user with
+ // config.import.<proj>.<name>.<type>. The possible cases are:
+ //
+ // 1. Empty or relative directory for liba{} and libs{} (absolute would
+ // be taken care of by phase 1 since these tragets are path-based).
+ //
+ // 2. Empty, relative, or absolute directory for lib{} (since it's not a
+ // path-based target).
+ //
+ const dir_path& dir (*p.tk.dir);
- context& ctx (p.scope->ctx);
- const scope& rs (*p.scope->root_scope ());
+ // Same semantics as in lookup_import() below.
+ //
+ if (!dir.empty () && dir.relative ())
+ fail << "relative path in imported " << p;
// Here is the problem: we may be building for two different toolchains
// simultaneously that use the same installed library. But our search is
@@ -977,17 +990,6 @@ namespace build2
? cpath
: cast<process_path> (rs["bin.ld.path"]));
- // @@ This is hairy enough to warrant a separate implementation for
- // Windows.
-
- // Note: since we are searching for a (presumably) installed library,
- // utility libraries do not apply.
- //
- bool l (p.is_a<lib> ());
- const optional<string>& ext (l ? nullopt : p.tk.ext); // Only liba/libs.
-
- const string& name (*p.tk.name);
-
// If this prerequisite is project-qualified do an ad hoc check for
// config.import.<proj>.<name>.{liba,libs} which can be used to specify
// different path (see import_search() for background). Note that for
@@ -1106,14 +1108,16 @@ namespace build2
//
const char* e ("");
+ an = dir; // Empty or absolute.
+
if (tsys == "win32-msvc")
{
- an = path (name);
+ an /= path (name);
e = "lib";
}
else
{
- an = path ("lib" + name);
+ an /= path ("lib" + name);
e = "a";
}
@@ -1142,14 +1146,16 @@ namespace build2
{
const char* e ("");
+ sn = dir;
+
if (tsys == "win32-msvc")
{
- sn = path (name);
+ sn /= path (name);
e = "dll.lib";
}
else
{
- sn = path ("lib" + name);
+ sn /= path ("lib" + name);
if (tsys == "darwin") e = "dylib";
else if (tsys == "mingw32") e = "dll.a"; // See search code below.
diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx
index 8e3b2a4..a6b62c0 100644
--- a/libbuild2/file.cxx
+++ b/libbuild2/file.cxx
@@ -2359,8 +2359,19 @@ namespace build2
{
string on (move (tgt.value)); // Original name as imported.
- tgt.dir = p->directory ();
- tgt.value = p->leaf ().string ();
+ // Keep the original name if the path is (syntactically) to
+ // directory.
+ //
+ if (p->to_directory ())
+ {
+ tgt.dir = path_cast<dir_path> (*p);
+ tgt.value = on;
+ }
+ else
+ {
+ tgt.dir = p->directory ();
+ tgt.value = p->leaf ().string ();
+ }
// If the path is relative, then keep it project-qualified
// assuming import phase 2 knows what to do with it. Think:
@@ -2379,48 +2390,59 @@ namespace build2
tgt.proj = move (proj);
else
{
- // Enter the target and assign its path (this will most commonly
- // be some out of project file).
- //
- // @@ Should we check that the file actually exists (and cache
- // the extracted timestamp)? Or just let things take their
- // natural course?
- //
name n (tgt);
const target_type* tt (ibase.find_target_type (n, loc).first);
if (tt == nullptr)
fail (loc) << "unknown target type " << n.type << " in " << n;
- // Note: not using the extension extracted by find_target_type()
- // to be consistent with import phase 2.
- //
- target& t (insert_target (trace, ctx, *tt, *p).first);
-
- // Load the metadata, similar to import phase 2.
+ // If this is not a path-based target, then delegate to import
+ // phase 2 as above (see cc::search_library() for an example).
//
- if (meta)
+ if (!tt->is_a<path_target> ())
{
- if (exe* e = t.is_a<exe> ())
+ tgt.proj = move (proj);
+ }
+ else
+ {
+ // Enter the target and assign its path (this will most
+ // commonly be some out of project file).
+ //
+ // @@ Should we check that the file actually exists (and cache
+ // the extracted timestamp)? Or just let things take their
+ // natural course?
+ //
+
+ // Note: not using the extension extracted by
+ // find_target_type() to be consistent with import phase 2.
+ //
+ target& t (insert_target (trace, ctx, *tt, *p).first);
+
+ // Load the metadata, similar to import phase 2.
+ //
+ if (meta)
{
- if (!e->vars[ctx.var_export_metadata].defined ())
+ if (exe* e = t.is_a<exe> ())
{
- optional<string> md;
+ if (!e->vars[ctx.var_export_metadata].defined ())
{
- auto df = make_diag_frame (
- [&proj, tt, &on] (const diag_record& dr)
- {
- import_suggest (
- dr, proj, tt, on, false, "alternative ");
- });
-
- md = extract_metadata (e->process_path (),
- *meta,
- false /* optional */,
- loc);
+ optional<string> md;
+ {
+ auto df = make_diag_frame (
+ [&proj, tt, &on] (const diag_record& dr)
+ {
+ import_suggest (
+ dr, proj, tt, on, false, "alternative ");
+ });
+
+ md = extract_metadata (e->process_path (),
+ *meta,
+ false /* optional */,
+ loc);
+ }
+
+ parse_metadata (*e, move (*md), loc);
}
-
- parse_metadata (*e, move (*md), loc);
}
}
}