diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70df67f03..f6eeb1b3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -325,19 +325,7 @@ jobs: rm -rf __build__ fi mkdir __build__ - cd __build__ - mkdir __include_dirs__ - cd __include_dirs__ - output_file="include_dirs.txt" - echo "cmake_minimum_required(VERSION 3.22)" > CMakeLists.txt - echo "project(get_implicit_dirs)" >> CMakeLists.txt - echo "file(WRITE \"${output_file}\" \"\${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}\")" >> CMakeLists.txt - mkdir __build__ - cd __build__ - cmake -D CMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx }} -D CMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc }} .. - cd .. - default_includes=$(cat "$output_file") - cd .. + cd __build__ cmake -D BOOST_URL_BUILD_TESTS=OFF -D BOOST_URL_BUILD_EXAMPLES=OFF -D CMAKE_EXPORT_COMPILE_COMMANDS=ON -D CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES="$default_includes" -D CMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx }} -D CMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc }} .. - name: Generate demos diff --git a/src/lib/Lib/AbsoluteCompilationDatabase.cpp b/src/lib/Lib/MrDocsCompilationDatabase.cpp similarity index 89% rename from src/lib/Lib/AbsoluteCompilationDatabase.cpp rename to src/lib/Lib/MrDocsCompilationDatabase.cpp index b23b02482..c7173f985 100644 --- a/src/lib/Lib/AbsoluteCompilationDatabase.cpp +++ b/src/lib/Lib/MrDocsCompilationDatabase.cpp @@ -12,7 +12,7 @@ #include "lib/Support/Debug.hpp" #include "lib/Support/Path.hpp" #include "lib/Lib/ConfigImpl.hpp" -#include "lib/Lib/AbsoluteCompilationDatabase.hpp" +#include "lib/Lib/MrDocsCompilationDatabase.hpp" #include #include #include @@ -51,14 +51,20 @@ static std::vector adjustCommandLine( const std::vector& cmdline, - const std::vector& additional_defines) + const std::vector& additional_defines, + std::unordered_map> const& implicitIncludeDirectories) { std::vector new_cmdline; - std::vector discarded_cmdline; llvm::opt::InputArgList args; StringRef driver_mode; + std::vector systemIncludePaths; + if(! cmdline.empty()) { + if (auto it = implicitIncludeDirectories.find(cmdline[0]); it != implicitIncludeDirectories.end()) { + systemIncludePaths = it->second; + } + std::vector raw_cmdline; raw_cmdline.reserve(cmdline.size()); for(const auto& s : cmdline) @@ -85,6 +91,9 @@ adjustCommandLine( for(const auto& def : additional_defines) new_cmdline.emplace_back(fmt::format("-D{}", def)); + for (auto const& inc : systemIncludePaths) + new_cmdline.emplace_back(fmt::format("-I{}", inc)); + for(unsigned idx = 1; idx < cmdline.size();) { const unsigned old_idx = idx; @@ -93,10 +102,6 @@ adjustCommandLine( if(! arg) { - discarded_cmdline.insert( - discarded_cmdline.end(), - cmdline.begin() + old_idx, - cmdline.begin() + idx); continue; } @@ -148,10 +153,6 @@ adjustCommandLine( // driver::options::OPT__SLASH_Tc )) { - discarded_cmdline.insert( - discarded_cmdline.end(), - cmdline.begin() + old_idx, - cmdline.begin() + idx); continue; } @@ -164,11 +165,12 @@ adjustCommandLine( return new_cmdline; } -AbsoluteCompilationDatabase:: -AbsoluteCompilationDatabase( +MrDocsCompilationDatabase:: +MrDocsCompilationDatabase( llvm::StringRef workingDir, CompilationDatabase const& inner, - std::shared_ptr config) + std::shared_ptr config, + std::unordered_map> const& implicitIncludeDirectories) { namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; @@ -187,7 +189,8 @@ AbsoluteCompilationDatabase( cmd.Output = cmd0.Output; cmd.CommandLine = adjustCommandLine( cmd0.CommandLine, - (*config_impl)->defines); + (*config_impl)->defines, + implicitIncludeDirectories); if(path::is_absolute(cmd0.Directory)) { @@ -227,7 +230,7 @@ AbsoluteCompilationDatabase( } std::vector -AbsoluteCompilationDatabase:: +MrDocsCompilationDatabase:: getCompileCommands( llvm::StringRef FilePath) const { @@ -243,7 +246,7 @@ getCompileCommands( } std::vector -AbsoluteCompilationDatabase:: +MrDocsCompilationDatabase:: getAllFiles() const { std::vector allFiles; @@ -254,7 +257,7 @@ getAllFiles() const } std::vector -AbsoluteCompilationDatabase:: +MrDocsCompilationDatabase:: getAllCompileCommands() const { return AllCommands_; diff --git a/src/lib/Lib/AbsoluteCompilationDatabase.hpp b/src/lib/Lib/MrDocsCompilationDatabase.hpp similarity index 55% rename from src/lib/Lib/AbsoluteCompilationDatabase.hpp rename to src/lib/Lib/MrDocsCompilationDatabase.hpp index afb65e876..e2d5f8dad 100644 --- a/src/lib/Lib/AbsoluteCompilationDatabase.hpp +++ b/src/lib/Lib/MrDocsCompilationDatabase.hpp @@ -8,8 +8,8 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#ifndef MRDOCS_LIB_TOOL_ABSOLUTECOMPILATIONDATABASE_HPP -#define MRDOCS_LIB_TOOL_ABSOLUTECOMPILATIONDATABASE_HPP +#ifndef MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP +#define MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP #include #include @@ -26,24 +26,32 @@ namespace mrdocs { them according to the working directory specified at construction. */ -class AbsoluteCompilationDatabase +class MrDocsCompilationDatabase : public tooling::CompilationDatabase { std::vector AllCommands_; llvm::StringMap IndexByFile_; public: - /** Constructor. - - This copies the contents of the source compilation - database. Every relative path is converted into an - absolute path by resolving against the specified - working directory. - */ - AbsoluteCompilationDatabase( + /** + * Constructor. + * + * This copies the contents of the source compilation + * database. Every relative path is converted into an + * absolute path by resolving against the specified + * working directory. + * + * @param workingDir The working directory against which relative paths will be resolved. + * @param inner The source compilation database to copy. + * @param config The shared configuration object. + * @param implicitIncludeDirectories A map from compiler executable paths to their respective + * implicit include directories, as determined by the system's compiler. + */ + MrDocsCompilationDatabase( llvm::StringRef workingDir, CompilationDatabase const& inner, - std::shared_ptr config); + std::shared_ptr config, + std::unordered_map> const& implicitIncludeDirectories); std::vector getCompileCommands( @@ -59,5 +67,5 @@ class AbsoluteCompilationDatabase } // mrdocs } // clang -#endif +#endif // MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index ab3e6ac0c..1d5224c15 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -12,9 +12,9 @@ #include "TestArgs.hpp" #include "lib/Support/Error.hpp" #include "lib/Support/Path.hpp" -#include "lib/Lib/AbsoluteCompilationDatabase.hpp" #include "lib/Lib/ConfigImpl.hpp" #include "lib/Lib/CorpusImpl.hpp" +#include "lib/Lib/MrDocsCompilationDatabase.hpp" #include "lib/Lib/SingleFileDB.hpp" #include #include @@ -105,9 +105,12 @@ handleFile( path::replace_extension(expectedPath, xmlGen_->fileExtension()); auto workingDir = files::getParentDir(filePath); + + std::unordered_map> defaultIncludePaths; + // Convert relative paths to absolute - AbsoluteCompilationDatabase compilations( - llvm::StringRef(workingDir), SingleFileDB(filePath), config); + MrDocsCompilationDatabase compilations( + llvm::StringRef(workingDir), SingleFileDB(filePath), config, defaultIncludePaths); // Build Corpus auto corpus = CorpusImpl::build( report::Level::debug, config, compilations); diff --git a/src/tool/CompilerInfo.cpp b/src/tool/CompilerInfo.cpp new file mode 100644 index 000000000..41ac80bea --- /dev/null +++ b/src/tool/CompilerInfo.cpp @@ -0,0 +1,110 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// 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 "CompilerInfo.hpp" + +#include + +#include + +namespace clang { +namespace mrdocs { + +std::optional +getCompilerVerboseOutput(llvm::StringRef compilerPath) +{ + if ( ! llvm::sys::fs::exists(compilerPath)) { + return std::nullopt; + } + + llvm::SmallString<128> outputPath; + if (auto ec = llvm::sys::fs::createTemporaryFile("compiler-info", "txt", outputPath)) + { + return std::nullopt; + } + + std::optional const redirects[] = {llvm::StringRef(), llvm::StringRef(), outputPath.str()}; + std::vector const args = {compilerPath, "-v", "-E", "-x", "c++", "-"}; + llvm::ArrayRef emptyEnv; + int const result = llvm::sys::ExecuteAndWait(compilerPath, args, emptyEnv, redirects); + if (result != 0) + { + llvm::sys::fs::remove(outputPath); + return std::nullopt; + } + + auto bufferOrError = llvm::MemoryBuffer::getFile(outputPath); + llvm::sys::fs::remove(outputPath); + if ( ! bufferOrError) + { + return std::nullopt; + } + + return bufferOrError.get()->getBuffer().str(); +} + +std::vector +parseIncludePaths(std::string const& compilerOutput) +{ + std::vector includePaths; + std::istringstream stream(compilerOutput); + std::string line; + bool capture = false; + + while (std::getline(stream, line)) + { + if (line.find("#include <...> search starts here:") != std::string::npos) + { + capture = true; + continue; + } + if (line.find("End of search list.") != std::string::npos) + { + break; + } + if (capture) + { + line.erase(0, line.find_first_not_of(" ")); + includePaths.push_back(line); + } + } + + return includePaths; +} + +std::unordered_map> +getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb) +{ + std::unordered_map> res; + auto const allCommands = compDb.getAllCompileCommands(); + + for (auto const& cmd : allCommands) { + if ( ! cmd.CommandLine.empty()) { + auto const& compilerPath = cmd.CommandLine[0]; + if (res.contains(compilerPath)) { + continue; + } + + auto const compilerOutput = getCompilerVerboseOutput(compilerPath); + if ( ! compilerOutput) { + report::warn("Warning: could not get compiler info for \"{}\"", compilerPath); + continue; + } + std::vector includePaths = parseIncludePaths(*compilerOutput); + res.emplace(compilerPath, std::move(includePaths)); + } + } + + return res; +} + +} // mrdocs +} // clang diff --git a/src/tool/CompilerInfo.hpp b/src/tool/CompilerInfo.hpp new file mode 100644 index 000000000..aa9d112ed --- /dev/null +++ b/src/tool/CompilerInfo.hpp @@ -0,0 +1,55 @@ +// +// 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_COMPILER_HPP +#define MRDOCS_TOOL_COMPILER_HPP + +#include +#include +#include +#include + +#include +#include + +namespace clang { +namespace mrdocs { + +/** + * @brief Get the compiler verbose output. + * + * @param compilerPath The compiler path. + * @return std::optional The compiler verbose output. +*/ +std::optional +getCompilerVerboseOutput(llvm::StringRef compilerPath); + +/** + * @brief Parse the include paths. + * + * @param compilerOutput The compiler output. + * @return std::vector The include paths. +*/ +std::vector +parseIncludePaths(std::string const& compilerOutput); + +/** + * @brief Get the compiler default include dir. + * + * @param compDb The compilation database. + * @return std::unordered_map> The compiler default include dir. +*/ +std::unordered_map> +getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb); + +} // mrdocs +} // clang + +#endif diff --git a/src/tool/GenerateAction.cpp b/src/tool/GenerateAction.cpp index 5627fbe1b..b4e10df03 100644 --- a/src/tool/GenerateAction.cpp +++ b/src/tool/GenerateAction.cpp @@ -9,14 +9,17 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include "CompilerInfo.hpp" #include "ToolArgs.hpp" -#include "lib/Lib/AbsoluteCompilationDatabase.hpp" #include "lib/Lib/ConfigImpl.hpp" #include "lib/Lib/CorpusImpl.hpp" +#include "lib/Lib/MrDocsCompilationDatabase.hpp" +#include "llvm/Support/Program.h" #include #include #include #include + #include namespace clang { @@ -87,10 +90,13 @@ DoGenerateAction() tooling::JSONCommandLineSyntax::AutoDetect), std::move(errorMessage)); + // Get the default include paths for each compiler + auto const defaultIncludePaths = getCompilersDefaultIncludeDir(compileCommands); + // Custom compilation database that converts relative paths to absolute auto compileCommandsDir = files::getParentDir(compilationsPath); - AbsoluteCompilationDatabase absCompileCommands( - compileCommandsDir, compileCommands, config); + MrDocsCompilationDatabase compilationDatabase( + compileCommandsDir, compileCommands, config, defaultIncludePaths); // Normalize outputPath path MRDOCS_CHECK(toolArgs.outputPath, "The output path argument is missing"); @@ -106,7 +112,7 @@ DoGenerateAction() MRDOCS_TRY( auto corpus, CorpusImpl::build( - report::Level::info, config, absCompileCommands)); + report::Level::info, config, compilationDatabase)); // -------------------------------------------------------------- //