From 10add60748226d67d3a1e4d1a8175f798a053708 Mon Sep 17 00:00:00 2001 From: Andrew Wilkins Date: Tue, 1 Sep 2015 03:14:31 +0000 Subject: [PATCH] Enable linking tools, shared libraries against libLLVM Summary: Three closely related changes, to have a mode in which we link all executables and shared libraries against libLLVM. 1. Add a new LLVM_LINK_LLVM_DYLIB cmake option, which, when ON, will link executables and shared libraries against libLLVM. For this to work, it is necessary to also set LLVM_BUILD_LLVM_DYLIB and LLVM_DYLIB_EXPORT_ALL. It is not strictly necessary to set LLVM_DISABLE_LLVM_DYLIB_ATEXIT, but we also default to OFF in this mode, or tools tend to misbehave (e.g. stdout may not flush on exit when output is buffered.) llvm-config and Tablegen do not use libLLVM, as they are dependencies of libLLVM. 2. Modify llvm-go to take a new flag, "linkmode=component-libs|dylib". Depending on which one is passed (default is component-libs), we link with the individual libraries or libLLVM respectively. We pass in dylib when LLVM_LINK_LLVM_DYLIB is ON. 3. Fix LLVM_DYLIB_EXPORT_ALL on Linux, and expand the symbols exported to actually export all. Don't strip leading underscore from symbols on Linux, and make sure we get all exported symbols and weak-with-default symbols ("W" in nm output). Without these changes, passes won't load because the "Annotate..." symbols defined in lib/Support/Valigrind.cpp are not found. Testing: - Ran default build ("ninja") with LLVM, clang, compiler-rt, llgo, lldb. - Ran "check", "check-clang", "check-tsan", "check-libgo" targets. I've never had much success with LLDB tests, and llgoi is currently broken so check-llgo fails for an unrelated reason. - Ran "lldb" to ensure it loads. Reviewers: chandlerc, beanz, pcc, rnk Subscribers: rnk, chapuni, sylvestre.ledru, llvm-commits Differential Revision: http://reviews.llvm.org/D12488 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@246527 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 11 ++-- cmake/modules/AddLLVM.cmake | 35 +++++++++---- cmake/modules/TableGen.cmake | 8 ++- tools/llvm-go/llvm-go.go | 91 +++++++++++++++++++-------------- tools/llvm-shlib/CMakeLists.txt | 41 +++++++++++++-- 5 files changed, 130 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85176fa2058..552ea5553b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,9 +347,14 @@ option (LLVM_ENABLE_SPHINX "Use Sphinx to generate llvm documentation." OFF) option (LLVM_BUILD_EXTERNAL_COMPILER_RT "Build compiler-rt as an external project." OFF) -option(LLVM_BUILD_LLVM_DYLIB "Build libllvm dynamic library" OFF) -option(LLVM_DYLIB_EXPORT_ALL "Export all symbols from libLLVM.dylib (default is C API only" OFF) -option(LLVM_DISABLE_LLVM_DYLIB_ATEXIT "Disable llvm-shlib's atexit destructors." ON) +option(LLVM_LINK_LLVM_DYLIB "Link tools against the libllvm dynamic library" OFF) +option(LLVM_BUILD_LLVM_DYLIB "Build libllvm dynamic library" ${LLVM_LINK_LLVM_DYLIB}) +option(LLVM_DYLIB_EXPORT_ALL "Export all symbols from libLLVM.dylib (default is C API only" ${LLVM_LINK_LLVM_DYLIB}) +set(LLVM_DISABLE_LLVM_DYLIB_ATEXIT_DEFAULT ON) +if (LLVM_LINK_LLVM_DYLIB) + set(LLVM_DISABLE_LLVM_DYLIB_ATEXIT_DEFAULT OFF) +endif() +option(LLVM_DISABLE_LLVM_DYLIB_ATEXIT "Disable llvm-shlib's atexit destructors." ${LLVM_DISABLE_LLVM_DYLIB_ATEXIT_DEFAULT}) if(LLVM_DISABLE_LLVM_DYLIB_ATEXIT) set(DISABLE_LLVM_DYLIB_ATEXIT 1) endif() diff --git a/cmake/modules/AddLLVM.cmake b/cmake/modules/AddLLVM.cmake index 30351ee92ad..f53b89d32ab 100644 --- a/cmake/modules/AddLLVM.cmake +++ b/cmake/modules/AddLLVM.cmake @@ -41,7 +41,7 @@ function(llvm_update_compile_flags name) # Assume that; # - LLVM_COMPILE_FLAGS is list. # - PROPERTY COMPILE_FLAGS is string. - string(REPLACE ";" " " target_compile_flags "${LLVM_COMPILE_FLAGS}") + string(REPLACE ";" " " target_compile_flags " ${LLVM_COMPILE_FLAGS}") if(update_src_props) foreach(fn ${sources}) @@ -303,6 +303,9 @@ endfunction(set_windows_version_resource_properties) # MODULE # Target ${name} might not be created on unsupported platforms. # Check with "if(TARGET ${name})". +# DISABLE_LLVM_LINK_LLVM_DYLIB +# Do not link this library to libLLVM, even if +# LLVM_LINK_LLVM_DYLIB is enabled. # OUTPUT_NAME name # Corresponds to OUTPUT_NAME in target properties. # DEPENDS targets... @@ -316,7 +319,7 @@ endfunction(set_windows_version_resource_properties) # ) function(llvm_add_library name) cmake_parse_arguments(ARG - "MODULE;SHARED;STATIC" + "MODULE;SHARED;STATIC;DISABLE_LLVM_LINK_LLVM_DYLIB" "OUTPUT_NAME" "ADDITIONAL_HEADERS;DEPENDS;LINK_COMPONENTS;LINK_LIBS;OBJLIBS" ${ARGN}) @@ -444,10 +447,14 @@ function(llvm_add_library name) # property has been set to an empty value. get_property(lib_deps GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_${name}) - llvm_map_components_to_libnames(llvm_libs - ${ARG_LINK_COMPONENTS} - ${LLVM_LINK_COMPONENTS} - ) + if (LLVM_LINK_LLVM_DYLIB AND NOT ARG_STATIC AND NOT ARG_DISABLE_LLVM_LINK_LLVM_DYLIB) + set(llvm_libs LLVM) + else() + llvm_map_components_to_libnames(llvm_libs + ${ARG_LINK_COMPONENTS} + ${LLVM_LINK_COMPONENTS} + ) + endif() if(CMAKE_VERSION VERSION_LESS 2.8.12) # Link libs w/o keywords, assuming PUBLIC. @@ -562,7 +569,8 @@ endmacro(add_llvm_loadable_module name) macro(add_llvm_executable name) - llvm_process_sources( ALL_FILES ${ARGN} ) + cmake_parse_arguments(ARG "DISABLE_LLVM_LINK_LLVM_DYLIB" "" "" ${ARGN}) + llvm_process_sources( ALL_FILES ${ARG_UNPARSED_ARGUMENTS} ) # Generate objlib if(LLVM_ENABLE_OBJLIB) @@ -604,7 +612,11 @@ macro(add_llvm_executable name) set(EXCLUDE_FROM_ALL OFF) set_output_directory(${name} ${LLVM_RUNTIME_OUTPUT_INTDIR} ${LLVM_LIBRARY_OUTPUT_INTDIR}) - llvm_config( ${name} ${LLVM_LINK_COMPONENTS} ) + if (LLVM_LINK_LLVM_DYLIB AND NOT ARG_DISABLE_LLVM_LINK_LLVM_DYLIB) + target_link_libraries(${name} LLVM) + else() + llvm_config( ${name} ${LLVM_LINK_COMPONENTS} ) + endif() if( LLVM_COMMON_DEPENDS ) add_dependencies( ${name} ${LLVM_COMMON_DEPENDS} ) endif( LLVM_COMMON_DEPENDS ) @@ -836,8 +848,13 @@ function(llvm_add_go_executable binary pkgpath) set(cppflags "${cppflags} -I${d}") endforeach(d) set(ldflags "${CMAKE_EXE_LINKER_FLAGS}") + if (LLVM_LINK_LLVM_DYLIB) + set(linkmode "dylib") + else() + set(linkmode "component-libs") + endif() add_custom_command(OUTPUT ${binpath} - COMMAND ${CMAKE_BINARY_DIR}/bin/llvm-go "go=${GO_EXECUTABLE}" "cc=${cc}" "cxx=${cxx}" "cppflags=${cppflags}" "ldflags=${ldflags}" + COMMAND ${CMAKE_BINARY_DIR}/bin/llvm-go "go=${GO_EXECUTABLE}" "cc=${cc}" "cxx=${cxx}" "cppflags=${cppflags}" "ldflags=${ldflags}" "linkmode=${linkmode}" ${ARG_GOFLAGS} build -o ${binpath} ${pkgpath} DEPENDS llvm-config ${CMAKE_BINARY_DIR}/bin/llvm-go${CMAKE_EXECUTABLE_SUFFIX} ${llvmlibs} ${ARG_DEPENDS} diff --git a/cmake/modules/TableGen.cmake b/cmake/modules/TableGen.cmake index fcb445afc58..f1ddcd49a58 100644 --- a/cmake/modules/TableGen.cmake +++ b/cmake/modules/TableGen.cmake @@ -77,7 +77,13 @@ macro(add_tablegen target project) # FIXME: It leaks to user, callee of add_tablegen. set(LLVM_ENABLE_OBJLIB ON) - add_llvm_utility(${target} ${ARGN}) + add_llvm_utility( + ${target} ${ARGN} + # libLLVM does not include the TableGen + # components, so we cannot link any tblgen + # utilities against it. + DISABLE_LLVM_LINK_LLVM_DYLIB) + set(LLVM_LINK_COMPONENTS ${${target}_OLD_LLVM_LINK_COMPONENTS}) set(${project}_TABLEGEN "${target}" CACHE diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go index c5c3fd244ca..ed79ca67b89 100644 --- a/tools/llvm-go/llvm-go.go +++ b/tools/llvm-go/llvm-go.go @@ -24,6 +24,11 @@ import ( "strings" ) +const ( + linkmodeComponentLibs = "component-libs" + linkmodeDylib = "dylib" +) + type pkg struct { llvmpath, pkgpath string } @@ -66,11 +71,12 @@ var components = []string{ func llvmConfig(args ...string) string { configpath := os.Getenv("LLVM_CONFIG") if configpath == "" { - // strip llvm-go, add llvm-config - configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config" + bin, _ := filepath.Split(os.Args[0]) + configpath = filepath.Join(bin, "llvm-config") } cmd := exec.Command(configpath, args...) + cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { panic(err.Error()) @@ -78,11 +84,21 @@ func llvmConfig(args ...string) string { outstr := string(out) outstr = strings.TrimSuffix(outstr, "\n") - return strings.Replace(outstr, "\n", " ", -1) + outstr = strings.Replace(outstr, "\n", " ", -1) + return outstr } -func llvmFlags() compilerFlags { - ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...) +func llvmFlags(linkmode string) compilerFlags { + ldflags := llvmConfig("--ldflags") + switch linkmode { + case linkmodeComponentLibs: + ldflags += " " + llvmConfig(append([]string{"--libs"}, components...)...) + case linkmodeDylib: + ldflags += " -lLLVM" + default: + panic("invalid linkmode: " + linkmode) + } + ldflags += " " + llvmConfig("--system-libs") if runtime.GOOS != "darwin" { // OS X doesn't like -rpath with cgo. See: // https://code.google.com/p/go/issues/detail?id=7293 @@ -117,8 +133,8 @@ func printComponents() { fmt.Println(strings.Join(components, " ")) } -func printConfig() { - flags := llvmFlags() +func printConfig(linkmode string) { + flags := llvmFlags(linkmode) fmt.Printf(`// +build !byollvm @@ -137,7 +153,7 @@ type (run_build_sh int) `, flags.cpp, flags.cxx, flags.ld) } -func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) { +func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode string) { args = addTag(args, "byollvm") srcdir := llvmConfig("--src-root") @@ -166,7 +182,7 @@ func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, l newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) - flags := llvmFlags() + flags := llvmFlags(linkmode) newenv := []string{ "CC=" + cc, @@ -178,7 +194,7 @@ func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, l "PATH=" + newpath, } if llgo != "" { - newenv = append(newenv, "GCCGO=" + llgo) + newenv = append(newenv, "GCCGO="+llgo) } for _, v := range os.Environ() { @@ -234,45 +250,44 @@ func main() { ldflags := os.Getenv("CGO_LDFLAGS") gocmd := "go" llgo := "" + linkmode := linkmodeComponentLibs + + flags := []struct { + name string + dest *string + }{ + {"cc", &cc}, + {"cxx", &cxx}, + {"go", &gocmd}, + {"llgo", &llgo}, + {"cppflags", &cppflags}, + {"ldflags", &ldflags}, + {"linkmode", &linkmode}, + } args := os.Args[1:] - DONE: for { - switch { - case len(args) == 0: +LOOP: + for { + if len(args) == 0 { usage() - case strings.HasPrefix(args[0], "cc="): - cc = args[0][3:] - args = args[1:] - case strings.HasPrefix(args[0], "cxx="): - cxx = args[0][4:] - args = args[1:] - case strings.HasPrefix(args[0], "go="): - gocmd = args[0][3:] - args = args[1:] - case strings.HasPrefix(args[0], "llgo="): - llgo = args[0][5:] - args = args[1:] - case strings.HasPrefix(args[0], "cppflags="): - cppflags = args[0][9:] - args = args[1:] - case strings.HasPrefix(args[0], "cxxflags="): - cxxflags = args[0][9:] - args = args[1:] - case strings.HasPrefix(args[0], "ldflags="): - ldflags = args[0][8:] - args = args[1:] - default: - break DONE } + for _, flag := range flags { + if strings.HasPrefix(args[0], flag.name+"=") { + *flag.dest = args[0][len(flag.name)+1:] + args = args[1:] + continue LOOP + } + } + break } switch args[0] { case "build", "get", "install", "run", "test": - runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags) + runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode) case "print-components": printComponents() case "print-config": - printConfig() + printConfig(linkmode) default: usage() } diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index 9be8b436fe5..7415722d3f6 100644 --- a/tools/llvm-shlib/CMakeLists.txt +++ b/tools/llvm-shlib/CMakeLists.txt @@ -2,11 +2,23 @@ # library is enabled by setting LLVM_BUILD_LLVM_DYLIB=yes on the CMake # commandline. By default the shared library only exports the LLVM C API. +if(LLVM_LINK_LLVM_DYLIB) + if(DEFINED LLVM_DYLIB_COMPONENTS) + # To avoid inscrutable link errors, just disallow setting + # LLVM_DYLIB_COMPONENTS when we're intending to link tools + # and shared libraries with the dylib. + message(FATAL_ERROR "LLVM_DYLIB_COMPONENTS must not be set when LLVM_LINK_LLVM_DYLIB is ON") + endif() + if(NOT LLVM_DYLIB_EXPORT_ALL) + message(FATAL_ERROR "LLVM_DYLIB_EXPORT_ALL must be ON when LLVM_LINK_LLVM_DYLIB is ON") + endif() + set(LLVM_DYLIB_COMPONENTS all) +endif() -# You can configure which libraries from LLVM you want to include in the shared -# library by setting LLVM_DYLIB_COMPONENTS to a semi-colon delimited list of -# LLVM components. All compoenent names handled by llvm-config are valid. - +# If LLVM_LINK_LLVM_DYLIB is not OFF, you can configure which libraries from +# LLVM you want to include in the shared library by setting +# LLVM_DYLIB_COMPONENTS to a semi-colon delimited list of LLVM components. +# All component names handled by llvm-config are valid. if(NOT DEFINED LLVM_DYLIB_COMPONENTS) set(LLVM_DYLIB_COMPONENTS ${LLVM_TARGETS_TO_BUILD} @@ -45,6 +57,25 @@ set(SOURCES llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) +if(LLVM_LINK_LLVM_DYLIB) + # libLLVM.so should not have any dependencies on any other LLVM + # shared libraries. When using the "all" pseudo-component, + # LLVM_AVAILABLE_LIBS is added to the dependencies, which may + # contain shared libraries (e.g. libLTO). + # + # Also exclude libLLVMTableGen for the following reasons: + # - it is only used by internal *-tblgen utilities; + # - it pollutes the global options space. + foreach(lib ${LIB_NAMES}) + get_target_property(t ${lib} TYPE) + if("${lib}" STREQUAL "LLVMTableGen") + elseif("x${t}" STREQUAL "xSTATIC_LIBRARY") + list(APPEND FILTERED_LIB_NAMES ${lib}) + endif() + endforeach() + set(LIB_NAMES ${FILTERED_LIB_NAMES}) +endif() + if(NOT DEFINED LLVM_DYLIB_EXPORTED_SYMBOL_FILE) if( WIN32 AND NOT CYGWIN ) @@ -94,7 +125,7 @@ else() add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) endif() -add_llvm_library(LLVM SHARED ${SOURCES}) +add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB ${SOURCES}) list(REMOVE_DUPLICATES LIB_NAMES) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # FIXME: It should be "GNU ld for elf" -- 2.34.1