Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -550,9 +550,13 @@ "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) + option(LLVM_BUILD_LLVM_C_DYLIB "Build LLVM-C.dll (Windows only)" OFF) +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_LINK_LLVM_DYLIB OR (LLVM_BUILD_LLVM_C_DYLIB AND NOT MSVC)) 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,7 @@ set(LLVM_TOOL_POLLY_BUILD Off) endif() -if(NOT LLVM_BUILD_LLVM_DYLIB ) +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,77 +6,87 @@ 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 "OpenBSD") - OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") - 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 "OpenBSD") + OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") + 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 (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}) @@ -104,3 +114,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 whether symbols are prefixed with underscores or not. + if(LLVM_HOST_TRIPLE STREQUAL "i686-pc-win32") + set(GEN_UNDERSCORE "--underscore") + else() + set(GEN_UNDERSCORE "") + 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} ${GEN_UNDERSCORE} --nm ${LLVM_TOOLS_BINARY_DIR}/llvm-nm -o ${LLVM_EXPORTED_SYMBOL_FILE} + DEPENDS ${LIB_NAMES} llvm-nm + 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,106 @@ +#===- 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. This only exports symbols +# that start with LLVM, so it only exports the LLVM C API. +# +# To have CMake run this, set LLVM_BUILD_LLVM_C_DYLIB to on while +# building on Windows. +# +# To run manually, build LLVM with Visual Studio, use a Command prompt +# to navigate to the directory with the .lib files (Debug\lib etc). Then run +# python C:\Path\To\gen-msvc-exports.py --nm ..\bin\llvm-nm.exe LLVM*.lib +# +# If you're generating a 32 bit DLL, use the `--underscore` flag. +# If you want to use a different `llvm-nm` executable, pass the path +# with the `--nm` flag. +# +# You can use the --output flag to set the name of the export file. +# +#===------------------------------------------------------------------------===# +from tempfile import mkstemp +from contextlib import contextmanager +from subprocess import check_call +import argparse +import os +import re + + +_UNDERSCORE_REGEX = { + False: re.compile(r"^\w+\s+T\s+(LLVM.*)$"), + True: re.compile(r"^\w+\s+T\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_c_export(output, underscore, libs, nm): + """Generate the export file for the LLVM-C DLL. + + Run `nm` for each lib in `libs`, and output an export file + to `output`. If `underscore` is true, symbols will + be assumed to be prefixed with an underscore. + """ + with removing(touch_tempfile(prefix='dumpout', suffix='.txt')) as dumpout: + + # Get the right regex. + p = _UNDERSCORE_REGEX[underscore] + + 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([nm, '-g', 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('-u', '--underscore', + help='labels are prefixed with an underscore (use for 32 bit DLLs)', + action='store_true' + ) + parser.add_argument( + '--nm', help='path to the llvm-nm executable', default='llvm-nm' + ) + parser.add_argument( + 'libs', metavar='LIBS', nargs='+', help='list of libraries to generate export from' + ) + + ns = parser.parse_args() + + gen_llvm_c_export(ns.output, ns.underscore, ns.libs, ns.nm) + + +if __name__ == '__main__': + main()