aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-06-17 16:42:38 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-06-17 16:42:38 +0200
commit774bfb559ecaef2aac1dcb7a0414bc6895a9b9d5 (patch)
treeef218a78b2122c6bdc1c13f376d1bcf83fe23e84
parent9ae6fd626ed2ca0b7eff64bda68ca84463e66b84 (diff)
Initial take on DLL support for MinGW toolchain
-rw-r--r--build2/algorithm12
-rw-r--r--build2/algorithm.cxx89
-rw-r--r--build2/cxx/compile.cxx6
-rw-r--r--build2/cxx/link3
-rw-r--r--build2/cxx/link.cxx98
-rw-r--r--build2/types14
6 files changed, 157 insertions, 65 deletions
diff --git a/build2/algorithm b/build2/algorithm
index 19b3971..d7dddba 100644
--- a/build2/algorithm
+++ b/build2/algorithm
@@ -220,6 +220,18 @@ namespace build2
//
target_state
perform_clean_depdb (action, target&);
+
+ // Helper for custom perform(clean) implementations that cleans extra files
+ // specified as a list of extensions. The extension string can be NULL, in
+ // which case it is ignored. Otherwise, the first character can be '+', in
+ // which case the extension is added (without the plus) to the existing
+ // extension (if any). In all other cases, the old extension is replaced
+ // with the new one. For example:
+ //
+ // clean_extra (a, t, {"+.d", ".lib"});
+ //
+ target_state
+ clean_extra (action, file&, initializer_list<const char*> extra_ext);
}
#include <build2/algorithm.ixx>
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index a22837f..17768e3 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -478,60 +478,81 @@ namespace build2
}
target_state
- perform_clean (action a, target& t)
+ clean_extra (action a, file& ft, initializer_list<const char*> es)
{
- // The reverse order of update: first remove the file, then clean
- // prerequisites.
+ // Clean the extras first and don't print the commands at verbosity level
+ // below 3.
//
- file& ft (dynamic_cast<file&> (t));
+ target_state er (target_state::unchanged);
+ path ef; // First extra file that actually got removed (see below).
- target_state r (rmfile (ft.path (), ft)
- ? target_state::changed
- : target_state::unchanged);
+ for (const char* e: es)
+ {
+ if (e == nullptr)
+ continue;
- // Update timestamp in case there are operations after us that
- // could use the information.
- //
- ft.mtime (timestamp_nonexistent);
+ path f;
+ if (*e == '+')
+ f = ft.path () + ++e;
+ else
+ f = ft.path ().base () + e;
- // Clean prerequisites.
- //
- r |= reverse_execute_prerequisites (a, t);
+ target_state r (rmfile (f, false)
+ ? target_state::changed
+ : target_state::unchanged);
- return r;
- }
+ if (r == target_state::changed && ef.empty ())
+ {
+ ef = move (f);
+ }
- target_state
- perform_clean_depdb (action a, target& t)
- {
- // Normally the .d file is created/updated before the target so remove it
- // first. Also, don't print the command at verbosity level below 3.
- //
- file& ft (dynamic_cast<file&> (t));
+ er |= r;
+ }
- path df (ft.path () + ".d");
- target_state dr (rmfile (df, false)
+ // Now clean the primary target and its prerequisited in the reverse order
+ // of update: first remove the file, then clean the prerequisites.
+ //
+ target_state tr (rmfile (ft.path (), ft)
? target_state::changed
: target_state::unchanged);
- // Factor the result of removing the .d file into the target state. While
- // strictly speaking removing it doesn't change the target state, if we
- // don't do this, then we may end up removing the file but still saying
- // that everything is clean (e.g., if someone removes the target file but
- // leaves .d laying around). That would be confusing.
+ // Update timestamp in case there are operations after us that could use
+ // the information.
+ //
+ ft.mtime (timestamp_nonexistent);
+
+ // Clean prerequisites.
//
- target_state tr (perform_clean (a, t));
+ tr |= reverse_execute_prerequisites (a, ft);
+ // Factor the result of removing the extra files into the target state.
+ // While strictly speaking removing them doesn't change the target state,
+ // if we don't do this, then we may end up removing the file but still
+ // saying that everything is clean (e.g., if someone removes the target
+ // file but leaves the extra laying around). That would be confusing.
+ //
// What would also be confusing is if we didn't print any commands in
// this case.
//
- if (tr == target_state::unchanged && dr == target_state::changed)
+ if (tr != target_state::changed && er == target_state::changed)
{
if (verb > 0 && verb < 3)
- text << "rm " << df;
+ text << "rm " << ef;
}
- tr |= dr;
+ tr |= er;
return tr;
}
+
+ target_state
+ perform_clean (action a, target& t)
+ {
+ return clean_extra (a, dynamic_cast<file&> (t), {});
+ }
+
+ target_state
+ perform_clean_depdb (action a, target& t)
+ {
+ return clean_extra (a, dynamic_cast<file&> (t), {"+.d"});
+ }
}
diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx
index bfeaeb9..b11a919 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cxx/compile.cxx
@@ -194,7 +194,7 @@ namespace build2
if (t.is_a<objso> ())
{
- // On Darwin -fPIC is the default.
+ // On Darwin, Win32 -fPIC is the default.
//
if (tclass == "linux" || tclass == "freebsd")
cs.append ("-fPIC");
@@ -610,7 +610,7 @@ namespace build2
if (t.is_a<objso> ())
{
- // On Darwin -fPIC is the default.
+ // On Darwin, Win32 -fPIC is the default.
//
if (tclass == "linux" || tclass == "freebsd")
args.push_back ("-fPIC");
@@ -1217,7 +1217,7 @@ namespace build2
{
if (t.is_a<objso> ())
{
- // On Darwin -fPIC is the default.
+ // On Darwin, Win32 -fPIC is the default.
//
if (tclass == "linux" || tclass == "freebsd")
args.push_back ("-fPIC");
diff --git a/build2/cxx/link b/build2/cxx/link
index d0584de..ca45e17 100644
--- a/build2/cxx/link
+++ b/build2/cxx/link
@@ -28,6 +28,9 @@ namespace build2
static target_state
perform_update (action, target&);
+ static target_state
+ perform_clean (action, target&);
+
static link instance;
public:
diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx
index 3d0255e..5792615 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cxx/link.cxx
@@ -255,7 +255,7 @@ namespace build2
scope& rs (*p.scope.root_scope ());
const string& cid (cast<string> (rs["cxx.id"]));
- const string& sys (cast<string> (rs["cxx.target.system"]));
+ const string& tsys (cast<string> (rs["cxx.target.system"]));
bool l (p.is_a<lib> ());
const string* ext (l ? nullptr : p.ext); // Only for liba/libso.
@@ -306,23 +306,31 @@ namespace build2
if (l || p.is_a<libso> ())
{
- // @@ VC TODO
- //
-
- sn = path ("lib" + p.name);
+ const char* e ("");
- if (ext == nullptr)
+ if (cid == "msvc")
{
- const char* e;
- if (sys == "darwin")
- e = "dylib";
- else
- e = "so";
+ // @@ VC TODO: still .lib, right?
+ //
+ }
+ else
+ {
+ sn = path ("lib" + p.name);
- ext = &extension_pool.find (e);
+ if (tsys == "darwin") e = "dylib";
+ //
+ // @@ Here we are searching for the import library but if it's not
+ // found, then we could also search for the DLL since we can link
+ // directly to it. Of course, we would also need some way to mark
+ // this libso{} as "import-less".
+ //
+ else if (tsys == "mingw32") e = "dll.a";
+ else e = "so";
}
- se = ext;
+ se = ext == nullptr
+ ? &extension_pool.find (e)
+ : ext;
if (!se->empty ())
{
@@ -594,9 +602,11 @@ namespace build2
{
//@@ VC: DLL name.
- if (tclass == "macosx") e = "dylib";
- else e = "so";
- if (p == nullptr) p = "lib";
+ if (tclass == "macosx") e = "dylib";
+ if (tclass == "windows") e = "dll";
+ else e = "so";
+
+ if (p == nullptr) p = "lib";
}
t.derive_path (e, p);
@@ -817,7 +827,7 @@ namespace build2
switch (a)
{
case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean_depdb;
+ case perform_clean_id: return &perform_clean;
default: return noop_recipe; // Configure update.
}
}
@@ -885,6 +895,7 @@ namespace build2
scope& rs (t.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"]));
const string& aid (lt == type::a
@@ -979,7 +990,7 @@ namespace build2
// Set soname.
//
- if (so && cid != "msvc")
+ if (so && tclass != "windows")
{
const string& leaf (t.path ().leaf ().string ());
@@ -1016,9 +1027,11 @@ namespace build2
// rpath of the imported libraries (i.e., we assume the are also
// installed).
//
- // @@ VC TODO: emulate own rpath somehow and complain on user's.
- //
- if (cid != "msvc")
+ if (tclass == "windows")
+ {
+ // @@ VC TODO: emulate own rpath somehow and complain on user's.
+ }
+ else
{
for (target* pt: t.prerequisite_targets)
{
@@ -1185,6 +1198,17 @@ namespace build2
args.push_back ("-o");
args.push_back (relt.string ().c_str ());
+
+ // Add any additional options (import library name, etc).
+ //
+ if (so)
+ {
+ if (tsys == "mingw32")
+ {
+ out = "-Wl,--out-implib=" + relt.string () + ".a";
+ args.push_back (out.c_str ());
+ }
+ }
}
}
@@ -1194,14 +1218,23 @@ namespace build2
{
path_target* ppt;
liba* a (nullptr);
+ libso* so (nullptr);
if ((ppt = pt->is_a<obja> ()) ||
(ppt = pt->is_a<objso> ()) ||
(lt != type::a &&
((ppt = a = pt->is_a<liba> ()) ||
- (ppt = pt->is_a<libso> ()))))
+ (ppt = so = pt->is_a<libso> ()))))
{
- sargs.push_back (relative (ppt->path ()).string ()); // string()&&
+ string p (relative (ppt->path ()).string ()); // string()&&
+
+ if (so != nullptr)
+ {
+ if (tsys == "mingw32")
+ p += ".a"; // Link to the import library (*.dll -> *.dll.a).
+ }
+
+ sargs.push_back (move (p));
// If this is a static library, link all the libraries it depends
// on, recursively.
@@ -1305,6 +1338,25 @@ namespace build2
return target_state::changed;
}
+ target_state link::
+ perform_clean (action a, target& xt)
+ {
+ file& t (static_cast<file&> (xt));
+
+ scope& rs (t.root_scope ());
+ const string& tsys (cast<string> (rs["cxx.target.system"]));
+
+ const char* e (nullptr);
+
+ if (link_type (t) == type::so)
+ {
+ if (tsys == "mingw32")
+ e = "+.a"; // Import library (*.dll.a).
+ }
+
+ return clean_extra (a, t, {"+.d", e});
+ }
+
link link::instance;
}
}
diff --git a/build2/types b/build2/types
index d3930ba..943c9e1 100644
--- a/build2/types
+++ b/build2/types
@@ -7,13 +7,15 @@
#include <vector>
#include <string>
-#include <memory> // unique_ptr, shared_ptr
-#include <utility> // pair, move()
-#include <cstddef> // size_t, nullptr_t
-#include <cstdint> // uint{8,16,32,64}_t
+#include <memory> // unique_ptr, shared_ptr
+#include <utility> // pair, move()
+#include <cstddef> // size_t, nullptr_t
+#include <cstdint> // uint{8,16,32,64}_t
#include <istream>
#include <ostream>
-#include <functional> // function, reference_wrapper
+#include <functional> // function, reference_wrapper
+#include <initializer_list>
+
#include <exception> // exception
#include <stdexcept> // logic_error, invalid_argument, runtime_error
@@ -44,6 +46,8 @@ namespace build2
using std::function;
using std::reference_wrapper;
+ using std::initializer_list;
+
using std::unique_ptr;
using std::shared_ptr;
using std::weak_ptr;