Fix `llvm-config` to adapt to the install environment.
authorRichard Diamond <wichard@vitalitystudios.com>
Mon, 9 Nov 2015 23:15:38 +0000 (23:15 +0000)
committerRichard Diamond <wichard@vitalitystudios.com>
Mon, 9 Nov 2015 23:15:38 +0000 (23:15 +0000)
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
tools/llvm-config/BuildVariables.inc.in
tools/llvm-config/CMakeLists.txt
tools/llvm-config/Makefile
tools/llvm-config/llvm-config.cpp

index 1f65a7b8f905f6900f8301d90e5cf7cf6feb8bf3..22b96a298effff50cb01f309d51563119ab4503d 100644 (file)
@@ -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 $< $@
 
index 38465f60d070dd44dc15524ad5f0e6c0a7cd8fe8..345f47e91b4a39243a6d7e406f6052ffd664c5dd 100644 (file)
@@ -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@"
index d1f8706496219c35b9e4b2e96802ff0a31403a12..83794bb3fddd6923de19d8c0776939bf8af7a25b 100644 (file)
@@ -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)
 
index 03d910bb03f05bd6c74bb0549ca788fec5504479..d2fe2cfa34aadc6ef027b089316044368d636832 100644 (file)
@@ -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)/' \
index 8bf2680d3918b32ef457396d19853041c6eda7fd..44ab715f965d5c26556876db31356172a9a25143 100644 (file)
@@ -30,6 +30,7 @@
 #include <cstdlib>
 #include <set>
 #include <vector>
+#include <unordered_set>
 
 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<AvailableComponent*> &ComponentMap,
                            std::set<AvailableComponent*> &VisitedComponents,
                            std::vector<StringRef> &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<StringRef> &Components,
                                      std::vector<StringRef> &RequiredLibs,
-                                     bool IncludeNonInstalled) {
+                                     bool IncludeNonInstalled, bool GetComponentNames,
+                                     const std::string *ActiveLibDir,
+                                     bool *HasMissing) {
   std::set<AvailableComponent*> VisitedComponents;
 
   // Build a map of component names to information.
@@ -116,7 +134,8 @@ static void ComputeLibsForComponents(const std::vector<StringRef> &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<StringRef> GetAllDyLibComponents(const bool IsInDevelopmentTree,
+                                             const bool GetComponentNames) {
+  std::vector<StringRef> 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<StringRef> Components;
+  ComputeLibsForComponents(DyLibComponents, Components,
+                           /*IncludeNonInstalled=*/IsInDevelopmentTree,
+                           GetComponentNames, nullptr, nullptr);
+
+  return std::move(Components);
+}
+
 int main(int argc, char **argv) {
   std::vector<StringRef> 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<StringRef> 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<StringRef> RequiredLibs;
+    bool HasMissing = false;
     ComputeLibsForComponents(Components, RequiredLibs,
-                             /*IncludeNonInstalled=*/IsInDevelopmentTree);
+                             /*IncludeNonInstalled=*/IsInDevelopmentTree, false,
+                             &ActiveLibDir, &HasMissing);
+
+    if (PrintSharedMode) {
+      std::unordered_set<std::string> FullDyLibComponents;
+      std::vector<StringRef> 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';