Skip to content

Commit

Permalink
feat: detect compiler default include paths
Browse files Browse the repository at this point in the history
  • Loading branch information
fpelliccioni authored Dec 20, 2023
1 parent cd00fe6 commit 0720a15
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 51 deletions.
14 changes: 1 addition & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <fmt/format.h>
#include <clang/Basic/LangStandard.h>
#include <clang/Driver/Driver.h>
Expand Down Expand Up @@ -51,14 +51,20 @@ static
std::vector<std::string>
adjustCommandLine(
const std::vector<std::string>& cmdline,
const std::vector<std::string>& additional_defines)
const std::vector<std::string>& additional_defines,
std::unordered_map<std::string, std::vector<std::string>> const& implicitIncludeDirectories)
{
std::vector<std::string> new_cmdline;
std::vector<std::string> discarded_cmdline;
llvm::opt::InputArgList args;
StringRef driver_mode;
std::vector<std::string> systemIncludePaths;

if(! cmdline.empty())
{
if (auto it = implicitIncludeDirectories.find(cmdline[0]); it != implicitIncludeDirectories.end()) {
systemIncludePaths = it->second;
}

std::vector<const char*> raw_cmdline;
raw_cmdline.reserve(cmdline.size());
for(const auto& s : cmdline)
Expand All @@ -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;
Expand All @@ -93,10 +102,6 @@ adjustCommandLine(

if(! arg)
{
discarded_cmdline.insert(
discarded_cmdline.end(),
cmdline.begin() + old_idx,
cmdline.begin() + idx);
continue;
}

Expand Down Expand Up @@ -148,10 +153,6 @@ adjustCommandLine(
// driver::options::OPT__SLASH_Tc
))
{
discarded_cmdline.insert(
discarded_cmdline.end(),
cmdline.begin() + old_idx,
cmdline.begin() + idx);
continue;
}

Expand All @@ -164,11 +165,12 @@ adjustCommandLine(
return new_cmdline;
}

AbsoluteCompilationDatabase::
AbsoluteCompilationDatabase(
MrDocsCompilationDatabase::
MrDocsCompilationDatabase(
llvm::StringRef workingDir,
CompilationDatabase const& inner,
std::shared_ptr<const Config> config)
std::shared_ptr<const Config> config,
std::unordered_map<std::string, std::vector<std::string>> const& implicitIncludeDirectories)
{
namespace fs = llvm::sys::fs;
namespace path = llvm::sys::path;
Expand All @@ -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))
{
Expand Down Expand Up @@ -227,7 +230,7 @@ AbsoluteCompilationDatabase(
}

std::vector<tooling::CompileCommand>
AbsoluteCompilationDatabase::
MrDocsCompilationDatabase::
getCompileCommands(
llvm::StringRef FilePath) const
{
Expand All @@ -243,7 +246,7 @@ getCompileCommands(
}

std::vector<std::string>
AbsoluteCompilationDatabase::
MrDocsCompilationDatabase::
getAllFiles() const
{
std::vector<std::string> allFiles;
Expand All @@ -254,7 +257,7 @@ getAllFiles() const
}

std::vector<tooling::CompileCommand>
AbsoluteCompilationDatabase::
MrDocsCompilationDatabase::
getAllCompileCommands() const
{
return AllCommands_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <mrdocs/Config.hpp>
#include <clang/Tooling/JSONCompilationDatabase.h>
Expand All @@ -26,24 +26,32 @@ namespace mrdocs {
them according to the working directory specified
at construction.
*/
class AbsoluteCompilationDatabase
class MrDocsCompilationDatabase
: public tooling::CompilationDatabase
{
std::vector<tooling::CompileCommand> AllCommands_;
llvm::StringMap<std::size_t> 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<const Config> config);
std::shared_ptr<const Config> config,
std::unordered_map<std::string, std::vector<std::string>> const& implicitIncludeDirectories);

std::vector<tooling::CompileCommand>
getCompileCommands(
Expand All @@ -59,5 +67,5 @@ class AbsoluteCompilationDatabase
} // mrdocs
} // clang

#endif
#endif // MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP

9 changes: 6 additions & 3 deletions src/test/TestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <mrdocs/Config.hpp>
#include <mrdocs/Generators.hpp>
Expand Down Expand Up @@ -105,9 +105,12 @@ handleFile(
path::replace_extension(expectedPath, xmlGen_->fileExtension());

auto workingDir = files::getParentDir(filePath);

std::unordered_map<std::string, std::vector<std::string>> 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);
Expand Down
110 changes: 110 additions & 0 deletions src/tool/CompilerInfo.cpp
Original file line number Diff line number Diff line change
@@ -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 ([email protected])
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#include "CompilerInfo.hpp"

#include <mrdocs/Support/Error.hpp>

#include <llvm/Support/Program.h>

namespace clang {
namespace mrdocs {

std::optional<std::string>
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<llvm::StringRef> const redirects[] = {llvm::StringRef(), llvm::StringRef(), outputPath.str()};
std::vector<llvm::StringRef> const args = {compilerPath, "-v", "-E", "-x", "c++", "-"};
llvm::ArrayRef<llvm::StringRef> 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<std::string>
parseIncludePaths(std::string const& compilerOutput)
{
std::vector<std::string> 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<std::string, std::vector<std::string>>
getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb)
{
std::unordered_map<std::string, std::vector<std::string>> 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<std::string> includePaths = parseIncludePaths(*compilerOutput);
res.emplace(compilerPath, std::move(includePaths));
}
}

return res;
}

} // mrdocs
} // clang
55 changes: 55 additions & 0 deletions src/tool/CompilerInfo.hpp
Original file line number Diff line number Diff line change
@@ -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 ([email protected])
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#ifndef MRDOCS_TOOL_COMPILER_HPP
#define MRDOCS_TOOL_COMPILER_HPP

#include <optional>
#include <string>
#include <vector>
#include <unordered_map>

#include <clang/Tooling/CompilationDatabase.h>
#include <llvm/ADT/StringRef.h>

namespace clang {
namespace mrdocs {

/**
* @brief Get the compiler verbose output.
*
* @param compilerPath The compiler path.
* @return std::optional<std::string> The compiler verbose output.
*/
std::optional<std::string>
getCompilerVerboseOutput(llvm::StringRef compilerPath);

/**
* @brief Parse the include paths.
*
* @param compilerOutput The compiler output.
* @return std::vector<std::string> The include paths.
*/
std::vector<std::string>
parseIncludePaths(std::string const& compilerOutput);

/**
* @brief Get the compiler default include dir.
*
* @param compDb The compilation database.
* @return std::unordered_map<std::string, std::vector<std::string>> The compiler default include dir.
*/
std::unordered_map<std::string, std::vector<std::string>>
getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb);

} // mrdocs
} // clang

#endif
Loading

0 comments on commit 0720a15

Please sign in to comment.