From 6c6be14bb5660acf923b8ca05b93d320cf59dfb4 Mon Sep 17 00:00:00 2001 From: Richard Diamond Date: Mon, 9 Nov 2015 23:15:38 +0000 Subject: [PATCH] Fix `llvm-config` to adapt to the install environment. Summary: This patch does a couple of things: - Adds a new argument `--shared-mode` which accepts a list of components and prints whether or not the provided components need to be linked statically or shared. - Fixes `--libnames` when CMake BUILD_SHARED_LIBS is used. - Fixes `--libnames`, `--libs`, and `--libfiles` for dylib when static components aren't installed. - Fixes `--libnames`, `--libs`, `--libfiles`, and `--components` to use LLVM_DYLIB_COMPONENTS as the component manifest for dylib linking. - Uses the host platform's usual convention for filename extensions and such, instead of always defaulting to Unix-izms. Because I don't own a Mac, I am not able to test the Mac platform dependent stuff locally. If someone would be willing to run a build for me on their machine (unless there's a better option), I'd appreciate it. Reviewers: jfb, brad.king, whitequark, beanz Subscribers: beanz, jauhien, llvm-commits Differential Revision: http://reviews.llvm.org/D13198 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252532 91177308-0d34-0410-b5e6-96231b3b80d8 --- bindings/ocaml/Makefile.ocaml | 2 + tools/llvm-config/BuildVariables.inc.in | 3 + tools/llvm-config/CMakeLists.txt | 5 + tools/llvm-config/Makefile | 8 + tools/llvm-config/llvm-config.cpp | 265 ++++++++++++++++++++++-- 5 files changed, 261 insertions(+), 22 deletions(-) diff --git a/bindings/ocaml/Makefile.ocaml b/bindings/ocaml/Makefile.ocaml index 1f65a7b8f90..22b96a298ef 100644 --- a/bindings/ocaml/Makefile.ocaml +++ b/bindings/ocaml/Makefile.ocaml @@ -277,6 +277,8 @@ uninstall-local:: uninstall-deplibs build-deplibs: $(OutputLibs) +$(OcamlDir)/%.so: $(LibDir)/%.so + $(Verb) ln -sf $< $@ $(OcamlDir)/%.a: $(LibDir)/%.a $(Verb) ln -sf $< $@ diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in index 38465f60d07..345f47e91b4 100644 --- a/tools/llvm-config/BuildVariables.inc.in +++ b/tools/llvm-config/BuildVariables.inc.in @@ -28,3 +28,6 @@ #define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@" #define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@" #define LLVM_HAS_RTTI "@LLVM_HAS_RTTI@" +#define LLVM_ENABLE_DYLIB "@LLVM_BUILD_LLVM_DYLIB@" +#define LLVM_ENABLE_SHARED "@LLVM_ENABLE_SHARED@" +#define LLVM_DYLIB_COMPONENTS "@LLVM_DYLIB_COMPONENTS@" diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt index d1f87064962..83794bb3fdd 100644 --- a/tools/llvm-config/CMakeLists.txt +++ b/tools/llvm-config/CMakeLists.txt @@ -31,6 +31,11 @@ set(LLVM_HAS_RTTI ${LLVM_CONFIG_HAS_RTTI}) set(LLVM_LDFLAGS "${CMAKE_CXX_LINK_FLAGS}") set(LLVM_BUILDMODE ${CMAKE_BUILD_TYPE}) set(LLVM_SYSTEM_LIBS ${SYSTEM_LIBS}) +if(BUILD_SHARED_LIBS) + set(LLVM_ENABLE_SHARED ON) +else() + set(LLVM_ENABLE_SHARED OFF) +endif() string(REPLACE ";" " " LLVM_TARGETS_BUILT "${LLVM_TARGETS_TO_BUILD}") configure_file(${BUILDVARIABLES_SRCPATH} ${BUILDVARIABLES_OBJPATH} @ONLY) diff --git a/tools/llvm-config/Makefile b/tools/llvm-config/Makefile index 03d910bb03f..d2fe2cfa34a 100644 --- a/tools/llvm-config/Makefile +++ b/tools/llvm-config/Makefile @@ -71,6 +71,14 @@ $(ObjDir)/BuildVariables.inc: $(BUILDVARIABLES_SRCPATH) Makefile $(ObjDir)/.dir >> temp.sed $(Verb) $(ECHO) 's/@LLVM_TARGETS_BUILT@/$(subst /,\/,$(TARGETS_TO_BUILD))/' \ >> temp.sed + $(if $(filter-out $(ENABLE_SHARED),0),\ + $(Verb) $(ECHO) 's/@LLVM_BUILD_LLVM_DYLIB@/ON/',\ + $(Verb) $(ECHO) 's/@LLVM_BUILD_LLVM_DYLIB@/OFF/') \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_ENABLE_SHARED@/OFF/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_DYLIB_COMPONENTS@/all/' \ + >> temp.sed $(Verb) $(ECHO) 's/@LLVM_BUILD_SYSTEM@/autoconf/' \ >> temp.sed $(Verb) $(ECHO) 's/@LLVM_HAS_RTTI@/$(LLVM_HAS_RTTI)/' \ diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp index 8bf2680d391..44ab715f965 100644 --- a/tools/llvm-config/llvm-config.cpp +++ b/tools/llvm-config/llvm-config.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace llvm; @@ -51,12 +52,16 @@ using namespace llvm; /// \param Name - The component to traverse. /// \param ComponentMap - A prebuilt map of component names to descriptors. /// \param VisitedComponents [in] [out] - The set of already visited components. -/// \param RequiredLibs [out] - The ordered list of required libraries. +/// \param RequiredLibs [out] - The ordered list of required +/// libraries. +/// \param GetComponentNames - Get the component names instead of the +/// library name. static void VisitComponent(StringRef Name, const StringMap &ComponentMap, std::set &VisitedComponents, std::vector &RequiredLibs, - bool IncludeNonInstalled) { + bool IncludeNonInstalled, bool GetComponentNames, + const std::string *ActiveLibDir, bool *HasMissing) { // Lookup the component. AvailableComponent *AC = ComponentMap.lookup(Name); assert(AC && "Invalid component name!"); @@ -74,12 +79,22 @@ static void VisitComponent(StringRef Name, // Otherwise, visit all the dependencies. for (unsigned i = 0; AC->RequiredLibraries[i]; ++i) { VisitComponent(AC->RequiredLibraries[i], ComponentMap, VisitedComponents, - RequiredLibs, IncludeNonInstalled); + RequiredLibs, IncludeNonInstalled, GetComponentNames, + ActiveLibDir, HasMissing); + } + + if (GetComponentNames) { + RequiredLibs.push_back(Name); + return; } // Add to the required library list. - if (AC->Library) + if (AC->Library) { + if (!IncludeNonInstalled && HasMissing && !*HasMissing && ActiveLibDir) { + *HasMissing = !sys::fs::exists(*ActiveLibDir + "/" + AC->Library); + } RequiredLibs.push_back(AC->Library); + } } /// \brief Compute the list of required libraries for a given list of @@ -91,9 +106,12 @@ static void VisitComponent(StringRef Name, /// are required to link the given components. /// \param IncludeNonInstalled - Whether non-installed components should be /// reported. +/// \param GetComponentNames - True if one would prefer the component names. static void ComputeLibsForComponents(const std::vector &Components, std::vector &RequiredLibs, - bool IncludeNonInstalled) { + bool IncludeNonInstalled, bool GetComponentNames, + const std::string *ActiveLibDir, + bool *HasMissing) { std::set VisitedComponents; // Build a map of component names to information. @@ -116,7 +134,8 @@ static void ComputeLibsForComponents(const std::vector &Components, } VisitComponent(ComponentLower, ComponentMap, VisitedComponents, - RequiredLibs, IncludeNonInstalled); + RequiredLibs, IncludeNonInstalled, GetComponentNames, + ActiveLibDir, HasMissing); } // The list is now ordered with leafs first, we want the libraries to printed @@ -159,6 +178,7 @@ Options:\n\ --assertion-mode Print assertion mode of LLVM tree (ON or OFF).\n\ --build-system Print the build system used to build LLVM (autoconf or cmake).\n\ --has-rtti Print whether or not LLVM was built with rtti (YES or NO).\n\ + --shared-mode Print how the provided components can be collectively linked (`shared` or `static`).\n\ Typical components:\n\ all All LLVM libraries (default).\n\ engine Either a native JIT or a bitcode interpreter.\n"; @@ -173,10 +193,38 @@ std::string GetExecutablePath(const char *Argv0) { return llvm::sys::fs::getMainExecutable(Argv0, P); } +/// \brief Expand the semi-colon delimited LLVM_DYLIB_COMPONENTS into +/// the full list of components. +std::vector GetAllDyLibComponents(const bool IsInDevelopmentTree, + const bool GetComponentNames) { + std::vector DyLibComponents; + { + StringRef DyLibComponentsStr(LLVM_DYLIB_COMPONENTS); + size_t Offset = 0; + while (true) { + const size_t NextOffset = DyLibComponentsStr.find(';', Offset); + DyLibComponents.push_back(DyLibComponentsStr.substr(Offset, NextOffset)); + if (NextOffset == std::string::npos) { + break; + } + Offset = NextOffset + 1; + } + + assert(DyLibComponents.size() > 0); + } + + std::vector Components; + ComputeLibsForComponents(DyLibComponents, Components, + /*IncludeNonInstalled=*/IsInDevelopmentTree, + GetComponentNames, nullptr, nullptr); + + return std::move(Components); +} + int main(int argc, char **argv) { std::vector Components; bool PrintLibs = false, PrintLibNames = false, PrintLibFiles = false; - bool PrintSystemLibs = false; + bool PrintSystemLibs = false, PrintSharedMode = false; bool HasAnyOption = false; // llvm-config is designed to support being run both from a development tree @@ -271,6 +319,108 @@ int main(int argc, char **argv) { ActiveIncludeOption = "-I" + ActiveIncludeDir; } + /// We only use `shared library` mode in cases where the static library form + /// of the components provided are not available; note however that this is + /// skipped if we're run from within the build dir. However, once installed, + /// we still need to provide correct output when the static archives are + /// removed or, as in the case of CMake's `BUILD_SHARED_LIBS`, never present + /// in the first place. This can't be done at configure/build time. + + StringRef SharedExt, SharedVersionedExt, SharedDir, SharedPrefix, StaticExt, + StaticPrefix, StaticDir = "lib"; + const Triple HostTriple(Triple::normalize(LLVM_DEFAULT_TARGET_TRIPLE)); + if (HostTriple.isOSWindows()) { + SharedExt = "dll"; + SharedVersionedExt = PACKAGE_VERSION ".dll"; + StaticExt = "a"; + SharedDir = ActiveBinDir; + StaticDir = ActiveLibDir; + StaticPrefix = SharedPrefix = ""; + } else if (HostTriple.isOSDarwin()) { + SharedExt = "dylib"; + SharedVersionedExt = PACKAGE_VERSION ".dylib"; + StaticExt = "a"; + StaticDir = SharedDir = ActiveLibDir; + StaticPrefix = SharedPrefix = "lib"; + } else { + // default to the unix values: + SharedExt = "so"; + SharedVersionedExt = PACKAGE_VERSION ".so"; + StaticExt = "a"; + StaticDir = SharedDir = ActiveLibDir; + StaticPrefix = SharedPrefix = "lib"; + } + + const bool BuiltDyLib = (std::strcmp(LLVM_ENABLE_DYLIB, "ON") == 0); + + enum { CMake, AutoConf } ConfigTool; + if (std::strcmp(LLVM_BUILD_SYSTEM, "cmake") == 0) { + ConfigTool = CMake; + } else { + ConfigTool = AutoConf; + } + + /// CMake style shared libs, ie each component is in a shared library. + const bool BuiltSharedLibs = + (ConfigTool == CMake && std::strcmp(LLVM_ENABLE_SHARED, "ON") == 0); + + bool DyLibExists = false; + const std::string DyLibName = + (SharedPrefix + "LLVM-" + SharedVersionedExt).str(); + + if (BuiltDyLib) { + DyLibExists = sys::fs::exists(SharedDir + "/" + DyLibName); + } + + /// Get the component's library name without the lib prefix and the + /// extension. Returns true if Lib is in a recognized format. + auto GetComponentLibraryNameSlice = [&](const StringRef &Lib, + StringRef &Out) { + if (Lib.startswith("lib")) { + unsigned FromEnd; + if (Lib.endswith(StaticExt)) { + FromEnd = StaticExt.size() + 1; + } else if (Lib.endswith(SharedExt)) { + FromEnd = SharedExt.size() + 1; + } else { + FromEnd = 0; + } + + if (FromEnd != 0) { + Out = Lib.slice(3, Lib.size() - FromEnd); + return true; + } + } + + return false; + }; + /// Maps Unixizms to the host platform. + auto GetComponentLibraryFileName = [&](const StringRef &Lib, + const bool ForceShared) { + std::string LibFileName = Lib; + StringRef LibName; + if (GetComponentLibraryNameSlice(Lib, LibName)) { + if (BuiltSharedLibs || ForceShared) { + LibFileName = (SharedPrefix + LibName + "." + SharedExt).str(); + } else { + // default to static + LibFileName = (StaticPrefix + LibName + "." + StaticExt).str(); + } + } + + return LibFileName; + }; + /// Get the full path for a possibly shared component library. + auto GetComponentLibraryPath = [&](const StringRef &Name, + const bool ForceShared) { + auto LibFileName = GetComponentLibraryFileName(Name, ForceShared); + if (BuiltSharedLibs || ForceShared) { + return (SharedDir + "/" + LibFileName).str(); + } else { + return (StaticDir + "/" + LibFileName).str(); + } + }; + raw_ostream &OS = outs(); for (int i = 1; i != argc; ++i) { StringRef Arg = argv[i]; @@ -304,13 +454,33 @@ int main(int argc, char **argv) { } else if (Arg == "--libfiles") { PrintLibFiles = true; } else if (Arg == "--components") { + /// If there are missing static archives and a dylib was + /// built, print LLVM_DYLIB_COMPONENTS instead of everything + /// in the manifest. + std::vector Components; for (unsigned j = 0; j != array_lengthof(AvailableComponents); ++j) { // Only include non-installed components when in a development tree. if (!AvailableComponents[j].IsInstalled && !IsInDevelopmentTree) continue; - OS << ' '; - OS << AvailableComponents[j].Name; + Components.push_back(AvailableComponents[j].Name); + if (AvailableComponents[j].Library && !IsInDevelopmentTree) { + if (DyLibExists && + !sys::fs::exists(GetComponentLibraryPath( + AvailableComponents[j].Library, false))) { + Components = GetAllDyLibComponents(IsInDevelopmentTree, true); + std::sort(Components.begin(), Components.end()); + break; + } + } + } + + for (unsigned I = 0; I < Components.size(); ++I) { + if (I) { + OS << ' '; + } + + OS << Components[I]; } OS << '\n'; } else if (Arg == "--targets-built") { @@ -329,6 +499,8 @@ int main(int argc, char **argv) { OS << LLVM_BUILD_SYSTEM << '\n'; } else if (Arg == "--has-rtti") { OS << LLVM_HAS_RTTI << '\n'; + } else if (Arg == "--shared-mode") { + PrintSharedMode = true; } else if (Arg == "--obj-root") { OS << ActivePrefix << '\n'; } else if (Arg == "--src-root") { @@ -344,35 +516,84 @@ int main(int argc, char **argv) { if (!HasAnyOption) usage(); - if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs) { + if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs || + PrintSharedMode) { + + if (PrintSharedMode && BuiltSharedLibs) { + OS << "shared\n"; + return 0; + } + // If no components were specified, default to "all". if (Components.empty()) Components.push_back("all"); // Construct the list of all the required libraries. std::vector RequiredLibs; + bool HasMissing = false; ComputeLibsForComponents(Components, RequiredLibs, - /*IncludeNonInstalled=*/IsInDevelopmentTree); + /*IncludeNonInstalled=*/IsInDevelopmentTree, false, + &ActiveLibDir, &HasMissing); + + if (PrintSharedMode) { + std::unordered_set FullDyLibComponents; + std::vector DyLibComponents = + GetAllDyLibComponents(IsInDevelopmentTree, false); + + for (auto &Component : DyLibComponents) { + FullDyLibComponents.insert(Component); + } + DyLibComponents.clear(); + + for (auto &Lib : RequiredLibs) { + if (!FullDyLibComponents.count(Lib)) { + OS << "static\n"; + return 0; + } + } + FullDyLibComponents.clear(); + + if (HasMissing && DyLibExists) { + OS << "shared\n"; + return 0; + } else { + OS << "static\n"; + return 0; + } + } if (PrintLibs || PrintLibNames || PrintLibFiles) { - for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) { - StringRef Lib = RequiredLibs[i]; - if (i) - OS << ' '; + auto PrintForLib = [&](const StringRef &Lib, const bool ForceShared) { if (PrintLibNames) { - OS << Lib; + OS << GetComponentLibraryFileName(Lib, ForceShared); } else if (PrintLibFiles) { - OS << ActiveLibDir << '/' << Lib; + OS << GetComponentLibraryPath(Lib, ForceShared); } else if (PrintLibs) { // If this is a typical library name, include it using -l. - if (Lib.startswith("lib") && Lib.endswith(".a")) { - OS << "-l" << Lib.slice(3, Lib.size()-2); - continue; + StringRef LibName; + if (Lib.startswith("lib")) { + if (GetComponentLibraryNameSlice(Lib, LibName)) { + OS << "-l" << LibName; + } else { + OS << "-l:" << GetComponentLibraryFileName(Lib, ForceShared); + } + } else { + // Otherwise, print the full path. + OS << GetComponentLibraryPath(Lib, ForceShared); } + } + }; - // Otherwise, print the full path. - OS << ActiveLibDir << '/' << Lib; + if (HasMissing && DyLibExists) { + PrintForLib(DyLibName, true); + } else { + for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) { + StringRef Lib = RequiredLibs[i]; + if (i) + OS << ' '; + + PrintForLib(Lib, false); } } OS << '\n';