From 774bfb559ecaef2aac1dcb7a0414bc6895a9b9d5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 17 Jun 2016 16:42:38 +0200 Subject: Initial take on DLL support for MinGW toolchain --- build2/algorithm | 12 +++++++ build2/algorithm.cxx | 89 +++++++++++++++++++++++++++------------------ build2/cxx/compile.cxx | 6 ++-- build2/cxx/link | 3 ++ build2/cxx/link.cxx | 98 ++++++++++++++++++++++++++++++++++++++------------ build2/types | 14 +++++--- 6 files changed, 157 insertions(+), 65 deletions(-) (limited to 'build2') 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 extra_ext); } #include 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 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 (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 (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 (t), {}); + } + + target_state + perform_clean_depdb (action a, target& t) + { + return clean_extra (a, dynamic_cast (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 ()) { - // 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 ()) { - // 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 ()) { - // 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 (rs["cxx.id"])); - const string& sys (cast (rs["cxx.target.system"])); + const string& tsys (cast (rs["cxx.target.system"])); bool l (p.is_a ()); const string* ext (l ? nullptr : p.ext); // Only for liba/libso. @@ -306,23 +306,31 @@ namespace build2 if (l || p.is_a ()) { - // @@ 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 (rs["cxx.id"])); + const string& tsys (cast (rs["cxx.target.system"])); const string& tclass (cast (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 ()) || (ppt = pt->is_a ()) || (lt != type::a && ((ppt = a = pt->is_a ()) || - (ppt = pt->is_a ())))) + (ppt = so = pt->is_a ())))) { - 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 (xt)); + + scope& rs (t.root_scope ()); + const string& tsys (cast (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 #include -#include // unique_ptr, shared_ptr -#include // pair, move() -#include // size_t, nullptr_t -#include // uint{8,16,32,64}_t +#include // unique_ptr, shared_ptr +#include // pair, move() +#include // size_t, nullptr_t +#include // uint{8,16,32,64}_t #include #include -#include // function, reference_wrapper +#include // function, reference_wrapper +#include + #include // exception #include // 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; -- cgit v1.1