diff --git a/clang/cmake/modules/AddClang.cmake b/clang/cmake/modules/AddClang.cmake --- a/clang/cmake/modules/AddClang.cmake +++ b/clang/cmake/modules/AddClang.cmake @@ -183,5 +183,8 @@ else() target_link_libraries(${target} ${type} ${ARGN}) endif() + if (TARGET obj.${target}) + target_link_libraries(obj.${target} ${ARGN}) + endif() endfunction() diff --git a/clang/tools/driver/CMakeLists.txt b/clang/tools/driver/CMakeLists.txt --- a/clang/tools/driver/CMakeLists.txt +++ b/clang/tools/driver/CMakeLists.txt @@ -33,6 +33,7 @@ DEPENDS intrinsics_gen ${support_plugins} + GENERATE_DRIVER ) clang_target_link_libraries(clang diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -327,7 +327,7 @@ return 1; } -int main(int Argc, const char **Argv) { +int clang_main(int Argc, char **Argv) { noteBottomOfStack(); llvm::InitLLVM X(Argc, Argv); llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL diff --git a/llvm/cmake/driver-template.cpp.in b/llvm/cmake/driver-template.cpp.in new file mode 100644 --- /dev/null +++ b/llvm/cmake/driver-template.cpp.in @@ -0,0 +1,11 @@ +//===-- driver-template.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +int @TOOL_NAME@_main(int argc, char **argv); + +int main(int argc, char **argv) { return @TOOL_NAME@_main(argc, argv); } diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake --- a/llvm/cmake/modules/AddLLVM.cmake +++ b/llvm/cmake/modules/AddLLVM.cmake @@ -851,7 +851,7 @@ macro(add_llvm_executable name) cmake_parse_arguments(ARG - "DISABLE_LLVM_LINK_LLVM_DYLIB;IGNORE_EXTERNALIZE_DEBUGINFO;NO_INSTALL_RPATH;SUPPORT_PLUGINS" + "DISABLE_LLVM_LINK_LLVM_DYLIB;IGNORE_EXTERNALIZE_DEBUGINFO;NO_INSTALL_RPATH;SUPPORT_PLUGINS;GENERATE_DRIVER" "ENTITLEMENTS;BUNDLE_PATH" "DEPENDS" ${ARGN}) @@ -861,7 +861,7 @@ list(APPEND LLVM_COMMON_DEPENDS ${ARG_DEPENDS}) # Generate objlib - if(LLVM_ENABLE_OBJLIB) + if(LLVM_ENABLE_OBJLIB OR ARG_GENERATE_DRIVER) # Generate an obj library for both targets. set(obj_name "obj.${name}") add_library(${obj_name} OBJECT EXCLUDE_FROM_ALL @@ -873,6 +873,23 @@ set_target_properties(${obj_name} PROPERTIES FOLDER "Object Libraries") endif() + if (ARG_GENERATE_DRIVER) + string(REPLACE "-" "_" TOOL_NAME ${name}) + configure_file( + ${LLVM_MAIN_SRC_DIR}/cmake/driver-template.cpp.in + ${CMAKE_CURRENT_BINARY_DIR}/${name}-driver.cpp) + + list(APPEND ALL_FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}-driver.cpp) + + set_property(GLOBAL APPEND PROPERTY LLVM_DRIVER_COMPONENTS ${LLVM_LINK_COMPONENTS}) + set_property(GLOBAL APPEND PROPERTY LLVM_DRIVER_DEPS ${ARG_DEPENDS} ${LLVM_COMMON_DEPENDS}) + set_property(GLOBAL APPEND PROPERTY LLVM_DRIVER_OBJLIBS "${obj_name}") + + set_property(GLOBAL APPEND PROPERTY LLVM_DRIVER_TOOLS ${name}) + target_link_libraries(${obj_name} PUBLIC ${LLVM_PTHREAD_LIB}) + llvm_config(${obj_name} ${USE_SHARED} ${LLVM_LINK_COMPONENTS} ) + endif() + add_windows_version_resource_file(ALL_FILES ${ALL_FILES}) if(XCODE) @@ -1436,6 +1453,12 @@ foreach(dir ${sub-dirs}) if(IS_DIRECTORY "${dir}" AND EXISTS "${dir}/CMakeLists.txt") canonicalize_tool_name(${dir} name) + # I don't like special casing things by order, but the llvm-driver ends up + # linking the object libraries from all the tools that opt-in, so adding + # it separately at the end is probably the simplest case. + if("${name}" STREQUAL "LLVM_DRIVER") + continue() + endif() if (${project}_TOOL_${name}_BUILD) get_filename_component(fn "${dir}" NAME) list(APPEND list_of_implicit_subdirs "${fn}") diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -18,6 +18,7 @@ LLVM_HAVE_TF_AOT LLVM_HAVE_TF_API LLVM_ENABLE_EXPENSIVE_CHECKS + LLVM_TOOL_LLVM_DRIVER_BUILD ) configure_lit_site_cfg( @@ -137,6 +138,10 @@ set(LLVM_TEST_DEPENDS ${LLVM_TEST_DEPENDS} llvm-lto) endif() +if(TARGET llvm-driver) + set(LLVM_TEST_DEPENDS ${LLVM_TEST_DEPENDS} llvm-driver) +endif() + # If Intel JIT events are supported, depend on a tool that tests the listener. if( LLVM_USE_INTEL_JITEVENTS ) set(LLVM_TEST_DEPENDS ${LLVM_TEST_DEPENDS} llvm-jitlistener) diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -139,6 +139,7 @@ config.llvm_locstats_used = os.path.exists(llvm_locstats_tool) tools = [ + ToolSubst('%llvm', FindTool('llvm')), ToolSubst('%lli', FindTool('lli'), post='.', extra_args=lli_args), ToolSubst('%llc_dwarf', FindTool('llc'), extra_args=llc_args), ToolSubst('%go', config.go_executable, unresolved='ignore'), @@ -296,6 +297,9 @@ if config.target_triple: config.available_features.add('default_triple') +if config.have_llvm_driver: + config.available_features.add('llvm-driver') + import subprocess diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in --- a/llvm/test/lit.site.cfg.py.in +++ b/llvm/test/lit.site.cfg.py.in @@ -55,6 +55,7 @@ config.have_tf_aot = @LLVM_HAVE_TF_AOT@ config.have_tf_api = @LLVM_HAVE_TF_API@ config.expensive_checks = @LLVM_ENABLE_EXPENSIVE_CHECKS@ +config.have_llvm_driver = @LLVM_TOOL_LLVM_DRIVER_BUILD@ # Support substitution of the tools_dir with user parameters. This is # used when we can't determine the tool dir at configuration time. diff --git a/llvm/test/tools/llvm-driver/help-passthrough.test b/llvm/test/tools/llvm-driver/help-passthrough.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-driver/help-passthrough.test @@ -0,0 +1,3 @@ +# REQUIRES: llvm-driver +# RUN: %llvm llvm-cxxfilt --help +# CHECK: USAGE: llvm-cxxfilt diff --git a/llvm/test/tools/llvm-driver/help.test b/llvm/test/tools/llvm-driver/help.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-driver/help.test @@ -0,0 +1,3 @@ +# REQUIRES: llvm-driver +# RUN: %llvm --help +# CHECK: USAGE: llvm [subcommand] diff --git a/llvm/test/tools/llvm-driver/symlink-call.test b/llvm/test/tools/llvm-driver/symlink-call.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-driver/symlink-call.test @@ -0,0 +1,10 @@ +## Don't make symlinks on Windows. +# UNSUPPORTED: system-windows +# REQUIRES: llvm-driver + +# RUN: rm -rf %t +# RUN: mkdir %t +# RUN: ln -s %llvm %t/llvm-cxxfilt +# RUN: %t/llvm-cxxfilt --help + +# CHECK: USAGE: llvm-cxxfilt diff --git a/llvm/tools/CMakeLists.txt b/llvm/tools/CMakeLists.txt --- a/llvm/tools/CMakeLists.txt +++ b/llvm/tools/CMakeLists.txt @@ -52,3 +52,7 @@ endforeach(p) set(LLVM_COMMON_DEPENDS ${LLVM_COMMON_DEPENDS} PARENT_SCOPE) + +# This is explicitly added at the end _after_ all tool projects so that it can +# scrape up tools from other projects into itself. +add_subdirectory(llvm-driver) diff --git a/llvm/tools/dsymutil/CMakeLists.txt b/llvm/tools/dsymutil/CMakeLists.txt --- a/llvm/tools/dsymutil/CMakeLists.txt +++ b/llvm/tools/dsymutil/CMakeLists.txt @@ -32,6 +32,7 @@ DEPENDS intrinsics_gen ${tablegen_deps} + GENERATE_DRIVER ) if(APPLE) diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -478,7 +478,7 @@ return OutputLocation(std::string(Path.str()), ResourceDir); } -int main(int argc, char **argv) { +int dsymutil_main(int argc, char **argv) { InitLLVM X(argc, argv); // Parse arguments. diff --git a/llvm/tools/llvm-ar/CMakeLists.txt b/llvm/tools/llvm-ar/CMakeLists.txt --- a/llvm/tools/llvm-ar/CMakeLists.txt +++ b/llvm/tools/llvm-ar/CMakeLists.txt @@ -15,6 +15,7 @@ DEPENDS intrinsics_gen + GENERATE_DRIVER ) add_llvm_tool_symlink(llvm-ranlib llvm-ar) diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -1261,7 +1261,7 @@ return performOperation(CreateSymTab, nullptr); } -int main(int argc, char **argv) { +int llvm_ar_main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; diff --git a/llvm/tools/llvm-cxxfilt/CMakeLists.txt b/llvm/tools/llvm-cxxfilt/CMakeLists.txt --- a/llvm/tools/llvm-cxxfilt/CMakeLists.txt +++ b/llvm/tools/llvm-cxxfilt/CMakeLists.txt @@ -13,6 +13,7 @@ DEPENDS CxxfiltOptsTableGen + GENERATE_DRIVER ) if(LLVM_INSTALL_BINUTILS_SYMLINKS) diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp --- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -147,7 +147,7 @@ OS.flush(); } -int main(int argc, char **argv) { +int llvm_cxxfilt_main(int argc, char **argv) { InitLLVM X(argc, argv); BumpPtrAllocator A; StringSaver Saver(A); diff --git a/llvm/tools/llvm-driver/CMakeLists.txt b/llvm/tools/llvm-driver/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-driver/CMakeLists.txt @@ -0,0 +1,28 @@ +get_property(LLVM_COMMON_DEPENDS GLOBAL PROPERTY LLVM_DRIVER_DEPS) +get_property(LLVM_DRIVER_OBJLIBS GLOBAL PROPERTY LLVM_DRIVER_OBJLIBS) + +get_property(LLVM_DRIVER_TOOLS GLOBAL PROPERTY LLVM_DRIVER_TOOLS) + +foreach(tool ${LLVM_DRIVER_TOOLS}) + string(REPLACE "-" "_" tool_entry ${tool}) + set(def_decl "${def_decl}LLVM_DRIVER_TOOL(\"${tool}\", ${tool_entry})\n") +endforeach() + +file(WRITE + "${CMAKE_CURRENT_BINARY_DIR}/LLVMDriverTools.def" + "${def_decl}#undef LLVM_DRIVER_TOOL\n") + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_llvm_tool(llvm-driver + llvm-driver.cpp + ) + +set_target_properties(llvm-driver PROPERTIES OUTPUT_NAME llvm) + +target_link_libraries(llvm-driver PUBLIC ${LLVM_DRIVER_OBJLIBS}) + +if(APPLE) + # dsymutil uses some CoreFoundation stuff on Darwin... + target_link_libraries(llvm-driver PRIVATE "-framework CoreFoundation") +endif(APPLE) diff --git a/llvm/tools/llvm-driver/llvm-driver.cpp b/llvm/tools/llvm-driver/llvm-driver.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-driver/llvm-driver.cpp @@ -0,0 +1,61 @@ +//===-- llvm-driver.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; + +#define LLVM_DRIVER_TOOL(tool, entry) int entry##_main(int argc, char **argv); +#include "LLVMDriverTools.def" + +// This function handles the case of not recognizing the tool requested, or if +// --help or --version are passed directly to the llvm driver. +int UnknownMain(int Argc, char **Argv) { + cl::OptionCategory LLVMDriverCategory("llvm options"); +#define LLVM_DRIVER_TOOL(tool, entry) \ + cl::SubCommand entry##Subcommand(tool, tool); +#include "LLVMDriverTools.def" + + cl::HideUnrelatedOptions(LLVMDriverCategory); + cl::ParseCommandLineOptions(Argc, Argv, "llvm compiler driver\n"); + llvm_unreachable("We should never get here, parsing should always exit."); + return 1; +} + +int main(int Argc, char **Argv) { + llvm::StringRef LaunchedTool = sys::path::stem(Argv[0]); + // If the driver is launched directly. + int PassThroughArgC = Argc; + char **PassThroughArgV = Argv; + bool ConsumeFirstArg = false; + if (LaunchedTool == "llvm") { + LaunchedTool = Argv[1]; + ConsumeFirstArg = true; + } + + // if it is launched through a symlink that is the tool name. + typedef int (*MainFunction)(int, char **); + MainFunction Func = StringSwitch(LaunchedTool) + +#define LLVM_DRIVER_TOOL(tool, entry) .Case(tool, entry##_main) +#include "LLVMDriverTools.def" + .Default(UnknownMain); + // If the main function is unknown we don't consume any args, so that we can + // print the appropriate help spew. + if (Func != UnknownMain && ConsumeFirstArg) { + --PassThroughArgC; + ++PassThroughArgV; + } + + return Func(PassThroughArgC, PassThroughArgV); +} diff --git a/llvm/tools/llvm-objcopy/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt --- a/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/llvm/tools/llvm-objcopy/CMakeLists.txt @@ -43,6 +43,7 @@ ObjcopyOptsTableGen InstallNameToolOptsTableGen StripOptsTableGen + GENERATE_DRIVER ) add_llvm_tool_symlink(llvm-install-name-tool llvm-objcopy) diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -401,7 +401,7 @@ return Error::success(); } -int main(int argc, char **argv) { +int llvm_objcopy_main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0];