From 82ae60a647ebb9567f7177c353245f4d72faaf5e Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 7 Sep 2012 21:37:19 +0300 Subject: [PATCH] Build and install import libraries on windows --- source/builder.cpp | 3 ++ source/exportdefinitions.cpp | 17 ++++++++ source/exportdefinitions.h | 27 ++++++++++++ source/gnulinker.cpp | 41 +++++++++++++++--- source/importlibrary.cpp | 21 ++++++++- source/importlibrary.h | 9 ++++ source/mingwdlltool.cpp | 84 ++++++++++++++++++++++++++++++++++++ source/mingwdlltool.h | 17 ++++++++ source/sharedlibrary.cpp | 11 ++++- source/sharedlibrary.h | 9 ++++ 10 files changed, 229 insertions(+), 10 deletions(-) create mode 100644 source/exportdefinitions.cpp create mode 100644 source/exportdefinitions.h create mode 100644 source/mingwdlltool.cpp create mode 100644 source/mingwdlltool.h diff --git a/source/builder.cpp b/source/builder.cpp index 3344429..6794842 100644 --- a/source/builder.cpp +++ b/source/builder.cpp @@ -21,6 +21,7 @@ #include "gnucxxcompiler.h" #include "gnulinker.h" #include "installedfile.h" +#include "mingwdlltool.h" #include "package.h" #include "pkgconfiggenerator.h" #include "sharedlibrary.h" @@ -203,6 +204,8 @@ Builder::Builder(int argc, char **argv): toolchain.add_tool(new Copy(*this)); toolchain.add_tool(new Tar(*this)); toolchain.add_tool(new PkgConfigGenerator(*this)); + if(current_arch->get_system()=="windows") + toolchain.add_tool(new MingwDllTool(*this, *current_arch)); } Builder::~Builder() diff --git a/source/exportdefinitions.cpp b/source/exportdefinitions.cpp new file mode 100644 index 0000000..cc50e16 --- /dev/null +++ b/source/exportdefinitions.cpp @@ -0,0 +1,17 @@ +#include "component.h" +#include "exportdefinitions.h" +#include "sourcepackage.h" + +using namespace Msp; + +ExportDefinitions::ExportDefinitions(Builder &b, const Component &c, SharedLibrary &l): + FileTarget(b, c.get_package(), generate_target_path(c)), + lib(l) +{ + component = &c; +} + +FS::Path ExportDefinitions::generate_target_path(const Component &comp) +{ + return comp.get_package().get_temp_dir()/comp.get_name()/(comp.get_name()+".def"); +} diff --git a/source/exportdefinitions.h b/source/exportdefinitions.h new file mode 100644 index 0000000..73d6f0f --- /dev/null +++ b/source/exportdefinitions.h @@ -0,0 +1,27 @@ +#ifndef EXPORTDEFINITIONS_H_ +#define EXPORTDEFINITIONS_H_ + +#include "filetarget.h" + +class SharedLibrary; + +/** +An export definition file for a shared library. Only used on Windows. +*/ +class ExportDefinitions: public FileTarget +{ +private: + SharedLibrary &lib; + +public: + ExportDefinitions(Builder &, const Component &, SharedLibrary &); +private: + static Msp::FS::Path generate_target_path(const Component &); + +public: + virtual const char *get_type() const { return "ExportDefinitions"; } + + SharedLibrary &get_library() const { return lib; } +}; + +#endif diff --git a/source/gnulinker.cpp b/source/gnulinker.cpp index ce51c44..114bf09 100644 --- a/source/gnulinker.cpp +++ b/source/gnulinker.cpp @@ -7,6 +7,7 @@ #include "builder.h" #include "component.h" #include "executable.h" +#include "exportdefinitions.h" #include "externaltask.h" #include "gnucompiler.h" #include "gnulinker.h" @@ -69,7 +70,18 @@ Target *GnuLinker::create_target(const list &sources, const string &ar const Component &comp = *objs.front()->get_component(); Binary *bin = 0; if(arg=="shared") - bin = new SharedLibrary(builder, comp, objs); + { + SharedLibrary *shlib = new SharedLibrary(builder, comp, objs); + if(architecture->get_system()=="windows") + { + ExportDefinitions *exp = new ExportDefinitions(builder, comp, *shlib); + shlib->add_side_effect(*exp); + const Tool &dlltool = builder.get_toolchain().get_tool("DLL"); + ImportLibrary *imp = dynamic_cast(dlltool.create_target(*exp)); + shlib->set_import_library(imp); + } + bin = shlib; + } else bin = new Executable(builder, comp, objs); bin->set_tool(*linker); @@ -82,8 +94,13 @@ Target *GnuLinker::create_install(Target &target) const { const Tool © = builder.get_toolchain().get_tool("CP"); InstalledFile *inst_tgt = dynamic_cast(copy.create_target(target)); - const Pattern &pattern = architecture->get_shared_library_patterns().front(); - inst_tgt->set_symlink(pattern.apply(shlib->get_libname())); + if(architecture->get_system()=="windows") + builder.add_installed_target(*shlib->get_import_library()); + else + { + const Pattern &pattern = architecture->get_shared_library_patterns().front(); + inst_tgt->set_symlink(pattern.apply(shlib->get_libname())); + } return inst_tgt; } else @@ -151,11 +168,19 @@ Task *GnuLinker::Linker::run(const Target &target) const const Component &comp = *bin.get_component(); + FS::Path work_dir = comp.get_package().get_source_directory(); + if(const SharedLibrary *shlib = dynamic_cast(&bin)) { argv.push_back("-shared"); argv.push_back("-fPIC"); - if(!shlib->get_soname().empty()) + const Target::Dependencies &side_eff = target.get_side_effects(); + for(Target::Dependencies::const_iterator i=side_eff.begin(); i!=side_eff.end(); ++i) + { + if(ExportDefinitions *exp = dynamic_cast(*i)) + argv.push_back("-Wl,--output-def,"+relative(exp->get_path(), work_dir).str()); + } + if(architecture->get_system()!="windows" && !shlib->get_soname().empty()) argv.push_back("-Wl,-soname,"+shlib->get_soname()); } @@ -171,8 +196,6 @@ Task *GnuLinker::Linker::run(const Target &target) const if(architecture->get_bits()!=native_arch.get_bits()) argv.push_back(format("-m%d", architecture->get_bits())); - FS::Path work_dir = comp.get_package().get_source_directory(); - argv.push_back("-o"); argv.push_back(relative(bin.get_path(), work_dir).str()); @@ -194,7 +217,11 @@ Task *GnuLinker::Linker::run(const Target &target) const } else if(ImportLibrary *imp = dynamic_cast(tgt)) { - argv.push_back(imp->get_path().str()); + shlib = imp->get_shared_library(); + if(shlib) + argv.push_back("-l"+shlib->get_libname()); + else + argv.push_back(imp->get_path().str()); static_link_ok = false; } } diff --git a/source/importlibrary.cpp b/source/importlibrary.cpp index 60e5511..9ac441f 100644 --- a/source/importlibrary.cpp +++ b/source/importlibrary.cpp @@ -1,8 +1,27 @@ #include +#include "component.h" +#include "exportdefinitions.h" #include "importlibrary.h" +#include "sourcepackage.h" +using namespace std; using namespace Msp; ImportLibrary::ImportLibrary(Builder &b, const FS::Path &p): - FileTarget(b, p) + FileTarget(b, p), + shared_lib(0) { } + +ImportLibrary::ImportLibrary(Builder &b, const Component &c, ExportDefinitions &exp): + FileTarget(b, c.get_package(), c.get_package().get_out_dir()/format("lib%s.dll.a", c.get_name())), + shared_lib(&exp.get_library()) +{ + component = &c; + add_dependency(exp); + + install_location = "lib"; + + const string &version = component->get_package().get_interface_version(); + if(!version.empty()) + install_filename = format("lib%s-%s.dll.a", c.get_name(), version); +} diff --git a/source/importlibrary.h b/source/importlibrary.h index 86d26f4..99db24d 100644 --- a/source/importlibrary.h +++ b/source/importlibrary.h @@ -3,16 +3,25 @@ #include "filetarget.h" +class ExportDefinitions; +class SharedLibrary; + /** A special case of static library which pulls in a shared library. Used on platforms with no true dynamic linking support. */ class ImportLibrary: public FileTarget { +private: + SharedLibrary *shared_lib; + public: ImportLibrary(Builder &, const Msp::FS::Path &); + ImportLibrary(Builder &, const Component &, ExportDefinitions &); virtual const char *get_type() const { return "ImportLibrary"; } + + SharedLibrary *get_shared_library() const { return shared_lib; } }; #endif diff --git a/source/mingwdlltool.cpp b/source/mingwdlltool.cpp new file mode 100644 index 0000000..582963e --- /dev/null +++ b/source/mingwdlltool.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include "architecture.h" +#include "builder.h" +#include "component.h" +#include "exportdefinitions.h" +#include "externaltask.h" +#include "importlibrary.h" +#include "installedfile.h" +#include "mingwdlltool.h" +#include "sharedlibrary.h" +#include "sourcepackage.h" + +using namespace std; +using namespace Msp; + +MingwDllTool::MingwDllTool(Builder &b, const Architecture &a): + Tool(b, a, "DLL") +{ + string command = "dlltool"; + if(architecture->is_cross()) + command = format("%s-%s", architecture->get_cross_prefix(), command); + executable = builder.get_vfs().find_binary(command); +} + +Target *MingwDllTool::create_target(const list &sources, const string &) const +{ + if(sources.size()!=1) + throw invalid_argument("MingwDllTool::create_target"); + ExportDefinitions &def = dynamic_cast(*sources.front()); + ImportLibrary *imp = new ImportLibrary(builder, *def.get_component(), def); + imp->set_tool(*this); + return imp; +} + +Target *MingwDllTool::create_install(Target &target) const +{ + if(ImportLibrary *imp = dynamic_cast(&target)) + { + const Tool © = builder.get_toolchain().get_tool("CP"); + InstalledFile *inst_tgt = dynamic_cast(copy.create_target(target)); + string link_name = format("lib%s.dll.a", imp->get_shared_library()->get_libname()); + inst_tgt->set_symlink(link_name); + return inst_tgt; + } + else + return 0; +} + +Task *MingwDllTool::run(const Target &target) const +{ + const ImportLibrary &imp = dynamic_cast(target); + const ExportDefinitions &exp = dynamic_cast(*imp.get_dependencies().front()); + const SharedLibrary &shlib = exp.get_library(); + + vector argv; + argv.push_back(executable->get_path().str()); + + /* dlltool is stupid and puts temporary files in the working directory by + default */ + argv.push_back("--temp-prefix"); + char random[8]; + for(unsigned i=0; i<8; ++i) + random[i] = 'a'+(rand()%26); + argv.push_back(string("/tmp/")+string(random, 8)); + + const Component &comp = *imp.get_component(); + FS::Path work_dir = comp.get_package().get_source_directory(); + + argv.push_back("-d"); + argv.push_back(relative(exp.get_path(), work_dir).str()); + + argv.push_back("-D"); + if(shlib.get_install_filename().empty()) + argv.push_back(FS::basename(shlib.get_path())); + else + argv.push_back(shlib.get_install_filename()); + + argv.push_back("-l"); + argv.push_back(relative(imp.get_path(), work_dir).str()); + + return new ExternalTask(argv, work_dir); +} diff --git a/source/mingwdlltool.h b/source/mingwdlltool.h new file mode 100644 index 0000000..25a1e10 --- /dev/null +++ b/source/mingwdlltool.h @@ -0,0 +1,17 @@ +#ifndef DLLTOOL_H_ +#define DLLTOOL_H_ + +#include "tool.h" + +class MingwDllTool: public Tool +{ +public: + MingwDllTool(Builder &, const Architecture &); + + virtual Target *create_target(const std::list &, const std::string &) const; + virtual Target *create_install(Target &) const; + + virtual Task *run(const Target &) const; +}; + +#endif diff --git a/source/sharedlibrary.cpp b/source/sharedlibrary.cpp index 56f9b32..06a9944 100644 --- a/source/sharedlibrary.cpp +++ b/source/sharedlibrary.cpp @@ -9,7 +9,8 @@ using namespace std; using namespace Msp; SharedLibrary::SharedLibrary(Builder &b, const Msp::FS::Path &p): - Binary(b, p) + Binary(b, p), + import_lib(0) { libname = FS::basepart(FS::basename(path)); if(!libname.compare(0, 3, "lib")) @@ -18,7 +19,8 @@ SharedLibrary::SharedLibrary(Builder &b, const Msp::FS::Path &p): SharedLibrary::SharedLibrary(Builder &b, const Component &c, const list &objs): Binary(b, c, generate_filename(c), objs), - libname(c.get_name()) + libname(c.get_name()), + import_lib(0) { install_location = "lib"; if(component->get_type()==Component::MODULE) @@ -48,3 +50,8 @@ string SharedLibrary::generate_filename(const Component &comp) return arch.get_shared_library_patterns().front().apply(comp.get_name()); } } + +void SharedLibrary::set_import_library(ImportLibrary *imp) +{ + import_lib = imp; +} diff --git a/source/sharedlibrary.h b/source/sharedlibrary.h index 5331ef5..c40bccd 100644 --- a/source/sharedlibrary.h +++ b/source/sharedlibrary.h @@ -3,17 +3,23 @@ #include "binary.h" +class ImportLibrary; + /** Represents a shared library. It has two special properties: libname and soname. Libname is the name used by the linker. Soname is the canonical filename of the library, including version number. If the owning package has no version, soname will be empty. + +A SharedLibrary can also store a pointer to the associated ImportLibrary, for +platforms that need one. */ class SharedLibrary: public Binary { private: std::string libname; std::string soname; + ImportLibrary *import_lib; public: SharedLibrary(Builder &, const Msp::FS::Path &); @@ -25,6 +31,9 @@ public: virtual const char *get_type() const { return "SharedLibrary"; } const std::string &get_libname() const { return libname; } const std::string &get_soname() const { return soname; } + + void set_import_library(ImportLibrary *); + ImportLibrary *get_import_library() const { return import_lib; } }; #endif -- 2.45.2