Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -528,9 +528,14 @@ "Semicolon-separated list of components to include in libLLVM, or \"all\".") endif() option(LLVM_LINK_LLVM_DYLIB "Link tools against the libllvm dynamic library" OFF) -option(LLVM_BUILD_LLVM_C_DYLIB "Build libllvm-c re-export library (Darwin Only)" OFF) +if(MSVC) + # Defaults to on. + option(LLVM_BUILD_LLVM_C_DYLIB "Build LLVM-C.dll (Windows only)" ON) +else() + option(LLVM_BUILD_LLVM_C_DYLIB "Build libllvm-c re-export library (Darwin only)" OFF) +endif() set(LLVM_BUILD_LLVM_DYLIB_default OFF) -if(LLVM_LINK_LLVM_DYLIB OR LLVM_BUILD_LLVM_C_DYLIB) +if(LLVM_BUILD_LLVM_C_DYLIB AND NOT MSVC OR LLVM_LINK_LLVM_DYLIB) set(LLVM_BUILD_LLVM_DYLIB_default ON) endif() option(LLVM_BUILD_LLVM_DYLIB "Build libllvm dynamic library" ${LLVM_BUILD_LLVM_DYLIB_default}) Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -17,7 +17,9 @@ set(LLVM_TOOL_POLLY_BUILD Off) endif() -if(NOT LLVM_BUILD_LLVM_DYLIB ) +# Don't build the shlib if not requested, +# always build on windows. +if(NOT LLVM_BUILD_LLVM_DYLIB AND NOT LLVM_BUILD_LLVM_C_DYLIB) set(LLVM_TOOL_LLVM_SHLIB_BUILD Off) endif() Index: tools/llvm-shlib/CMakeLists.txt =================================================================== --- tools/llvm-shlib/CMakeLists.txt +++ tools/llvm-shlib/CMakeLists.txt @@ -6,79 +6,89 @@ libllvm.cpp ) -llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) - if(LLVM_LINK_LLVM_DYLIB AND LLVM_DYLIB_EXPORTED_SYMBOL_FILE) message(WARNING "Using LLVM_LINK_LLVM_DYLIB with LLVM_DYLIB_EXPORTED_SYMBOL_FILE may not work. Use at your own risk.") endif() -# 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}) +if(LLVM_BUILD_LLVM_DYLIB) + if(MSVC) + message(FATAL_ERROR "Generating libLLVM is not supported on MSVC") endif() -endforeach() -set(LIB_NAMES ${FILTERED_LIB_NAMES}) -if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) - set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE}) - add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) -endif() + llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) + + # 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}) + + if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) + set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE}) + add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) + endif() -add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES}) - -list(REMOVE_DUPLICATES LIB_NAMES) -if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) - OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") - OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly") - OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME: It should be "GNU ld for elf" - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in - ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map) - - # GNU ld doesn't resolve symbols in the version script. - set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) - if (NOT LLVM_LINKER_IS_SOLARISLD) - # Solaris ld does not accept global: *; so there is no way to version *all* global symbols - set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES}) + add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES}) + + list(REMOVE_DUPLICATES LIB_NAMES) + if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) + OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") + OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly") + OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME: It should be "GNU ld for elf" + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in + ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map) + + # GNU ld doesn't resolve symbols in the version script. + set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) + if (NOT LLVM_LINKER_IS_SOLARISLD) + # Solaris ld does not accept global: *; so there is no way to version *all* global symbols + set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES}) + endif() + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) endif() -elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) -endif() -target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) + target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) -if (LLVM_DYLIB_SYMBOL_VERSIONING) - set_property(TARGET LLVM APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--default-symver") -endif() + if (LLVM_DYLIB_SYMBOL_VERSIONING) + set_property(TARGET LLVM APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--default-symver") + endif() -if (APPLE) - set_property(TARGET LLVM APPEND_STRING PROPERTY - LINK_FLAGS - " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") -endif() + if (APPLE) + set_property(TARGET LLVM APPEND_STRING PROPERTY + LINK_FLAGS + " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + endif() -if(TARGET libLLVMExports) - add_dependencies(LLVM libLLVMExports) + if(TARGET libLLVMExports) + add_dependencies(LLVM libLLVMExports) + endif() endif() -if(LLVM_BUILD_LLVM_C_DYLIB) - # To get the export list for a single llvm library: - # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports - +if(LLVM_BUILD_LLVM_C_DYLIB AND NOT MSVC) if(NOT APPLE) message(FATAL_ERROR "Generating libLLVM-c is only supported on Darwin") endif() + if(NOT LLVM_BUILD_LLVM_DYLIB) + message(FATAL_ERROR "Generating libLLVM-c requires LLVM_BUILD_LLVM_C_DYLIB on Darwin") + endif() + + # To get the export list for a single llvm library: + # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm-c.exports) set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) @@ -106,3 +116,40 @@ " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH} -Wl,-reexport_library ${LIB_PATH}") endif() +if(MSVC) + # Build the LLVM-C.dll library that exports the C API. + + set(LLVM_LINK_COMPONENTS + ${LLVM_DYLIB_COMPONENTS} + ) + + llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) + list(REMOVE_DUPLICATES LIB_NAMES) + + # The python script needs to know which arch we are using. + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(GEN_ARCH x64) + else() + set(GEN_ARCH x86) + endif() + + # Get the full name to the libs so the python script understands them. + foreach(lib ${LIB_NAMES}) + list(APPEND FULL_LIB_NAMES ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib/${lib}.lib) + endforeach() + + # Generate the exports file dynamically. + set(GEN_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/gen-msvc-exports.py) + + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllvm-c.exports) + + add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GEN_SCRIPT} ${FULL_LIB_NAMES} --arch ${GEN_ARCH} -o ${LLVM_EXPORTED_SYMBOL_FILE} + DEPENDS ${LIB_NAMES} + COMMENT "Generating export list for LLVM-C" + VERBATIM ) + + # Finally link the target. + add_llvm_library(LLVM-C SHARED ${SOURCES} DEPENDS intrinsics_gen) + +endif() Index: tools/llvm-shlib/gen-msvc-exports.py =================================================================== --- /dev/null +++ tools/llvm-shlib/gen-msvc-exports.py @@ -0,0 +1,93 @@ +#===- gen-msvc-exports.py - Generate C API export file -------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# +# Generate an export file from a list of given LIB files. Only exports symbols +# that starts with LLVM*, so is only usefull for export the LLVM C API. +# +# To use, build LLVM with Visual Studio, use the Visual Studio Command prompt +# to navigate to the directory with the .lib files (Debug\lib etc). Then run +# python C:\Path\To\gen-msvc-exports.py LLVM*.lib +# +# If you're generating a 64 bit DLL, use the `--arch=x64` flag. +# +# You can use the --output flag to set the name of the export file. +# +# Generate the 64 bit DLL from the 64 bit prompt, 32 bit from the 32 bit prompt. +# +#===------------------------------------------------------------------------===# +from tempfile import mkstemp +from contextlib import contextmanager +from subprocess import check_call +import argparse +import os +import re + + +_ARCH_REGEX = { + 'x64': re.compile(r"^\s+\w+\s+(LLVM.*)$"), + 'x86': re.compile(r"^\s+\w+\s+_(LLVM.*)$") +} + + +@contextmanager +def removing(path): + try: + yield path + finally: + os.unlink(path) + + +def touch_tempfile(*args, **kwargs): + fd, name = mkstemp(*args, **kwargs) + os.close(fd) + return name + + +def gen_llvm_dll(output, arch, libs): + with removing(touch_tempfile(prefix='dumpout', suffix='.txt')) as dumpout: + + # Get the right regex for this arch. + p = _ARCH_REGEX[arch] + + with open(output, 'w+t') as output_f: + + # For each lib get the LLVM* functions it exports. + for lib in libs: + # Call dumpbin. + with open(dumpout, 'w+t') as dumpout_f: + check_call(['dumpbin', '/linkermember:1', lib], stdout=dumpout_f) + + # Get the matching lines. + with open(dumpout) as dumpbin: + for line in dumpbin: + m = p.match(line) + if m is not None: + output_f.write(m.group(1) + '\n') + + +def main(): + parser = argparse.ArgumentParser('gen-msvc-exports') + + parser.add_argument( + '-o', '--output', help='output filename', default='LLVM-C.exports' + ) + parser.add_argument( + '--arch', help='architecture', default='x86', choices=['x86', 'x64'], type=str.lower + ) + parser.add_argument( + 'libs', metavar='LIBS', nargs='+', help='list of libraries to generate export from' + ) + + ns = parser.parse_args() + + gen_llvm_dll(ns.output, ns.arch, ns.libs) + + +if __name__ == '__main__': + main()