Use clang+llvm C++ in cmake; parse args with clang driver

This commit is contained in:
Fangrui Song 2018-05-12 13:31:56 -07:00
parent b349983e13
commit c269876593
7 changed files with 97 additions and 250 deletions

View File

@ -86,10 +86,12 @@ if(NOT SYSTEM_CLANG)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL Clang AND CLANG_USE_BUNDLED_LIBC++)
message(STATUS "Using bundled libc++")
target_compile_options(ccls PRIVATE -nostdinc++ -cxx-isystem ${CLANG_ROOT}/include/c++/v1)
target_link_libraries(ccls PRIVATE -stdlib=libc++ -L${CLANG_ROOT}/lib -lc++experimental)
if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
# Don't use -stdlib=libc++ because while ccls is linked with libc++, bundled clang+llvm require libstdc++
target_link_libraries(ccls PRIVATE -L${CLANG_ROOT}/lib c++ c++experimental c++abi)
else()
# FreeBSD uses system libcxxrt.a and does not need libc++abi.
target_link_libraries(ccls PRIVATE c++abi)
target_link_libraries(ccls PRIVATE -stdlib=libc++ -L${CLANG_ROOT}/lib c++experimental)
endif()
endif()
@ -101,7 +103,11 @@ endif()
# See cmake/FindClang.cmake
find_package(Clang ${CLANG_VERSION} REQUIRED)
find_package(Curses REQUIRED)
target_link_libraries(ccls PRIVATE Clang::Clang)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL Windows)
target_link_libraries(ccls PRIVATE Clang::Clang ${CURSES_LIBRARIES})
endif()
# Enable threading support
set(THREADS_PREFER_PTHREAD_FLAG ON)
@ -188,27 +194,6 @@ endif()
file(GLOB SOURCES src/*.cc src/*.h src/serializers/*.cc src/serializers/*.h
src/messages/*.h src/messages/*.cc)
if(Clang_FORMAT AND ${Clang_VERSION} STREQUAL 6.0.0)
add_custom_target(format
COMMAND ${Clang_FORMAT} -i ${SOURCES}
# .clang-format is located in the ccls root project dir
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Running clang-format ...")
else()
# Set error message depending on which condition was false
if (NOT Clang_FORMAT)
set(Clang_FORMAT_ERROR "Error: clang-format executable not found")
elseif(NOT ${Clang_VERSION} STREQUAL 6.0.0)
set(Clang_FORMAT_ERROR "Error: clang-format version does not match \
6.0.0. Due to differences in clang-format output between versions we only \
support clang-format 6.0.0")
endif()
add_custom_target(format
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red --bold
${Clang_FORMAT_ERROR})
endif()
### Sources
target_sources(ccls PRIVATE third_party/siphash.cc)

View File

@ -8,7 +8,6 @@
#
# Clang_FOUND - True if headers and requested libraries were found
# Clang_EXECUTABLE - Clang executable
# Clang_FORMAT - Clang-format executable
# Clang_RESOURCE_DIR - Clang resource directory
# Clang_VERSION - Clang version as reported by Clang executable
#
@ -19,7 +18,6 @@
# This module reads hints about which libraries to look for and where to find
# them from the following variables::
#
# CLANG_CXX - Search for and add Clang C++ libraries
# CLANG_ROOT - If set, only look for Clang components in CLANG_ROOT
#
# Example to link against Clang target::
@ -41,6 +39,11 @@ macro(_Clang_find_library VAR NAME)
endif()
endmacro()
macro(_Clang_find_add_library NAME)
_Clang_find_library(${NAME}_LIBRARY ${NAME})
list(APPEND _Clang_LIBRARIES ${${NAME}_LIBRARY})
endmacro()
macro(_Clang_find_path VAR INCLUDE_FILE)
if (CLANG_ROOT)
find_path(${VAR} ${INCLUDE_FILE}
@ -59,45 +62,22 @@ macro(_Clang_find_program VAR NAME)
endif()
endmacro()
# Macro to avoid duplicating logic for each Clang C++ library
macro(_Clang_find_and_add_cxx_lib NAME INCLUDE_FILE)
# Find library
_Clang_find_library(Clang_${NAME}_LIBRARY ${NAME})
list(APPEND _Clang_REQUIRED_VARS Clang_${NAME}_LIBRARY)
list(APPEND _Clang_CXX_LIBRARIES ${Clang_${NAME}_LIBRARY})
# Find corresponding include directory
_Clang_find_path(Clang_${NAME}_INCLUDE_DIR ${INCLUDE_FILE})
list(APPEND _Clang_REQUIRED_VARS Clang_${NAME}_INCLUDE_DIR)
list(APPEND _Clang_CXX_INCLUDE_DIRS ${Clang_${NAME}_INCLUDE_DIR})
endmacro()
### Start
set(_Clang_REQUIRED_VARS Clang_LIBRARY Clang_INCLUDE_DIR Clang_EXECUTABLE
Clang_RESOURCE_DIR Clang_VERSION)
Clang_RESOURCE_DIR Clang_VERSION
LLVM_INCLUDE_DIR LLVM_BUILD_INCLUDE_DIR)
_Clang_find_library(Clang_LIBRARY clang)
_Clang_find_add_library(clangDriver)
_Clang_find_add_library(LLVMOption)
_Clang_find_add_library(LLVMSupport)
_Clang_find_add_library(LLVMDemangle)
_Clang_find_path(Clang_INCLUDE_DIR clang-c/Index.h)
_Clang_find_path(Clang_BUILD_INCLUDE_DIR clang/Driver/Options.inc)
_Clang_find_path(LLVM_INCLUDE_DIR llvm/PassInfo.h)
_Clang_find_path(LLVM_BUILD_INCLUDE_DIR llvm/Config/llvm-config.h)
if(CLANG_CXX)
# The order is derived by topological sorting LINK_LIBS in
# clang/lib/*/CMakeLists.txt
_Clang_find_and_add_cxx_lib(clangFormat clang/Format/Format.h)
_Clang_find_and_add_cxx_lib(clangToolingCore clang/Tooling/Core/Diagnostic.h)
_Clang_find_and_add_cxx_lib(clangRewrite clang/Rewrite/Core/Rewriter.h)
_Clang_find_and_add_cxx_lib(clangAST clang/AST/AST.h)
_Clang_find_and_add_cxx_lib(clangLex clang/Lex/Lexer.h)
_Clang_find_and_add_cxx_lib(clangBasic clang/Basic/ABI.h)
# The order is derived from llvm-config --libs core
_Clang_find_and_add_cxx_lib(LLVMCore llvm/Pass.h)
_Clang_find_and_add_cxx_lib(LLVMBinaryFormat llvm/BinaryFormat/Dwarf.h)
_Clang_find_and_add_cxx_lib(LLVMSupport llvm/Support/Error.h)
_Clang_find_and_add_cxx_lib(LLVMDemangle llvm/Demangle/Demangle.h)
endif()
_Clang_find_program(Clang_FORMAT clang-format)
_Clang_find_program(Clang_EXECUTABLE clang)
if(Clang_EXECUTABLE)
# Find Clang resource directory with Clang executable
@ -127,12 +107,10 @@ find_package_handle_standard_args(Clang
)
if(Clang_FOUND AND NOT TARGET Clang::Clang)
set(_Clang_LIBRARIES ${Clang_LIBRARY} ${_Clang_CXX_LIBRARIES})
set(_Clang_INCLUDE_DIRS ${Clang_INCLUDE_DIR} ${_Clang_CXX_INCLUDE_DIRS})
add_library(Clang::Clang UNKNOWN IMPORTED)
set_target_properties(Clang::Clang PROPERTIES
IMPORTED_LOCATION ${Clang_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES "${Clang_INCLUDE_DIR};${Clang_BUILD_INCLUDE_DIR};${LLVM_INCLUDE_DIR};${LLVM_BUILD_INCLUDE_DIR}")
add_library(Clang::Clang INTERFACE IMPORTED)
set_property(TARGET Clang::Clang PROPERTY
INTERFACE_LINK_LIBRARIES ${_Clang_LIBRARIES})
set_property(TARGET Clang::Clang PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${_Clang_INCLUDE_DIRS})
set_property(TARGET Clang::Clang PROPERTY INTERFACE_LINK_LIBRARIES ${_Clang_LIBRARIES})
endif()

View File

@ -517,7 +517,8 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
for (int i = 0; i < g_config->index.threads; i++) {
std::thread([=]() {
g_thread_id = i + 1;
SetThreadName("indexer" + std::to_string(i));
std::string name = "indexer" + std::to_string(i);
SetThreadName(name.c_str());
Indexer_Main(diag_engine, vfs, import_pipeline_status, project,
working_files, waiter);
}).detach();

View File

@ -1,10 +1,9 @@
#pragma once
#include <optional>
#include <string_view>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
void PlatformInit();
@ -12,9 +11,7 @@ void PlatformInit();
std::string GetExecutablePath();
std::string NormalizePath(const std::string& path);
void SetThreadName(const std::string& thread_name);
std::optional<int64_t> GetLastModificationTime(const std::string& absolute_path);
void SetThreadName(const char* name);
// Free any unused memory and return it to the system.
void FreeUnusedMemory();

View File

@ -1,9 +1,12 @@
#include <llvm/ADT/Twine.h>
#include <llvm/Support/Threading.h>
#if defined(__unix__) || defined(__APPLE__)
#include "platform.h"
#include "utils.h"
#include "loguru.hpp"
#include <loguru.hpp>
#include <pthread.h>
#if defined(__FreeBSD__)
@ -155,17 +158,6 @@ std::string NormalizePath(const std::string& path) {
return resolved ? *resolved : path;
}
void SetThreadName(const std::string& thread_name) {
loguru::set_thread_name(thread_name.c_str());
#if defined(__APPLE__)
pthread_setname_np(thread_name.c_str());
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), thread_name.c_str());
#elif defined(__linux__)
pthread_setname_np(pthread_self(), thread_name.c_str());
#endif
}
void FreeUnusedMemory() {
#if defined(__GLIBC__)
malloc_trim(0);
@ -225,3 +217,8 @@ std::string GetExternalCommandOutput(const std::vector<std::string>& command,
}
#endif
void SetThreadName(const char* name) {
loguru::set_thread_name(name);
llvm::set_thread_name(name);
}

View File

@ -49,36 +49,6 @@ std::string NormalizePath(const std::string& path) {
return result;
}
// See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO {
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName(const std::string& thread_name) {
loguru::set_thread_name(thread_name.c_str());
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = thread_name.c_str();
info.dwThreadID = (DWORD)-1;
info.dwFlags = 0;
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR),
(ULONG_PTR*)&info);
#ifdef _MSC_VER
} __except (EXCEPTION_EXECUTE_HANDLER) {
#else
} catch (...) {
#endif
}
}
void FreeUnusedMemory() {}
// TODO Wait for debugger to attach

