Index: bindings/ocaml/Makefile.ocaml =================================================================== --- bindings/ocaml/Makefile.ocaml +++ bindings/ocaml/Makefile.ocaml @@ -277,6 +277,8 @@ build-deplibs: $(OutputLibs) +$(OcamlDir)/%.so: $(LibDir)/%.so + $(Verb) ln -sf $< $@ $(OcamlDir)/%.a: $(LibDir)/%.a $(Verb) ln -sf $< $@ Index: tools/llvm-config/BuildVariables.inc.in =================================================================== --- tools/llvm-config/BuildVariables.inc.in +++ tools/llvm-config/BuildVariables.inc.in @@ -27,3 +27,6 @@ #define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@" #define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@" #define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@" +#define LLVM_ENABLE_DYLIB "@LLVM_BUILD_LLVM_DYLIB@" +#define LLVM_ENABLE_SHARED "@LLVM_ENABLE_SHARED@" +#define LLVM_DYLIB_COMPONENTS "@LLVM_DYLIB_COMPONENTS@" Index: tools/llvm-config/CMakeLists.txt =================================================================== --- tools/llvm-config/CMakeLists.txt +++ tools/llvm-config/CMakeLists.txt @@ -30,6 +30,11 @@ 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) Index: tools/llvm-config/Makefile =================================================================== --- tools/llvm-config/Makefile +++ tools/llvm-config/Makefile @@ -65,6 +65,14 @@ >> 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) $(SED) -f temp.sed < $< > $@ Index: tools/llvm-config/llvm-config.cpp =================================================================== --- tools/llvm-config/llvm-config.cpp +++ tools/llvm-config/llvm-config.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace llvm; @@ -51,12 +52,16 @@ /// \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 @@ // 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 @@ /// 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 @@ } 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 @@ -158,6 +177,7 @@ --build-mode Print build mode of LLVM tree (e.g. Debug or Release).\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\ + --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"; @@ -172,10 +192,38 @@ 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 @@ -270,6 +318,122 @@ 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, SharedDir, SharedPrefix, StaticExt, StaticDir = "lib", + StaticPrefix = "lib"; + const Triple HostTriple(Triple::normalize(LLVM_DEFAULT_TARGET_TRIPLE)); + if (HostTriple.isOSWindows()) { + SharedExt = "dll"; + StaticExt = "a"; + SharedDir = ActiveBinDir; + StaticDir = ActiveLibDir; + StaticPrefix = SharedPrefix = ""; + } else if (HostTriple.isOSDarwin()) { + SharedExt = "dylib"; + StaticExt = "a"; + StaticDir = SharedDir = ActiveLibDir; + StaticPrefix = SharedPrefix = "lib"; + } else { + // default to the unix values: + SharedExt = "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; + std::string DyLibName; + if (ConfigTool == CMake) { + DyLibName = + (SharedPrefix + "LLVM." + SharedExt + "." PACKAGE_VERSION).str(); + } else { + DyLibName = (SharedPrefix + "LLVM-" PACKAGE_VERSION "." + SharedExt).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(".a")) { + FromEnd = 2; + } else if (Lib.endswith(".so")) { + FromEnd = 3; + } else if (Lib.endswith(".so." PACKAGE_VERSION)) { + FromEnd = 4 + std::strlen(PACKAGE_VERSION); + } else if (Lib.endswith(".dylib")) { + FromEnd = 6; + } else if (Lib.endswith(".dylib." PACKAGE_VERSION)) { + FromEnd = 7 + std::strlen(PACKAGE_VERSION); + } 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) { + if (ConfigTool == CMake) { + LibFileName = (SharedPrefix + LibName + "." + SharedExt).str(); + if (!HostTriple.isOSWindows()) { + LibFileName += "." PACKAGE_VERSION; + } + } else { + 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]; @@ -303,13 +467,33 @@ } 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") { @@ -326,6 +510,8 @@ #endif } else if (Arg == "--build-system") { OS << LLVM_BUILD_SYSTEM << '\n'; + } else if (Arg == "--shared-mode") { + PrintSharedMode = true; } else if (Arg == "--obj-root") { OS << ActivePrefix << '\n'; } else if (Arg == "--src-root") { @@ -341,35 +527,84 @@ 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); } + } + }; + + if (HasMissing && DyLibExists) { + PrintForLib(DyLibName, true); + } else { + for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) { + StringRef Lib = RequiredLibs[i]; + if (i) + OS << ' '; - // Otherwise, print the full path. - OS << ActiveLibDir << '/' << Lib; + PrintForLib(Lib, false); } } OS << '\n';