aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-07-19 17:10:51 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-07-19 17:10:51 +0200
commit89a9f8174ec858bf6df8515a84f061f211dec551 (patch)
tree030daad1364ea38a4f2ec22c57b41aeeb8b1b6e8
parent3ec07c196c9ab86db09c77bff7eb11cd5a5a9b1e (diff)
Add import library target libi{}, make libs{} the DLL
In the end, having libs{} be the DLL with import library being its member is more natural than making libs{} the import library and having dll{} as its member.
-rw-r--r--build2/bin/module.cxx92
-rw-r--r--build2/bin/target12
-rw-r--r--build2/bin/target.cxx13
-rw-r--r--build2/cxx/compile.cxx4
-rw-r--r--build2/cxx/link.cxx201
-rw-r--r--build2/cxx/module.cxx16
-rw-r--r--build2/cxx/msvc.cxx17
-rw-r--r--build2/cxx/windows-rpath.cxx65
-rw-r--r--build2/rule.cxx29
-rw-r--r--build2/target12
-rw-r--r--build2/target.cxx3
11 files changed, 291 insertions, 173 deletions
diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx
index 334384a..194b409 100644
--- a/build2/bin/module.cxx
+++ b/build2/bin/module.cxx
@@ -348,19 +348,59 @@ namespace build2
//
const string& tclass (cast<string> (r["bin.target.class"]));
- // Register target types.
+ // Register target types and configure their default "installability".
//
+ using namespace install;
+
{
auto& t (b.target_types);
+ t.insert<obj> ();
t.insert<obje> ();
t.insert<obja> ();
t.insert<objs> ();
- t.insert<obj> ();
+
t.insert<exe> ();
+ install_path<exe> (b, dir_path ("bin")); // Install into install.bin.
+
+ t.insert<lib> ();
t.insert<liba> ();
t.insert<libs> ();
- t.insert<lib> ();
+
+ install_path<liba> (b, dir_path ("lib")); // Install into install.lib.
+ install_mode<liba> (b, "644");
+
+ // Should shared libraries have the executable bit? That depends on
+ // who you ask. In Debian, for example, it should not unless, it
+ // really is executable (i.e., has main()). On the other hand, on
+ // some systems, this may be required in order for the dynamic
+ // linker to be able to load the library. So, by default, we will
+ // keep it executable, especially seeing that this is also the
+ // behavior of autotools. At the same time, it is easy to override
+ // this, for example:
+ //
+ // config.install.lib.mode=644
+ //
+ // And a library that wants to override any such overrides (e.g.,
+ // because it does have main()) can do:
+ //
+ // libs{foo}: install.mode=755
+ //
+ // Everyone is happy then? On Windows libs{} is the DLL and goes to
+ // bin/, not lib/.
+ //
+ install_path<libs> (b, dir_path (tclass == "windows" ? "bin" : "lib"));
+
+ // Create additional target types for certain targets.
+ //
+ if (tclass == "windows")
+ {
+ // Import library.
+ //
+ t.insert<libi> ();
+ install_path<libi> (b, dir_path ("lib"));
+ install_mode<libi> (b, "644");
+ }
}
// Register rules.
@@ -388,39 +428,6 @@ namespace build2
r.insert<lib> (perform_install_id, "bin.lib", lib_);
}
- // Configure "installability" of our target types.
- //
- using namespace install;
-
- install_path<exe> (b, dir_path ("bin")); // Install into install.bin.
-
- // Should shared libraries have executable bit? That depends on
- // who you ask. In Debian, for example, it should not unless, it
- // really is executable (i.e., has main()). On the other hand, on
- // some systems, this may be required in order for the dynamic
- // linker to be able to load the library. So, by default, we will
- // keep it executable, especially seeing that this is also the
- // behavior of autotools. At the same time, it is easy to override
- // this, for example:
- //
- // config.install.lib.mode=644
- //
- // And a library that wants to override any such overrides (e.g.,
- // because it does have main()) can do:
- //
- // libs{foo}: install.mode=755
- //
- // Everyone is happy then? Not Windows users. When targeting Windows
- // libs{} is an import library and shouldn't be exec.
- //
- install_path<libs> (b, dir_path ("lib")); // Install into install.lib.
-
- if (tclass == "windows")
- install_mode<libs> (b, "644");
-
- install_path<liba> (b, dir_path ("lib")); // Install into install.lib.
- install_mode<liba> (b, "644");
-
return true;
}
@@ -485,6 +492,19 @@ namespace build2
r.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
}
+ const string& lid (cast<string> (r["bin.ld.id"]));
+
+ // Register the pdb{} target if using the VC toolchain.
+ //
+ using namespace install;
+
+ if (lid == "msvc")
+ {
+ const target_type& pdb (b.derive_target_type<file> ("pdb").first);
+ install_path (pdb, b, dir_path ("bin")); // Goes to install.bin
+ install_mode (pdb, b, "644"); // But not executable.
+ }
+
return true;
}
diff --git a/build2/bin/target b/build2/bin/target
index 8c32e84..849316e 100644
--- a/build2/bin/target
+++ b/build2/bin/target
@@ -107,6 +107,18 @@ namespace build2
static const target_type static_type;
virtual const target_type& dynamic_type () const {return static_type;}
};
+
+ // Windows import library.
+ //
+ class libi: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
}
}
diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx
index 3f16467..d78d34c 100644
--- a/build2/bin/target.cxx
+++ b/build2/bin/target.cxx
@@ -261,5 +261,18 @@ namespace build2
&search_target,
false
};
+
+ // libi
+ //
+ const target_type libi::static_type
+ {
+ "libi",
+ &file::static_type,
+ &target_factory<libi>,
+ &target_extension_var<ext_var, nullptr>,
+ nullptr,
+ &search_file,
+ false
+ };
}
}
diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx
index 9efa0b6..432b485 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cxx/compile.cxx
@@ -67,7 +67,7 @@ namespace build2
{
tracer trace ("cxx::compile");
- path_target& t (static_cast<path_target&> (xt));
+ file& t (static_cast<file&> (xt));
scope& bs (t.base_scope ());
scope& rs (*bs.root_scope ());
@@ -1235,7 +1235,7 @@ namespace build2
target_state compile::
perform_update (action a, target& xt)
{
- path_target& t (static_cast<path_target&> (xt));
+ file& t (static_cast<file&> (xt));
cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
if (s == nullptr)
diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx
index e13cfe3..b80fc8b 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cxx/link.cxx
@@ -202,6 +202,10 @@ namespace build2
{
tracer trace ("cxx::link::search_library");
+ // @@ This is hairy enough to warrant a separate implementation for
+ // Windows.
+ //
+
// First check the cache.
//
if (p.target != nullptr)
@@ -210,6 +214,7 @@ namespace build2
scope& rs (*p.scope.root_scope ());
const string& cid (cast<string> (rs["cxx.id"]));
const string& tsys (cast<string> (rs["cxx.target.system"]));
+ const string& tclass (cast<string> (rs["cxx.target.class"]));
bool l (p.is_a<lib> ());
const string* ext (l ? nullptr : p.ext); // Only for liba/libs.
@@ -321,7 +326,48 @@ namespace build2
f /= sn;
mt = file_mtime (f);
- if (tsys == "mingw32")
+ if (mt != timestamp_nonexistent)
+ {
+ // On Windows what we found is the import library which we need
+ // to make the first ad hoc member of libs{}.
+ //
+ if (tclass == "windows")
+ {
+ s = &targets.insert<libs> (
+ d, dir_path (), p.name, nullptr, trace);
+
+ if (s->member == nullptr)
+ {
+ libi& i (
+ targets.insert<libi> (
+ d, dir_path (), p.name, se, trace));
+
+ if (i.path ().empty ())
+ i.path (move (f));
+
+ i.mtime (mt);
+
+ // Presumably there is a DLL somewhere, we just don't know
+ // where (and its possible we might have to look for one if we
+ // decide we need to do rpath emulation for installed
+ // libraries as well). We will represent this as empty path
+ // but valid timestamp (aka "trust me, it's there").
+ //
+ s->mtime (mt);
+ s->member = &i;
+ }
+ }
+ else
+ {
+ s = &targets.insert<libs> (d, dir_path (), p.name, se, trace);
+
+ if (s->path ().empty ())
+ s->path (move (f));
+
+ s->mtime (mt);
+ }
+ }
+ else if (ext == nullptr && tsys == "mingw32")
{
// Above we searched for the import library (.dll.a) but if it's
// not found, then we also search for the .dll (unless the
@@ -329,22 +375,19 @@ namespace build2
// directly. Note also that the resulting libs{} would end up
// being the .dll.
//
- if (mt == timestamp_nonexistent && ext == nullptr)
- {
- se = &extension_pool.find ("dll");
- f = f.base (); // Remove .a from .dll.a.
- mt = file_mtime (f);
- }
- }
+ se = &extension_pool.find ("dll");
+ f = f.base (); // Remove .a from .dll.a.
+ mt = file_mtime (f);
- if (mt != timestamp_nonexistent)
- {
- s = &targets.insert<libs> (d, dir_path (), p.name, se, trace);
+ if (mt != timestamp_nonexistent)
+ {
+ s = &targets.insert<libs> (d, dir_path (), p.name, se, trace);
- if (s->path ().empty ())
- s->path (move (f));
+ if (s->path ().empty ())
+ s->path (move (f));
- s->mtime (mt);
+ s->mtime (mt);
+ }
}
}
@@ -595,7 +638,7 @@ namespace build2
{
tracer trace ("cxx::link::apply");
- path_target& t (static_cast<path_target&> (xt));
+ file& t (static_cast<file&> (xt));
scope& bs (t.base_scope ());
scope& rs (*bs.root_scope ());
@@ -656,28 +699,13 @@ namespace build2
else if (tclass == "windows")
{
// On Windows libs{} is an ad hoc group. The libs{} itself is
- // the import library and we add dll{} as a member (see below).
- // While at first it may seem strange that libs{} is the import
- // library and not the DLL, if you meditate on it, you will see
- // it makes a lot of sense: our main task here is building and
- // for that we need the import library, not the DLL.
+ // the DLL and we add libi{} import library as its member (see
+ // below).
//
if (tsys == "mingw32")
- {
p = "lib";
- e = "dll.a";
- }
- else
- {
- // Usually on Windows the import library is called the same as
- // the DLL but with the .lib extension. Which means it clashes
- // with the static library. Instead of decorating the static
- // library name with ugly suffixes (as is customary), let's
- // use the MinGW approach (one must admit it's quite elegant)
- // and call it .dll.lib.
- //
- e = "dll.lib";
- }
+
+ e = "dll";
}
else
{
@@ -713,14 +741,21 @@ namespace build2
if (tclass == "windows")
{
- // DLL
+ // Import library.
//
if (lt == otype::s)
{
- file& dll (add_adhoc (t, "dll"));
-
- if (dll.path ().empty ())
- dll.derive_path ("dll", tsys == "mingw32" ? "lib" : nullptr);
+ file& imp (add_adhoc (t, "libi"));
+
+ // Usually on Windows the import library is called the same as the
+ // DLL but with the .lib extension. Which means it clashes with the
+ // static library. Instead of decorating the static library name
+ // with ugly suffixes (as is customary), let's use the MinGW
+ // approach (one must admit it's quite elegant) and call it
+ // .dll.lib.
+ //
+ if (imp.path ().empty ())
+ imp.derive_path (t.path (), tsys == "mingw32" ? "a" : "lib");
}
// PDB
@@ -729,10 +764,13 @@ namespace build2
cid == "msvc" &&
find_option ("/DEBUG", t, "cxx.loptions", true))
{
- // Add after the DLL if any.
+ // Add after the import library if any.
//
file& pdb (add_adhoc (t.member == nullptr ? t : *t.member, "pdb"));
+ // We call it foo.{exe,dll}.pdb rather than just foo.pdb because we
+ // can have both foo.exe and foo.dll in the same directory.
+ //
if (pdb.path ().empty ())
pdb.derive_path (t.path (), "pdb");
}
@@ -1360,17 +1398,28 @@ namespace build2
for (target* pt: t.prerequisite_targets)
{
- path_target* ppt;
+ file* f;
liba* a (nullptr);
+ libs* s (nullptr);
- if ((ppt = pt->is_a<obje> ()) ||
- (ppt = pt->is_a<obja> ()) ||
- (ppt = pt->is_a<objs> ()) ||
+ if ((f = pt->is_a<obje> ()) ||
+ (f = pt->is_a<obja> ()) ||
+ (f = pt->is_a<objs> ()) ||
(lt != otype::a &&
- ((ppt = a = pt->is_a<liba> ()) ||
- (ppt = pt->is_a<libs> ()))))
+ ((f = a = pt->is_a<liba> ()) ||
+ (f = s = pt->is_a<libs> ()))))
{
- cs.append (ppt->path ().string ());
+ // On Windows a shared library is a DLL with the import library as
+ // a first ad hoc group member. MinGW though can link directly to
+ // DLLs (see search_library() for details).
+ //
+ if (s != nullptr && tclass == "windows")
+ {
+ if (s->member != nullptr)
+ f = static_cast<file*> (s->member);
+ }
+
+ cs.append (f->path ().string ());
// If this is a static library, link all the libraries it depends
// on, recursively.
@@ -1487,26 +1536,26 @@ namespace build2
if (lt == otype::s)
{
- // On Windows libs{} is the import stub and its first ad hoc
- // group member is dll{}.
+ // On Windows libs{} is the DLL and its first ad hoc group
+ // member is the import library.
//
// This will also create the .exp export file. Its name will be
// derived from the import library by changing the extension.
- // Lucky us -- there is no option to name it.
+ // Lucky for us -- there is no option to name it.
//
- out2 = "/IMPLIB:" + relt.string ();
+ auto imp (static_cast<file*> (t.member));
+ out2 = "/IMPLIB:" + relative (imp->path ()).string ();
args.push_back (out2.c_str ());
-
- relt = relative (static_cast<file*> (t.member)->path ());
}
- // If we have /DEBUG then name the .pdb file. We call it
- // foo.{exe,dll}.pdb rather than just foo.pdb because we can have,
- // both foo.exe and foo.dll in the same directory.
+ // If we have /DEBUG then name the .pdb file. It is either the
+ // first (exe) or the second (dll) ad hoc group member.
//
if (find_option ("/DEBUG", args, true))
{
- out1 = "/PDB:" + relt.string () + ".pdb";
+ auto pdb (static_cast<file*> (
+ lt == otype::e ? t.member : t.member->member));
+ out1 = "/PDB:" + relative (pdb->path ()).string ();
args.push_back (out1.c_str ());
}
@@ -1533,13 +1582,12 @@ namespace build2
if (tsys == "mingw32")
{
- // On Windows libs{} is the import stub and its first ad hoc
- // group member is dll{}.
+ // On Windows libs{} is the DLL and its first ad hoc group
+ // member is the import library.
//
- out = "-Wl,--out-implib=" + relt.string ();
+ auto imp (static_cast<file*> (t.member));
+ out = "-Wl,--out-implib=" + relative (imp->path ()).string ();
args.push_back (out.c_str ());
-
- relt = relative (static_cast<file*> (t.member)->path ());
}
}
@@ -1553,17 +1601,28 @@ namespace build2
for (target* pt: t.prerequisite_targets)
{
- path_target* ppt;
+ file* f;
liba* a (nullptr);
+ libs* s (nullptr);
- if ((ppt = pt->is_a<obje> ()) ||
- (ppt = pt->is_a<obja> ()) ||
- (ppt = pt->is_a<objs> ()) ||
+ if ((f = pt->is_a<obje> ()) ||
+ (f = pt->is_a<obja> ()) ||
+ (f = pt->is_a<objs> ()) ||
(lt != otype::a &&
- ((ppt = a = pt->is_a<liba> ()) ||
- (ppt = pt->is_a<libs> ()))))
+ ((f = a = pt->is_a<liba> ()) ||
+ (f = s = pt->is_a<libs> ()))))
{
- sargs.push_back (relative (ppt->path ()).string ()); // string()&&
+ // On Windows a shared library is a DLL with the import library as a
+ // first ad hoc group member. MinGW though can link directly to DLLs
+ // (see search_library() for details).
+ //
+ if (s != nullptr && tclass == "windows")
+ {
+ if (s->member != nullptr)
+ f = static_cast<file*> (s->member);
+ }
+
+ sargs.push_back (relative (f->path ()).string ()); // string()&&
// If this is a static library, link all the libraries it depends
// on, recursively.
@@ -1687,7 +1746,7 @@ namespace build2
{
case otype::a:
{
- e = {"+.d"};
+ e = {".d"};
break;
}
case otype::e:
@@ -1717,7 +1776,7 @@ namespace build2
{
// Assuming it's VC or alike. Clean up .exp and .ilk.
//
- e = {".d", "-.exp", "--.ilk"};
+ e = {".d", ".exp", "-.ilk"};
}
else
e = {".d"};
diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx
index 69e76a3..8972bf9 100644
--- a/build2/cxx/module.cxx
+++ b/build2/cxx/module.cxx
@@ -258,7 +258,6 @@ namespace build2
const string& cid (cast<string> (r["cxx.id"]));
const string& tsys (cast<string> (r["cxx.target.system"]));
- const string& tclass (cast<string> (r["cxx.target.class"]));
// Initialize the bin module. Only do this if it hasn't already been
// loaded so that we don't overwrite user's bin.* settings.
@@ -361,21 +360,6 @@ namespace build2
install_path<txx> (b, dir_path ("include"));
install_path<h> (b, dir_path ("include"));
- // Create additional target types for certain target platforms.
- //
- if (tclass == "windows")
- {
- const target_type& dll (b.derive_target_type<file> ("dll").first);
- install_path (dll, b, dir_path ("bin"));
-
- if (cid == "msvc")
- {
- const target_type& pdb (b.derive_target_type<file> ("pdb").first);
- install_path (pdb, b, dir_path ("bin"));
- install_mode (pdb, b, "644");
- }
- }
-
return true;
}
}
diff --git a/build2/cxx/msvc.cxx b/build2/cxx/msvc.cxx
index 1c5a3fa..ff91018 100644
--- a/build2/cxx/msvc.cxx
+++ b/build2/cxx/msvc.cxx
@@ -227,11 +227,24 @@ namespace build2
libs*
msvc_search_shared (const path& ld, const dir_path& d, prerequisite& p)
{
+ tracer trace ("cxx::msvc_search_shared");
+
libs* r (nullptr);
- auto search = [&r, &ld, &d, &p] (const char* pf, const char* sf) -> bool
+ auto search = [&r, &ld, &d, &p, &trace] (
+ const char* pf, const char* sf) -> bool
{
- r = search_library<libs> (ld, d, p, otype::s, pf, sf);
+ if (libi* i = search_library<libi> (ld, d, p, otype::s, pf, sf))
+ {
+ r = &targets.insert<libs> (d, dir_path (), p.name, nullptr, trace);
+
+ if (r->member == nullptr)
+ {
+ r->mtime (i->mtime ());
+ r->member = i;
+ }
+ }
+
return r != nullptr;
};
diff --git a/build2/cxx/windows-rpath.cxx b/build2/cxx/windows-rpath.cxx
index 0bd4bc5..b5bad5e 100644
--- a/build2/cxx/windows-rpath.cxx
+++ b/build2/cxx/windows-rpath.cxx
@@ -51,22 +51,18 @@ namespace build2
{
if (libs* ls = pt->is_a<libs> ())
{
- // This can be an installed library in which case we will have just
- // the import stub but may also have just the DLL. For now we don't
- // bother with installed libraries.
+ // Skip installed DLLs.
//
- if (ls->member == nullptr)
+ if (ls->path ().empty ())
continue;
- file& dll (static_cast<file&> (*ls->member));
-
// What if the DLL is in the same directory as the executable, will
// it still be found even if there is an assembly? On the other
// hand, handling it as any other won't hurt us much.
//
timestamp t;
- if ((t = dll.mtime ()) > r)
+ if ((t = ls->mtime ()) > r)
r = t;
if ((t = windows_rpath_timestamp (*ls)) > r)
@@ -80,18 +76,18 @@ namespace build2
// Like *_timestamp() but actually collect the DLLs.
//
static void
- rpath_dlls (set<file*>& s, file& t)
+ rpath_dlls (set<libs*>& s, file& t)
{
for (target* pt: t.prerequisite_targets)
{
if (libs* ls = pt->is_a<libs> ())
{
- if (ls->member == nullptr)
+ // Skip installed DLLs.
+ //
+ if (ls->path ().empty ())
continue;
- file& dll (static_cast<file&> (*ls->member));
-
- s.insert (&dll);
+ s.insert (ls);
rpath_dlls (s, *ls);
}
}
@@ -143,7 +139,7 @@ namespace build2
//
bool empty (ts == timestamp_nonexistent);
- set<file*> dlls;
+ set<libs*> dlls;
if (!empty)
rpath_dlls (dlls, t);
@@ -185,16 +181,12 @@ namespace build2
scope& as (*rs.weak_scope ()); // Amalgamation scope.
- for (file* dt: dlls)
+ auto link = [&as, &ad] (const path& f, const path& l)
{
- const path& dp (dt->path ()); // DLL path.
- const path dn (dp.leaf ()); // DLL name.
- const path lp (ad / dn); // Link path.
-
- auto print = [&dp, &lp] (const char* cmd)
+ auto print = [&f, &l] (const char* cmd)
{
if (verb >= 3)
- text << cmd << ' ' << dp << ' ' << lp;
+ text << cmd << ' ' << f << ' ' << l;
};
// First we try to create a symlink. If that fails (e.g., "Windows
@@ -208,10 +200,10 @@ namespace build2
// part of the same amalgamation. This way if the amalgamation is
// moved as a whole, the links will remain valid.
//
- if (dp.sub (as.out_path ()))
- mksymlink (dp.relative (ad), lp);
+ if (f.sub (as.out_path ()))
+ mksymlink (f.relative (ad), l);
else
- mksymlink (dp, lp);
+ mksymlink (f, l);
print ("ln -s");
}
@@ -222,12 +214,12 @@ namespace build2
if (c != EPERM && c != ENOSYS)
{
print ("ln -s");
- fail << "unable to create symlink " << lp << ": " << e.what ();
+ fail << "unable to create symlink " << l << ": " << e.what ();
}
try
{
- mkhardlink (dp, lp);
+ mkhardlink (f, l);
print ("ln");
}
catch (const system_error& e)
@@ -237,23 +229,38 @@ namespace build2
if (c != EPERM && c != ENOSYS)
{
print ("ln");
- fail << "unable to create hard link " << lp << ": "
- << e.what ();
+ fail << "unable to create hardlink " << l << ": " << e.what ();
}
try
{
- cpfile (dp, lp);
+ cpfile (f, l);
print ("cp");
}
catch (const system_error& e)
{
print ("cp");
- fail << "unable to create copy " << lp << ": " << e.what ();
+ fail << "unable to create copy " << l << ": " << e.what ();
}
}
}
+ };
+
+ for (libs* dll: dlls)
+ {
+ const path& dp (dll->path ()); // DLL path.
+ const path dn (dp.leaf ()); // DLL name.
+ link (dp, ad / dn);
+
+ // Link .pdb if there is one (second member of the ad hoc group).
+ //
+ if (dll->member != nullptr && dll->member->member != nullptr)
+ {
+ file& pdb (static_cast<file&> (*dll->member->member));
+ link (pdb.path (), ad / pdb.path ().leaf ());
+ }
+
ofs << " <file name='" << dn.string () << "'/>\n";
}
diff --git a/build2/rule.cxx b/build2/rule.cxx
index cbb7d08..0fcfde2 100644
--- a/build2/rule.cxx
+++ b/build2/rule.cxx
@@ -44,18 +44,25 @@ namespace build2
{
path_target& pt (dynamic_cast<path_target&> (t));
- // Assign the path. While normally we shouldn't do this in match(),
- // no other rule should ever be ambiguous with the fallback one.
+ // First check the timestamp. This allows for the special "trust me,
+ // this file exists" situations (used, for example, for installed
+ // stuff where we know it's there, just not exactly where).
//
- if (pt.path ().empty ())
- pt.derive_path ();
-
- // We cannot just call pt.mtime() since we haven't matched yet.
- //
- timestamp ts (file_mtime (pt.path ()));
- pt.mtime (ts);
-
- if (ts != timestamp_nonexistent)
+ timestamp ts (pt.mtime ());
+
+ if (ts == timestamp_unknown)
+ {
+ // Assign the path. While normally we shouldn't do this in match(),
+ // no other rule should ever be ambiguous with the fallback one.
+ //
+ if (pt.path ().empty ())
+ {
+ pt.derive_path ();
+ ts = pt.mtime ();
+ }
+ }
+
+ if (ts != timestamp_unknown && ts != timestamp_nonexistent)
return t;
l4 ([&]{trace << "no existing file for target " << t;});
diff --git a/build2/target b/build2/target
index 28753df..dfc6abc 100644
--- a/build2/target
+++ b/build2/target
@@ -954,10 +954,12 @@ namespace build2
public:
using target::target;
- // Generally, modification time for a target can only be queried
- // after a rule has been matched since that's where the path is
- // normally gets assigned. Normally, however, it would make sense
- // to first execute the rule to get the "updated" timestamp.
+ // Generally, modification time for a target can only be queried after a
+ // rule has been matched since that's where the path is normally gets
+ // assigned (if the path was not assigned and no timestamp was set
+ // manually, this function will return timestamp_unknown). Normally,
+ // however, it would make sense to first execute the rule to get the
+ // "updated" timestamp.
//
// The rule for groups that utilize the group state is as follows:
// if it has any members that are mtime_targets, then the group
@@ -986,6 +988,8 @@ namespace build2
}
protected:
+ // Return timestamp_unknown if the mtime cannot be loaded.
+ //
virtual timestamp
load_mtime () const = 0;
diff --git a/build2/target.cxx b/build2/target.cxx
index a8fae39..b0fc92d 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -383,8 +383,7 @@ namespace build2
load_mtime () const
{
const path_type& f (path ());
- assert (!f.empty ());
- return file_mtime (f);
+ return f.empty () ? timestamp_unknown : file_mtime (f);
}
// Search functions.