View File

@ -12,6 +12,14 @@
#include "utils.h"
#include "working_files.h"
#include <clang/Driver/Options.h>
#include <llvm/ADT/ArrayRef.h>
#include <llvm/Option/ArgList.h>
#include <llvm/Option/OptTable.h>
using namespace clang;
using namespace llvm;
using namespace llvm::opt;
#include <clang-c/CXCompilationDatabase.h>
#include <doctest/doctest.h>
#include <rapidjson/writer.h>
@ -52,45 +60,13 @@ struct ProjectConfig {
ProjectMode mode = ProjectMode::CompileCommandsJson;
};
// TODO: See
// https://github.com/Valloric/ycmd/blob/master/ycmd/completers/cpp/flags.py.
// Flags '-include' and '-include-pch' are blacklisted here cause libclang returns error in case when
// precompiled header was generated by a different compiler (even two different builds of same version
// of clang for the same platform are incompatible). Note that libclang always generate it's own pch
// internally. For details, see https://github.com/Valloric/ycmd/issues/892 .
std::vector<std::string> kBlacklistMulti = {
"-MF", "-MT", "-MQ", "-o", "--serialize-diagnostics", "-Xclang"};
// Blacklisted flags which are always removed from the command line.
std::vector<std::string> kBlacklist = {
"-c", "-MP", "-MD", "-MMD", "--fcolor-diagnostics", "-showIncludes"
enum OptionClass {
EqOrJoinOrSep,
EqOrSep,
JoinOrSep,
Separate,
};
// Arguments which are followed by a potentially relative path. We need to make
// all relative paths absolute, otherwise libclang will not resolve them.
std::vector<std::string> kPathArgs = {
"-I", "-iquote", "-cxx-isystem", "-isystem", "--sysroot=",
"-isysroot", "-gcc-toolchain", "-include-pch", "-iframework",
"-F", "-imacros", "-include", "/I",
"-idirafter"};
// Arguments which always require an absolute path, ie, clang -working-directory
// does not work as expected. Argument processing assumes that this is a subset
// of kPathArgs.
std::vector<std::string> kNormalizePathArgs = {"--sysroot="};
// Arguments whose path arguments should be injected into include dir lookup
// for #include completion.
std::vector<std::string> kQuoteIncludeArgs = {"-iquote", "-I", "/I"};
std::vector<std::string> kAngleIncludeArgs = {"-cxx-isystem", "-isystem", "-I", "/I"};
bool ShouldAddToQuoteIncludes(const std::string& arg) {
return StartsWithAny(arg, kQuoteIncludeArgs);
}
bool ShouldAddToAngleIncludes(const std::string& arg) {
return StartsWithAny(arg, kAngleIncludeArgs);
}
Project::Entry GetCompilationEntryFromCompileCommandEntry(
ProjectConfig* config,
const CompileCommandsEntry& entry) {
@ -136,109 +112,51 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
// Compiler driver.
result.args.push_back(args[0]);
// Add -working-directory if not provided.
if (!AnyStartsWith(args, "-working-directory"))
result.args.emplace_back("-working-directory=" + entry.directory.string());
std::unique_ptr<OptTable> Opts = driver::createDriverOptTable();
unsigned MissingArgIndex, MissingArgCount;
std::vector<const char*> cargs;
for (auto& arg : args)
cargs.push_back(arg.c_str());
InputArgList Args =
Opts->ParseArgs(makeArrayRef(cargs), MissingArgIndex, MissingArgCount,
driver::options::CC1Option);
bool next_flag_is_path = false;
bool add_next_flag_to_quote_dirs = false;
bool add_next_flag_to_angle_dirs = false;
using namespace clang::driver::options;
for (const auto* A :
Args.filtered(OPT_I, OPT_c_isystem, OPT_cxx_isystem, OPT_isystem))
config->angle_dirs.insert(entry.ResolveIfRelative(A->getValue()));
for (const auto* A : Args.filtered(OPT_I, OPT_iquote))
config->quote_dirs.insert(entry.ResolveIfRelative(A->getValue()));
// Note that when processing paths, some arguments support multiple forms, ie,
// {"-Ifoo"} or {"-I", "foo"}. Support both styles.
size_t i = 1;
result.args.reserve(args.size() + config->extra_flags.size());
for (; i < args.size(); ++i) {
std::string arg = args[i];
// Finish processing path for the previous argument, which was a switch.
// {"-I", "foo"} style.
if (next_flag_is_path) {
std::string normalized_arg = entry.ResolveIfRelative(arg);
if (add_next_flag_to_quote_dirs)
config->quote_dirs.insert(normalized_arg);
if (add_next_flag_to_angle_dirs)
config->angle_dirs.insert(normalized_arg);
if (clang_cl)
arg = normalized_arg;
next_flag_is_path = false;
add_next_flag_to_quote_dirs = false;
add_next_flag_to_angle_dirs = false;
} else {
// If blacklist skip.
if (StartsWithAny(arg, kBlacklistMulti)) {
i++;
continue;
}
// Check to see if arg is a path and needs to be updated.
for (const std::string& flag_type : kPathArgs) {
// {"-I", "foo"} style.
if (arg == flag_type) {
next_flag_is_path = true;
add_next_flag_to_quote_dirs = ShouldAddToQuoteIncludes(arg);
add_next_flag_to_angle_dirs = ShouldAddToAngleIncludes(arg);
goto done;
}
// {"-Ifoo"} style.
if (StartsWith(arg, flag_type)) {
std::string path = arg.substr(flag_type.size());
assert(!path.empty());
path = entry.ResolveIfRelative(path);
if (clang_cl || StartsWithAny(arg, kNormalizePathArgs))
arg = flag_type + path;
if (ShouldAddToQuoteIncludes(flag_type))
config->quote_dirs.insert(path);
if (ShouldAddToAngleIncludes(flag_type))
config->angle_dirs.insert(path);
goto done;
}
}
if (StartsWithAny(arg, kBlacklist))
continue;
// This is most likely the file path we will be passing to clang. The
// path needs to be absolute, otherwise clang_codeCompleteAt is extremely
// slow. See
// https://github.com/cquery-project/cquery/commit/af63df09d57d765ce12d40007bf56302a0446678.
if (EndsWith(arg, base_name))
arg = entry.ResolveIfRelative(arg);
// TODO Exclude .a .o to make link command in compile_commands.json work.
// Also, clang_parseTranslationUnit2FullArgv does not seem to accept
// multiple source filenames.
else if (EndsWith(arg, ".a") || EndsWith(arg, ".o"))
continue;
for (size_t i = 1; i < args.size(); i++)
// This is most likely the file path we will be passing to clang. The
// path needs to be absolute, otherwise clang_codeCompleteAt is extremely
// slow. See
// https://github.com/cquery-project/cquery/commit/af63df09d57d765ce12d40007bf56302a0446678.
if (args[i][0] != '-' && EndsWith(args[i], base_name)) {
args[i] = entry.ResolveIfRelative(args[i]);
continue;
}
done:
result.args.push_back(arg);
}
// We don't do any special processing on user-given extra flags.
for (const auto& flag : config->extra_flags)
result.args.push_back(flag);
args.push_back(flag);
// Add -resource-dir so clang can correctly resolve system includes like
// <cstddef>
if (!AnyStartsWith(result.args, "-resource-dir"))
result.args.push_back("-resource-dir=" + g_config->clang.resourceDir);
if (!Args.hasArg(OPT_resource_dir))
args.push_back("-resource-dir=" + g_config->clang.resourceDir);
if (!Args.hasArg(OPT_working_directory))
args.push_back("-working-directory=" + entry.directory.string());
// There could be a clang version mismatch between what the project uses and
// what ccls uses. Make sure we do not emit warnings for mismatched options.
if (!AnyStartsWith(result.args, "-Wno-unknown-warning-option"))
result.args.push_back("-Wno-unknown-warning-option");
args.push_back("-Wno-unknown-warning-option");
// Using -fparse-all-comments enables documentation in the indexer and in
// code completion.
if (g_config->index.comments > 1 &&
!AnyStartsWith(result.args, "-fparse-all-comments")) {
result.args.push_back("-fparse-all-comments");
}
if (g_config->index.comments > 1)
args.push_back("-fparse-all-comments");
result.args = std::move(args);
return result;
}
@ -601,15 +519,15 @@ TEST_SUITE("Project") {
CheckFlags(
/* raw */ {"clang", "-lstdc++", "myfile.cc"},
/* expected */
{"clang", "-working-directory=/dir/", "-lstdc++", "/dir/myfile.cc",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"});
{"clang", "-lstdc++", "/dir/myfile.cc",
"-resource-dir=/w/resource_dir/", "-working-directory=/dir/",
"-Wno-unknown-warning-option", "-fparse-all-comments"});
CheckFlags(
/* raw */ {"clang.exe"},
/* expected */
{"clang.exe", "-working-directory=/dir/",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
{"clang.exe", "-resource-dir=/w/resource_dir/",
"-working-directory=/dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"});
}
@ -648,12 +566,13 @@ TEST_SUITE("Project") {
#endif
TEST_CASE("Path in args") {
CheckFlags("/home/user", "/home/user/foo/bar.c",
/* raw */ {"cc", "-O0", "foo/bar.c"},
/* expected */
{"cc", "-working-directory=/home/user", "-O0",
"/home/user/foo/bar.c", "-resource-dir=/w/resource_dir/",
"-Wno-unknown-warning-option", "-fparse-all-comments"});
CheckFlags(
"/home/user", "/home/user/foo/bar.c",
/* raw */ {"cc", "-O0", "foo/bar.c"},
/* expected */
{"cc", "-O0", "/home/user/foo/bar.c", "-resource-dir=/w/resource_dir/",
"-working-directory=/home/user", "-Wno-unknown-warning-option",
"-fparse-all-comments"});
}
TEST_CASE("Directory extraction") {