From 9a6cd49b2e2df82917efdc42e10d5083dc2c88b3 Mon Sep 17 00:00:00 2001 From: Fernando Pelliccioni Date: Wed, 24 Jul 2024 12:10:40 -0300 Subject: [PATCH] Standard library incompatibility fix by packaging libc++ --- .github/workflows/ci.yml | 103 ++++++++++++++++++---- .gitignore | 4 +- CMakeLists.txt | 33 ++++++- include/mrdocs/Config.hpp | 6 ++ include/mrdocs/ConfigOptions.inc | 2 + share/cmake/MrDocs.cmake | 6 ++ src/lib/Lib/ConfigImpl.cpp | 54 ++++++++++-- src/lib/Lib/ConfigImpl.hpp | 15 ++-- src/lib/Lib/MrDocsCompilationDatabase.cpp | 44 ++++++--- src/lib/Metadata/Finalize.cpp | 1 + src/test/TestArgs.cpp | 5 ++ src/test/TestArgs.hpp | 1 + src/test/TestRunner.cpp | 12 ++- src/tool/GenerateAction.cpp | 4 +- src/tool/StdLib.cpp | 85 ++++++++++++++++++ src/tool/StdLib.hpp | 33 +++++++ src/tool/ToolArgs.cpp | 14 +++ src/tool/ToolArgs.hpp | 6 ++ 18 files changed, 376 insertions(+), 52 deletions(-) create mode 100644 src/tool/StdLib.cpp create mode 100644 src/tool/StdLib.hpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a20847d13..54bdbdc5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,9 +104,9 @@ jobs: cd fmt cmake -S . -B ./build -D FMT_DOC=OFF -D FMT_TEST=OFF -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -DCMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx }} -DCMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc }} N_CORES=$(nproc 2>/dev/null || echo 1) - cmake --build ./build --config ${{ matrix.build-type }} --parallel $N_CORES + cmake --build ./build --config ${{ matrix.build-type }} --parallel $N_CORES cmake --install ./build --prefix ./install - + fmt_root=$(pwd)/install if [[ ${{ runner.os }} == 'Windows' ]]; then fmt_root=$(echo "$fmt_root" | sed 's/\\/\//g') @@ -135,9 +135,9 @@ jobs: fi cmake -S . -B ./build -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -DCMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx }} -DCMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc }} N_CORES=$(nproc 2>/dev/null || echo 1) - cmake --build ./build --config ${{ matrix.build-type }} --parallel $N_CORES + cmake --build ./build --config ${{ matrix.build-type }} --parallel $N_CORES cmake --install ./build --prefix ./install - + duktape_root=$(pwd)/install if [[ ${{ runner.os }} == 'Windows' ]]; then duktape_root=$(echo "$duktape_root" | sed 's/\\/\//g') @@ -159,12 +159,12 @@ jobs: git config --global advice.detachedHead false git clone https://github.com/GNOME/libxml2 --branch v2.12.6 --depth 1 cd libxml2 - + cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Release -DLIBXML2_WITH_PROGRAMS=ON -DLIBXML2_WITH_FTP=OFF -DLIBXML2_WITH_HTTP=OFF -DLIBXML2_WITH_ICONV=OFF -DLIBXML2_WITH_LEGACY=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_ZLIB=OFF -DLIBXML2_WITH_ICU=OFF -DLIBXML2_WITH_TESTS=OFF -DLIBXML2_WITH_HTML=ON -DLIBXML2_WITH_C14N=ON -DLIBXML2_WITH_CATALOG=ON -DLIBXML2_WITH_DEBUG=ON -DLIBXML2_WITH_ISO8859X=ON -DLIBXML2_WITH_MEM_DEBUG=OFF -DLIBXML2_WITH_MODULES=ON -DLIBXML2_WITH_OUTPUT=ON -DLIBXML2_WITH_PATTERN=ON -DLIBXML2_WITH_PUSH=ON -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_READER=ON -DLIBXML2_WITH_REGEXPS=ON -DLIBXML2_WITH_SAX1=ON -DLIBXML2_WITH_SCHEMAS=ON -DLIBXML2_WITH_SCHEMATRON=ON -DLIBXML2_WITH_THREADS=ON -DLIBXML2_WITH_THREAD_ALLOC=OFF -DLIBXML2_WITH_TREE=ON -DLIBXML2_WITH_VALID=ON -DLIBXML2_WITH_WRITER=ON -DLIBXML2_WITH_XINCLUDE=ON -DLIBXML2_WITH_XPATH=ON -DLIBXML2_WITH_XPTR=ON -DCMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx || steps.parameters.outputs.clang-bin }} -DCMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc || steps.parameters.outputs.clang-bin }} N_CORES=$(nproc 2>/dev/null || echo 1) - cmake --build ./build --config ${{ matrix.build-type }} --parallel $N_CORES + cmake --build ./build --config ${{ matrix.build-type }} --parallel $N_CORES cmake --install ./build --prefix ./install - + libxml2_root=$(pwd)/install if [[ ${{ runner.os }} == 'Windows' ]]; then libxml2_root=$(echo "$libxml2_root" | sed 's/\\/\//g') @@ -188,6 +188,14 @@ jobs: fi echo -E "llvm-root=$llvm_root" >> $GITHUB_OUTPUT + libcxx_root=$(pwd)/third-party/llvm-project/install/libcxx + if [[ ${{ runner.os }} == 'Windows' ]]; then + libcxx_root=$(echo "$libcxx_root" | sed 's/\\/\//g') + libcxx_root=$(echo $libcxx_root | sed 's|^/d/|D:/|') + echo "$libcxx_root" + fi + echo -E "libcxx-root=$libcxx_root" >> $GITHUB_OUTPUT + - name: LLVM Binaries id: llvm-cache uses: actions/cache@v4 @@ -202,7 +210,7 @@ jobs: run: | # LLVM is be installed with the default compiler set -x - + # Shallow clone LLVM_HASH in ../third-party/llvm cd .. mkdir -p third-party/llvm-project @@ -214,11 +222,11 @@ jobs: git remote add origin https://github.com/llvm/llvm-project.git git fetch --depth 1 origin ${{ steps.llvm-parameters.outputs.llvm-hash }} git checkout FETCH_HEAD - + # Copy presets cp ../../mrdocs/third-party/llvm/CMakePresets.json ./llvm cp ../../mrdocs/third-party/llvm/CMakeUserPresets.json.example ./llvm/CMakeUserPresets.json - + # Build cd llvm llvm_root=$(pwd) @@ -226,7 +234,7 @@ jobs: cmake -S . -B ./build --preset=${{ steps.llvm-parameters.outputs.llvm-build-preset }} -DCMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx }} -DCMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc }} if [[ ${{ runner.os }} == 'Linux' ]]; then cmake --build ./build --target help - fi + fi N_CORES=$(nproc 2>/dev/null || echo 1) if [ ${{ runner.os }} != 'Windows' ]; then cmake --build ./build --config Release --parallel $N_CORES @@ -253,6 +261,63 @@ jobs: fi cmake --install ./build --prefix "$llvm_project_root"/install + - name: LibC++ Binaries + id: libcxx-cache + uses: actions/cache@v4 + with: + path: ${{ steps.llvm-parameters.outputs.libcxx-root }} + key: libcxx-${{ runner.os }}-${{ matrix.compiler }}-${{ matrix.version }}-${{ steps.llvm-parameters.outputs.llvm-hash }} + + - name: Install LibC++ + id: libcxx-install + if: ${{ steps.libcxx-cache.outputs.cache-hit != 'true'}} + shell: bash + run: | + set -x + + cd .. + llvm_project_root="third-party/llvm-project" + if [ ! -d "$llvm_project_root/runtimes" ]; then + mkdir -p $llvm_project_root + cd $llvm_project_root + git config --global init.defaultBranch master + git config --global advice.detachedHead false + git init + git remote add origin https://github.com/llvm/llvm-project.git + git fetch --depth 1 origin ${{ steps.llvm-parameters.outputs.llvm-hash }} + git checkout FETCH_HEAD + fi + + export CXX="$llvm_project_root/install/bin/clang++" + export CC="$llvm_project_root/install/bin/clang" + + cd $llvm_project_root + + if [ ${{ runner.os }} != 'Windows' ]; then + cmake -G Ninja \ + -S runtimes \ + -B build-libcxx \ + -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \ + -DCMAKE_INSTALL_PREFIX="$llvm_project_root/install/libcxx" + + ninja -C build-libcxx cxx cxxabi unwind + ninja -C build-libcxx install-cxx install-cxxabi install-unwind + else + cmake -G Ninja \ + -S runtimes \ + -B build-libcxx \ + -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \ + -DCMAKE_INSTALL_PREFIX="$llvm_project_root/install/libcxx" \ + -DLIBCXXABI_USE_LLVM_UNWINDER=OFF \ + -DLIBCXXABI_ENABLE_SHARED=OFF \ + -DLIBCXXABI_ENABLE_STATIC=ON \ + -DLIBCXX_ENABLE_SHARED=OFF \ + -DLIBCXX_NO_VCRUNTIME=ON \ + -DCMAKE_CXX_FLAGS="-D__ORDER_LITTLE_ENDIAN__=1234 -D__ORDER_BIG_ENDIAN__=4321 -D__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__" + ninja -C build-libcxx cxx + ninja -C build-libcxx install-cxx + fi + - name: Install Node.js uses: actions/setup-node@v3 with: @@ -278,7 +343,7 @@ jobs: -D duktape_ROOT=${{ steps.duktape-install.outputs.duktape-root }} -D Duktape_ROOT=${{ steps.duktape-install.outputs.duktape-root }} -D fmt_ROOT=${{ steps.fmt-install.outputs.fmt-root }} - ${{ (steps.libxml2-install.outputs.libxml2-root && format('-D libxml2_ROOT={0}', steps.libxml2-install.outputs.libxml2-root)) || '' }} + ${{ (steps.libxml2-install.outputs.libxml2-root && format('-D libxml2_ROOT={0}', steps.libxml2-install.outputs.libxml2-root)) || '' }} ${{ (steps.libxml2-install.outputs.libxml2-root && format('-D LibXml2_ROOT={0}', steps.libxml2-install.outputs.libxml2-root)) || '' }} export-compile-commands: true run-tests: true @@ -440,13 +505,13 @@ jobs: export CXX CC="${{ steps.setup-cpp.outputs.cc }}" export CC - + declare -a generators=( "adoc" "html" "xml" ) - + for variant in single multi; do for generator in "${generators[@]}"; do [[ $variant = multi ]] && multipage="true" || multipage="false" @@ -500,15 +565,15 @@ jobs: id: compare-demos run: | set -x - + # Define URLs and directories LOCAL_DEMOS_DIR="./demos/" PREV_DEMOS_DIR="./demos-previous/" DIFF_DIR="./demos-diff/" - + # Create directories if they don't exist mkdir -p $PREV_DEMOS_DIR $DIFF_DIR - + # Iterate over the previous files and compare them with the corresponding local files find $PREV_DEMOS_DIR -type f | while read previous_file; do # Derive the corresponding local file path @@ -526,7 +591,7 @@ jobs: cat "$previous_file" >> "$diff_output" fi done - + # Iterate over the local files to find new files find $LOCAL_DEMOS_DIR -type f | while read local_file; do previous_file="${PREV_DEMOS_DIR}${local_file#$LOCAL_DEMOS_DIR}" @@ -537,7 +602,7 @@ jobs: echo "NEW CONTENT OF THE FILE IS:" >> "$diff_output" fi done - + # Check if the diff directory is empty if [[ -z $(ls -A $DIFF_DIR) ]]; then echo "No differences found." diff --git a/.gitignore b/.gitignore index 2a85a591d..b396488a2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ /test-files/**/*.adoc /test-files/**/*.bad.xml docs/node_modules -docs/build \ No newline at end of file +docs/build +share/mrdocs/libcxx/ +share/mrdocs/clang/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 86ae5cde7..9da5a3611 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,19 @@ elseif (LLVM_ROOT) endif() find_package(LLVM REQUIRED CONFIG) find_package(Clang REQUIRED CONFIG) + +message(STATUS "Clang_FOUND: ${Clang_FOUND}") +message(STATUS "Clang_INCLUDE_DIRS: ${Clang_INCLUDE_DIRS}") +message(STATUS "Clang_LIBRARIES: ${Clang_LIBRARIES}") +message(STATUS "Clang_DEFINITIONS: ${Clang_DEFINITIONS}") +message(STATUS "Clang_EXECUTABLE: ${Clang_EXECUTABLE}") +message(STATUS "Clang_VERSION: ${Clang_VERSION}") + +if (LLVM_ROOT) + set(LIBCXX_DIR "${LLVM_ROOT}/../install/libcxx/include/c++/v1/") + set(CLANG_19_DIR "${LLVM_ROOT}/lib/clang/19/include/") +endif() + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") include(HandleLLVMOptions) add_definitions(${LLVM_DEFINITIONS}) @@ -284,11 +297,11 @@ if (MRDOCS_BUILD_TESTS) endif () target_compile_definitions(mrdocs-test PRIVATE -DMRDOCS_TEST_FILES_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test-files") add_test(NAME mrdocs-unit-tests COMMAND mrdocs-test --unit=true) - add_test(NAME mrdocs-golden-tests COMMAND mrdocs-test --unit=false --action=test "${PROJECT_SOURCE_DIR}/test-files/golden-tests" --addons="${CMAKE_SOURCE_DIR}/share/mrdocs/addons") + add_test(NAME mrdocs-golden-tests COMMAND mrdocs-test --unit=false --action=test "${PROJECT_SOURCE_DIR}/test-files/golden-tests" --addons="${CMAKE_SOURCE_DIR}/share/mrdocs/addons" --libcxx-paths="${LIBCXX_DIR}") foreach (action IN ITEMS create update) add_custom_target( mrdocs-${action}-test-fixtures - COMMAND mrdocs-test --unit=false --action=${action} "${PROJECT_SOURCE_DIR}/test-files/golden-tests" --addons="${CMAKE_SOURCE_DIR}/share/mrdocs/addons" + COMMAND mrdocs-test --unit=false --action=${action} "${PROJECT_SOURCE_DIR}/test-files/golden-tests" --addons="${CMAKE_SOURCE_DIR}/share/mrdocs/addons" --libcxx-paths="${LIBCXX_DIR}" DEPENDS mrdocs-test ) endforeach () @@ -319,6 +332,22 @@ if (MRDOCS_BUILD_TESTS) endif() endif() +#------------------------------------------------- +# +# Copy libc++ and clang headers +# +#------------------------------------------------- + +set(MRDOCS_SHARE_LIBCXX_DIR "${CMAKE_CURRENT_BINARY_DIR}/../share/mrdocs/libcxx") +message(STATUS "Copying libc++ headers to ${MRDOCS_SHARE_LIBCXX_DIR}") +file(MAKE_DIRECTORY ${MRDOCS_SHARE_LIBCXX_DIR}) +file(COPY ${LIBCXX_DIR}/ DESTINATION ${MRDOCS_SHARE_LIBCXX_DIR}) + +set(MRDOCS_SHARE_CLANG_DIR "${CMAKE_CURRENT_BINARY_DIR}/../share/mrdocs/clang") +message(STATUS "Copying clang headers to ${MRDOCS_SHARE_CLANG_DIR}") +file(MAKE_DIRECTORY ${MRDOCS_SHARE_CLANG_DIR}) +file(COPY ${CLANG_19_DIR}/ DESTINATION ${MRDOCS_SHARE_CLANG_DIR}) + #------------------------------------------------- # # Docs diff --git a/include/mrdocs/Config.hpp b/include/mrdocs/Config.hpp index feb441c06..ebf973ae4 100644 --- a/include/mrdocs/Config.hpp +++ b/include/mrdocs/Config.hpp @@ -104,6 +104,12 @@ class MRDOCS_DECL /// from CMakeLists.txt std::string cmake; + /// True if the compiler has to use just the system's standard library + bool useSystemStdLib = false; + + /// Standard Library include paths + std::vector stdLibPaths; + /// Additional defines passed to the compiler std::vector defines; diff --git a/include/mrdocs/ConfigOptions.inc b/include/mrdocs/ConfigOptions.inc index e447b3c1c..460e74f3c 100644 --- a/include/mrdocs/ConfigOptions.inc +++ b/include/mrdocs/ConfigOptions.inc @@ -69,6 +69,8 @@ COMMON_OPTION (compilationDatabase , compilation-database , Path to the c // Build options COMMON_OPTION (cmake , cmake , CMake arguments when generating the compilation database from CMakeLists.txt) COMMON_OPTION (defines , defines , Additional defines passed to the compiler) +COMMON_OPTION (useSystemStdLib , use-system-stdlib , True if the compiler has to use just the system standard library) +COMMON_OPTION (stdLibPaths , stdlib-paths , Standard Library include paths) // Generators COMMON_OPTION (generate , generate , Documentation generator. Supported generators are: adoc/html/xml) diff --git a/share/cmake/MrDocs.cmake b/share/cmake/MrDocs.cmake index fe7a9dda0..0f4a2330b 100644 --- a/share/cmake/MrDocs.cmake +++ b/share/cmake/MrDocs.cmake @@ -146,6 +146,12 @@ function(add_mrdocs MRDOCS_TARGET_NAME) set(MRDOCS_TARGET_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mrdocs) endif() + #------------------------------------------------- + # LibC++ paths + #------------------------------------------------- + if (NOT DEFINED MRDOCS_LIBCXX_PATHS) + set(MRDOCS_LIBCXX_PATHS ${CMAKE_SOURCE_DIR}/share/libcxx) + endif() #------------------------------------------------- # Custom target diff --git a/src/lib/Lib/ConfigImpl.cpp b/src/lib/Lib/ConfigImpl.cpp index 5587a74ba..c23a50771 100644 --- a/src/lib/Lib/ConfigImpl.cpp +++ b/src/lib/Lib/ConfigImpl.cpp @@ -63,7 +63,8 @@ ConfigImpl:: ConfigImpl( Config::Settings const& publicSettings, std::shared_ptr baseConfig, - ThreadPool& threadPool) + ThreadPool& threadPool, + std::string_view execPath) : threadPool_(threadPool) { namespace fs = llvm::sys::fs; @@ -83,6 +84,39 @@ ConfigImpl( } settings_.configDir = files::makeDirsy(files::normalizePath(settings_.configDir)); + // if (settings_.libCxxPaths.empty()) + // { + // // Set LibC++ path from process working directory + // std::string binDir = files::getParentDir(execPath); + // { + // std::string libCxxDir = files::makeDirsy(files::appendPath( + // binDir, "..", "share", "mrdocs", "libcxx")); + // Error err = files::requireDirectory(libCxxDir); + // if (err) + // { + // formatError("Cannot find LibC++ include directory in \"{}\"", libCxxDir).Throw(); + // } + // settings_.libCxxPaths.push_back(libCxxDir); + // } + // { + // std::string clangDir = files::makeDirsy(files::appendPath( + // binDir, "..", "share", "mrdocs", "clang")); + // Error err = files::requireDirectory(clangDir); + // if (err) + // { + // formatError("Cannot find Clang include directory in \"{}\"", clangDir).Throw(); + // } + // settings_.libCxxPaths.push_back(clangDir); + // } + // } + // else + // { + // for (auto& path : settings_.libCxxPaths) + // { + // path = files::makeDirsy(files::normalizePath(path)); + // } + // } + // Addons directory settings_.addons = files::makeAbsolute(publicSettings.addons).value(); files::requireDirectory(settings_.addons).maybeThrow(); @@ -194,12 +228,14 @@ createConfig( std::string_view configDir, Config::Settings const& publicSettings, std::string_view configYaml, - ThreadPool& threadPool) + ThreadPool& threadPool, + std::string_view execPath) { return std::make_shared( publicSettings, nullptr, - threadPool); + threadPool, + execPath); } static @@ -207,7 +243,8 @@ Expected> loadConfig( Config::Settings const& publicSettings, std::shared_ptr const& baseConfig, - ThreadPool& threadPool) + ThreadPool& threadPool, + std::string_view execPath) { namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; @@ -218,7 +255,8 @@ loadConfig( return std::make_shared( publicSettings, baseConfig, - threadPool); + threadPool, + execPath); } catch(Exception const& ex) { @@ -230,12 +268,14 @@ Expected> loadConfigFile( Config::Settings const& publicSettings, std::shared_ptr const& baseConfig, - ThreadPool& threadPool) + ThreadPool& threadPool, + std::string_view execPath) { return loadConfig( publicSettings, baseConfig, - threadPool); + threadPool, + execPath); } namespace { diff --git a/src/lib/Lib/ConfigImpl.hpp b/src/lib/Lib/ConfigImpl.hpp index c7b467d22..09e9a3ce2 100644 --- a/src/lib/Lib/ConfigImpl.hpp +++ b/src/lib/Lib/ConfigImpl.hpp @@ -90,7 +90,8 @@ class ConfigImpl ConfigImpl( Config::Settings const& publicSettings, std::shared_ptr baseConfig, - ThreadPool& threadPool); + ThreadPool& threadPool, + std::string_view execPath); ThreadPool& threadPool() const noexcept override @@ -138,7 +139,8 @@ class ConfigImpl std::string_view workingDir, std::string_view addonsDir, std::string_view configYaml, - ThreadPool& threadPool); + ThreadPool& threadPool, + std::string_view execPath); friend Expected> @@ -146,7 +148,8 @@ class ConfigImpl std::string_view filePath, std::string_view addonsDir, std::shared_ptr const&, - ThreadPool& threadPool); + ThreadPool& threadPool, + std::string_view execPath); }; //------------------------------------------------ @@ -195,7 +198,8 @@ createConfig( std::string_view workingDir, std::string_view addonsDir, std::string_view configYaml, - ThreadPool& threadPool); + ThreadPool& threadPool, + std::string_view execPath); /** Create a configuration by loading a YAML file. @@ -243,7 +247,8 @@ Expected> loadConfigFile( Config::Settings const& publicSettings, std::shared_ptr const& baseConfig, - ThreadPool& threadPool); + ThreadPool& threadPool, + std::string_view execPath); } // mrdocs } // clang diff --git a/src/lib/Lib/MrDocsCompilationDatabase.cpp b/src/lib/Lib/MrDocsCompilationDatabase.cpp index c61f1dd64..2cad13c22 100644 --- a/src/lib/Lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/Lib/MrDocsCompilationDatabase.cpp @@ -222,7 +222,9 @@ std::vector adjustCommandLine( const std::vector& cmdline, const std::vector& additional_defines, - std::unordered_map> const& implicitIncludeDirectories) + std::unordered_map> const& implicitIncludeDirectories, + std::vector const& stdLibIncludeDirs, + bool useSystemStdLib) { if (cmdline.empty()) { @@ -274,18 +276,32 @@ adjustCommandLine( new_cmdline.emplace_back(fmt::format("-D{}", def)); } - // ------------------------------------------------------ - // Add implicit include paths - // ------------------------------------------------------ - // Implicit include paths are those which are automatically - // added by the compiler. These will not be defined in the - // compile command, so we add them here so that clang - // can also find these headers. - if (auto it = implicitIncludeDirectories.find(progName); - it != implicitIncludeDirectories.end()) { - for (auto const& inc : it->second) + if (useSystemStdLib) + { + // ------------------------------------------------------ + // Add implicit include paths + // ------------------------------------------------------ + // Implicit include paths are those which are automatically + // added by the compiler. These will not be defined in the + // compile command, so we add them here so that clang + // can also find these headers. + if (auto it = implicitIncludeDirectories.find(progName); + it != implicitIncludeDirectories.end()) { + for (auto const& inc : it->second) + { + new_cmdline.emplace_back(fmt::format("-isystem{}", inc)); + } + } + } + else + { + // ------------------------------------------------------ + // Add standard library include directories + // ------------------------------------------------------ + + for (auto const& inc : stdLibIncludeDirs) { - new_cmdline.emplace_back(fmt::format("-I{}", inc)); + new_cmdline.emplace_back(fmt::format("-stdlib++-isystem{}", inc)); } } @@ -366,7 +382,9 @@ MrDocsCompilationDatabase( cmd.CommandLine = adjustCommandLine( cmd0.CommandLine, (*config_impl)->defines, - implicitIncludeDirectories); + implicitIncludeDirectories, + (*config_impl)->stdLibPaths, + (*config_impl)->useSystemStdLib); cmd.Directory = makeAbsoluteAndNative(workingDir, cmd0.Directory); cmd.Filename = makeAbsoluteAndNative(workingDir, cmd0.Filename); if (isCXXSrcFile(cmd.Filename)) diff --git a/src/lib/Metadata/Finalize.cpp b/src/lib/Metadata/Finalize.cpp index 90b0af062..b1fb17368 100644 --- a/src/lib/Metadata/Finalize.cpp +++ b/src/lib/Metadata/Finalize.cpp @@ -12,6 +12,7 @@ #include "lib/Lib/Info.hpp" #include "lib/Support/NameParser.hpp" #include +#include #include #include diff --git a/src/test/TestArgs.cpp b/src/test/TestArgs.cpp index 7f6c6da4a..d2b540f40 100644 --- a/src/test/TestArgs.cpp +++ b/src/test/TestArgs.cpp @@ -80,6 +80,11 @@ R"( "addons", llvm::cl::desc("The directory with the addons."), llvm::cl::cat(commonCat)) + +, stdLibPaths( + "stdlib-paths", + llvm::cl::desc("A list of paths to the standard library."), + llvm::cl::cat(commonCat)) { } diff --git a/src/test/TestArgs.hpp b/src/test/TestArgs.hpp index 6a9c20288..6915cbf47 100644 --- a/src/test/TestArgs.hpp +++ b/src/test/TestArgs.hpp @@ -47,6 +47,7 @@ class TestArgs llvm::cl::opt unitOption; llvm::cl::list inputPaths; llvm::cl::opt addons; + llvm::cl::list stdLibPaths; // Hide all options that don't belong to us void hideForeignOptions(); diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index 8a568783c..c4dff2316 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -90,11 +90,13 @@ handleFile( publicSettings.config = configPath; publicSettings.configDir = files::getParentDir(filePath); publicSettings.configYaml = files::getFileText(publicSettings.config).value(); + publicSettings.stdLibPaths = testArgs.stdLibPaths; loadConfig(publicSettings, publicSettings.configYaml).value(); auto configFile = loadConfigFile( publicSettings, config, - threadPool_); + threadPool_, + ""); if(! configFile) return report::error("{}: \"{}\"", configPath, configFile.error()); @@ -242,6 +244,7 @@ namespace { publicSettings.config = configPath; publicSettings.configDir = dirPath; publicSettings.configYaml = files::getFileText(publicSettings.config).value(); + publicSettings.stdLibPaths = testArgs.stdLibPaths; loadConfig(publicSettings, publicSettings.configYaml).value(); publicSettings.sourceRoot = files::makeAbsolute( publicSettings.sourceRoot, @@ -249,7 +252,8 @@ namespace { auto configFile = loadConfigFile( publicSettings, config, - threadPool); + threadPool, + ""); if(! configFile) { report::error("{}: \"{}\"", configPath, configFile.error()); @@ -362,6 +366,7 @@ checkPath( publicSettings.config = configPath; publicSettings.configDir = files::getParentDir(inputPath); publicSettings.configYaml = files::getFileText(publicSettings.config).value(); + publicSettings.stdLibPaths = testArgs.stdLibPaths; if (testArgs.addons.getValue() != "") { publicSettings.addons = files::normalizeDir(testArgs.addons.getValue()); @@ -374,7 +379,8 @@ checkPath( auto configFile = loadConfigFile( publicSettings, config, - threadPool_); + threadPool_, + ""); if(! configFile) return report::error("{}: \"{}\"", configPath, configFile.error()); diff --git a/src/tool/GenerateAction.cpp b/src/tool/GenerateAction.cpp index f4d1213dc..4ccaae13c 100644 --- a/src/tool/GenerateAction.cpp +++ b/src/tool/GenerateAction.cpp @@ -40,7 +40,7 @@ namespace { * * @param inputPath The path to the project, which can be a directory, a `compile_commands.json` file, or a `CMakeLists.txt` file. * @param cmakeArgs The arguments to pass to CMake when generating the compilation database. - * @return An `Expected` object containing the path to the `compile_commands.json` file if the database is generated, or the provided path if it is already the `compile_commands.json` file. + * @return An `Expected` object containing the path to the `compile_commands.json` file if the database is generated, or the provided path if it is already the `compile_commands.json` file. * Returns an `Unexpected` object in case of failure (e.g., file not found, CMake execution failure). */ Expected @@ -97,7 +97,7 @@ DoGenerateAction(std::string_view execPath, char const** argv) ThreadPool threadPool(toolArgs.concurrency); MRDOCS_TRY( std::shared_ptr config, - loadConfigFile(publicSettings, nullptr, threadPool)); + loadConfigFile(publicSettings, nullptr, threadPool, execPath)); // -------------------------------------------------------------- // diff --git a/src/tool/StdLib.cpp b/src/tool/StdLib.cpp new file mode 100644 index 000000000..2ff0afdff --- /dev/null +++ b/src/tool/StdLib.cpp @@ -0,0 +1,85 @@ +// +// Licensed 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 +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "StdLib.hpp" +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +Expected +setupStdLibDirs( + std::vector& stdLibDirsArg, + std::string_view execPath) +{ + if (stdLibDirsArg.empty()) + { + MRDOCS_CHECK(execPath, "getMainExecutable failed"); + + // Set LibC++ path from process working directory + std::string binDir = files::getParentDir(execPath); + { + std::string stdLibDir = files::makeDirsy(files::appendPath( + binDir, "..", "share", "mrdocs", "libcxx")); + Error err = files::requireDirectory(stdLibDir); + MRDOCS_CHECK(err); + stdLibDirsArg.push_back(stdLibDir); + } + { + std::string clangDir = files::makeDirsy(files::appendPath( + binDir, "..", "share", "mrdocs", "clang")); + Error err = files::requireDirectory(clangDir); + MRDOCS_CHECK(err); + stdLibDirsArg.push_back(clangDir); + } + } + else + { + for (auto& path : stdLibDirsArg) + { + path = files::makeDirsy(files::normalizePath(path)); + } + } + return {}; + + // if (!addonsDirArg.empty()) + // { + // // Already set from mrdocs.yml or command line + // return {}; + // } + + // // Set addons dir from environment variable + // auto addonsEnvVar = llvm::sys::Process::GetEnv("MRDOCS_ADDONS_DIR"); + // if (addonsEnvVar) + // { + // MRDOCS_CHECK(*addonsEnvVar, "MRDOCS_ADDONS_DIR is empty"); + // std::string addonsDir = files::makeDirsy(files::normalizePath(*addonsEnvVar)); + // MRDOCS_TRY(files::requireAbsolute(addonsDir)); + // MRDOCS_TRY(files::requireDirectory(addonsDir)); + // addonsDirArg = addonsDir; + // return {}; + // } + + // // Set addons dir from process working directory + // MRDOCS_CHECK(execPath, "getMainExecutable failed"); + // std::string binDir = files::getParentDir(execPath); + // std::string addonsDir = files::makeDirsy(files::appendPath( + // binDir, "..", "share", "mrdocs", "addons")); + // Error err = files::requireDirectory(addonsDir); + // MRDOCS_CHECK(err); + // addonsDirArg = addonsDir; + // return {}; +} + +} // mrdocs +} // clang diff --git a/src/tool/StdLib.hpp b/src/tool/StdLib.hpp new file mode 100644 index 000000000..1d4419bda --- /dev/null +++ b/src/tool/StdLib.hpp @@ -0,0 +1,33 @@ +// +// Licensed 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 +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_TOOL_STDLIB_HPP +#define MRDOCS_TOOL_STDLIB_HPP + +#include +#include +#include + +namespace clang { +namespace mrdocs { + +/** Set the StdLib directory using the argument as a hint. + + @return The error if any occurred. +*/ +Expected +setupStdLibDirs( + std::vector& stdLibDirsArg, + std::string_view execPath); + +} // mrdocs +} // clang + +#endif // MRDOCS_TOOL_STDLIB_HPP diff --git a/src/tool/ToolArgs.cpp b/src/tool/ToolArgs.cpp index 33e7ab914..c27478eaa 100644 --- a/src/tool/ToolArgs.cpp +++ b/src/tool/ToolArgs.cpp @@ -10,6 +10,7 @@ #include "ToolArgs.hpp" #include "Addons.hpp" +#include "StdLib.hpp" #include #include #include @@ -50,6 +51,8 @@ R"( , output(llvm::cl::cat(pathsCat)) , compilationDatabase(llvm::cl::cat(pathsCat)) , cmake(llvm::cl::cat(buildOptsCat)) +, useSystemStdLib(llvm::cl::init(false), llvm::cl::cat(buildOptsCat)) +, stdLibPaths(llvm::cl::cat(buildOptsCat)) , defines(llvm::cl::cat(buildOptsCat)) , generate(llvm::cl::init("adoc"), llvm::cl::cat(generatorCat)) , addons(llvm::cl::cat(generatorCat)) @@ -295,6 +298,17 @@ apply( } } + auto const exp = setupStdLibDirs(s.stdLibPaths, execPath); + if (!exp) + { + return formatError( + "{}:\n" + "Could not locate the standard library directories because " + "the directories ../share/mrdocs/libcxx and ../share/mrdocs/clang " + "do not exist.", + exp.error()); + } + // Make all common relative paths relative to the config file and // make all directories dirsy auto makeRelativeToConfigDir = [&]( diff --git a/src/tool/ToolArgs.hpp b/src/tool/ToolArgs.hpp index 009d13898..afbf55430 100644 --- a/src/tool/ToolArgs.hpp +++ b/src/tool/ToolArgs.hpp @@ -59,6 +59,12 @@ class ToolArgs /// from CMakeLists.txt llvm::cl::opt cmake; + /// True if the compiler has to use just the system's standard library + llvm::cl::opt useSystemStdLib; + + /// Standard Library include paths + llvm::cl::list stdLibPaths; + /// Additional defines passed to the compiler llvm::cl::list defines;