Skip to content

Commit 0720a15

Browse files
authored
feat: detect compiler default include paths
1 parent cd00fe6 commit 0720a15

File tree

7 files changed

+224
-51
lines changed

7 files changed

+224
-51
lines changed

.github/workflows/ci.yml

+1-13
Original file line numberDiff line numberDiff line change
@@ -325,19 +325,7 @@ jobs:
325325
rm -rf __build__
326326
fi
327327
mkdir __build__
328-
cd __build__
329-
mkdir __include_dirs__
330-
cd __include_dirs__
331-
output_file="include_dirs.txt"
332-
echo "cmake_minimum_required(VERSION 3.22)" > CMakeLists.txt
333-
echo "project(get_implicit_dirs)" >> CMakeLists.txt
334-
echo "file(WRITE \"${output_file}\" \"\${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}\")" >> CMakeLists.txt
335-
mkdir __build__
336-
cd __build__
337-
cmake -D CMAKE_CXX_COMPILER=${{ steps.setup-cpp.outputs.cxx }} -D CMAKE_C_COMPILER=${{ steps.setup-cpp.outputs.cc }} ..
338-
cd ..
339-
default_includes=$(cat "$output_file")
340-
cd ..
328+
cd __build__
341329
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 }} ..
342330
343331
- name: Generate demos

src/lib/Lib/AbsoluteCompilationDatabase.cpp renamed to src/lib/Lib/MrDocsCompilationDatabase.cpp

+21-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#include "lib/Support/Debug.hpp"
1313
#include "lib/Support/Path.hpp"
1414
#include "lib/Lib/ConfigImpl.hpp"
15-
#include "lib/Lib/AbsoluteCompilationDatabase.hpp"
15+
#include "lib/Lib/MrDocsCompilationDatabase.hpp"
1616
#include <fmt/format.h>
1717
#include <clang/Basic/LangStandard.h>
1818
#include <clang/Driver/Driver.h>
@@ -51,14 +51,20 @@ static
5151
std::vector<std::string>
5252
adjustCommandLine(
5353
const std::vector<std::string>& cmdline,
54-
const std::vector<std::string>& additional_defines)
54+
const std::vector<std::string>& additional_defines,
55+
std::unordered_map<std::string, std::vector<std::string>> const& implicitIncludeDirectories)
5556
{
5657
std::vector<std::string> new_cmdline;
57-
std::vector<std::string> discarded_cmdline;
5858
llvm::opt::InputArgList args;
5959
StringRef driver_mode;
60+
std::vector<std::string> systemIncludePaths;
61+
6062
if(! cmdline.empty())
6163
{
64+
if (auto it = implicitIncludeDirectories.find(cmdline[0]); it != implicitIncludeDirectories.end()) {
65+
systemIncludePaths = it->second;
66+
}
67+
6268
std::vector<const char*> raw_cmdline;
6369
raw_cmdline.reserve(cmdline.size());
6470
for(const auto& s : cmdline)
@@ -85,6 +91,9 @@ adjustCommandLine(
8591
for(const auto& def : additional_defines)
8692
new_cmdline.emplace_back(fmt::format("-D{}", def));
8793

94+
for (auto const& inc : systemIncludePaths)
95+
new_cmdline.emplace_back(fmt::format("-I{}", inc));
96+
8897
for(unsigned idx = 1; idx < cmdline.size();)
8998
{
9099
const unsigned old_idx = idx;
@@ -93,10 +102,6 @@ adjustCommandLine(
93102

94103
if(! arg)
95104
{
96-
discarded_cmdline.insert(
97-
discarded_cmdline.end(),
98-
cmdline.begin() + old_idx,
99-
cmdline.begin() + idx);
100105
continue;
101106
}
102107

@@ -148,10 +153,6 @@ adjustCommandLine(
148153
// driver::options::OPT__SLASH_Tc
149154
))
150155
{
151-
discarded_cmdline.insert(
152-
discarded_cmdline.end(),
153-
cmdline.begin() + old_idx,
154-
cmdline.begin() + idx);
155156
continue;
156157
}
157158

@@ -164,11 +165,12 @@ adjustCommandLine(
164165
return new_cmdline;
165166
}
166167

167-
AbsoluteCompilationDatabase::
168-
AbsoluteCompilationDatabase(
168+
MrDocsCompilationDatabase::
169+
MrDocsCompilationDatabase(
169170
llvm::StringRef workingDir,
170171
CompilationDatabase const& inner,
171-
std::shared_ptr<const Config> config)
172+
std::shared_ptr<const Config> config,
173+
std::unordered_map<std::string, std::vector<std::string>> const& implicitIncludeDirectories)
172174
{
173175
namespace fs = llvm::sys::fs;
174176
namespace path = llvm::sys::path;
@@ -187,7 +189,8 @@ AbsoluteCompilationDatabase(
187189
cmd.Output = cmd0.Output;
188190
cmd.CommandLine = adjustCommandLine(
189191
cmd0.CommandLine,
190-
(*config_impl)->defines);
192+
(*config_impl)->defines,
193+
implicitIncludeDirectories);
191194

192195
if(path::is_absolute(cmd0.Directory))
193196
{
@@ -227,7 +230,7 @@ AbsoluteCompilationDatabase(
227230
}
228231

229232
std::vector<tooling::CompileCommand>
230-
AbsoluteCompilationDatabase::
233+
MrDocsCompilationDatabase::
231234
getCompileCommands(
232235
llvm::StringRef FilePath) const
233236
{
@@ -243,7 +246,7 @@ getCompileCommands(
243246
}
244247

245248
std::vector<std::string>
246-
AbsoluteCompilationDatabase::
249+
MrDocsCompilationDatabase::
247250
getAllFiles() const
248251
{
249252
std::vector<std::string> allFiles;
@@ -254,7 +257,7 @@ getAllFiles() const
254257
}
255258

256259
std::vector<tooling::CompileCommand>
257-
AbsoluteCompilationDatabase::
260+
MrDocsCompilationDatabase::
258261
getAllCompileCommands() const
259262
{
260263
return AllCommands_;

src/lib/Lib/AbsoluteCompilationDatabase.hpp renamed to src/lib/Lib/MrDocsCompilationDatabase.hpp

+21-13
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
// Official repository: https://github.com/cppalliance/mrdocs
99
//
1010

11-
#ifndef MRDOCS_LIB_TOOL_ABSOLUTECOMPILATIONDATABASE_HPP
12-
#define MRDOCS_LIB_TOOL_ABSOLUTECOMPILATIONDATABASE_HPP
11+
#ifndef MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP
12+
#define MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP
1313

1414
#include <mrdocs/Config.hpp>
1515
#include <clang/Tooling/JSONCompilationDatabase.h>
@@ -26,24 +26,32 @@ namespace mrdocs {
2626
them according to the working directory specified
2727
at construction.
2828
*/
29-
class AbsoluteCompilationDatabase
29+
class MrDocsCompilationDatabase
3030
: public tooling::CompilationDatabase
3131
{
3232
std::vector<tooling::CompileCommand> AllCommands_;
3333
llvm::StringMap<std::size_t> IndexByFile_;
3434

3535
public:
36-
/** Constructor.
37-
38-
This copies the contents of the source compilation
39-
database. Every relative path is converted into an
40-
absolute path by resolving against the specified
41-
working directory.
42-
*/
43-
AbsoluteCompilationDatabase(
36+
/**
37+
* Constructor.
38+
*
39+
* This copies the contents of the source compilation
40+
* database. Every relative path is converted into an
41+
* absolute path by resolving against the specified
42+
* working directory.
43+
*
44+
* @param workingDir The working directory against which relative paths will be resolved.
45+
* @param inner The source compilation database to copy.
46+
* @param config The shared configuration object.
47+
* @param implicitIncludeDirectories A map from compiler executable paths to their respective
48+
* implicit include directories, as determined by the system's compiler.
49+
*/
50+
MrDocsCompilationDatabase(
4451
llvm::StringRef workingDir,
4552
CompilationDatabase const& inner,
46-
std::shared_ptr<const Config> config);
53+
std::shared_ptr<const Config> config,
54+
std::unordered_map<std::string, std::vector<std::string>> const& implicitIncludeDirectories);
4755

4856
std::vector<tooling::CompileCommand>
4957
getCompileCommands(
@@ -59,5 +67,5 @@ class AbsoluteCompilationDatabase
5967
} // mrdocs
6068
} // clang
6169

62-
#endif
70+
#endif // MRDOCS_LIB_TOOL_MR_DOCS_COMPILATION_DATABASE_HPP
6371

src/test/TestRunner.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
#include "TestArgs.hpp"
1313
#include "lib/Support/Error.hpp"
1414
#include "lib/Support/Path.hpp"
15-
#include "lib/Lib/AbsoluteCompilationDatabase.hpp"
1615
#include "lib/Lib/ConfigImpl.hpp"
1716
#include "lib/Lib/CorpusImpl.hpp"
17+
#include "lib/Lib/MrDocsCompilationDatabase.hpp"
1818
#include "lib/Lib/SingleFileDB.hpp"
1919
#include <mrdocs/Config.hpp>
2020
#include <mrdocs/Generators.hpp>
@@ -105,9 +105,12 @@ handleFile(
105105
path::replace_extension(expectedPath, xmlGen_->fileExtension());
106106

107107
auto workingDir = files::getParentDir(filePath);
108+
109+
std::unordered_map<std::string, std::vector<std::string>> defaultIncludePaths;
110+
108111
// Convert relative paths to absolute
109-
AbsoluteCompilationDatabase compilations(
110-
llvm::StringRef(workingDir), SingleFileDB(filePath), config);
112+
MrDocsCompilationDatabase compilations(
113+
llvm::StringRef(workingDir), SingleFileDB(filePath), config, defaultIncludePaths);
111114
// Build Corpus
112115
auto corpus = CorpusImpl::build(
113116
report::Level::debug, config, compilations);

src/tool/CompilerInfo.cpp

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//
2+
// This is a derivative work. originally part of the LLVM Project.
3+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
8+
//
9+
// Official repository: https://github.com/cppalliance/mrdocs
10+
//
11+
12+
#include "CompilerInfo.hpp"
13+
14+
#include <mrdocs/Support/Error.hpp>
15+
16+
#include <llvm/Support/Program.h>
17+
18+
namespace clang {
19+
namespace mrdocs {
20+
21+
std::optional<std::string>
22+
getCompilerVerboseOutput(llvm::StringRef compilerPath)
23+
{
24+
if ( ! llvm::sys::fs::exists(compilerPath)) {
25+
return std::nullopt;
26+
}
27+
28+
llvm::SmallString<128> outputPath;
29+
if (auto ec = llvm::sys::fs::createTemporaryFile("compiler-info", "txt", outputPath))
30+
{
31+
return std::nullopt;
32+
}
33+
34+
std::optional<llvm::StringRef> const redirects[] = {llvm::StringRef(), llvm::StringRef(), outputPath.str()};
35+
std::vector<llvm::StringRef> const args = {compilerPath, "-v", "-E", "-x", "c++", "-"};
36+
llvm::ArrayRef<llvm::StringRef> emptyEnv;
37+
int const result = llvm::sys::ExecuteAndWait(compilerPath, args, emptyEnv, redirects);
38+
if (result != 0)
39+
{
40+
llvm::sys::fs::remove(outputPath);
41+
return std::nullopt;
42+
}
43+
44+
auto bufferOrError = llvm::MemoryBuffer::getFile(outputPath);
45+
llvm::sys::fs::remove(outputPath);
46+
if ( ! bufferOrError)
47+
{
48+
return std::nullopt;
49+
}
50+
51+
return bufferOrError.get()->getBuffer().str();
52+
}
53+
54+
std::vector<std::string>
55+
parseIncludePaths(std::string const& compilerOutput)
56+
{
57+
std::vector<std::string> includePaths;
58+
std::istringstream stream(compilerOutput);
59+
std::string line;
60+
bool capture = false;
61+
62+
while (std::getline(stream, line))
63+
{
64+
if (line.find("#include <...> search starts here:") != std::string::npos)
65+
{
66+
capture = true;
67+
continue;
68+
}
69+
if (line.find("End of search list.") != std::string::npos)
70+
{
71+
break;
72+
}
73+
if (capture)
74+
{
75+
line.erase(0, line.find_first_not_of(" "));
76+
includePaths.push_back(line);
77+
}
78+
}
79+
80+
return includePaths;
81+
}
82+
83+
std::unordered_map<std::string, std::vector<std::string>>
84+
getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb)
85+
{
86+
std::unordered_map<std::string, std::vector<std::string>> res;
87+
auto const allCommands = compDb.getAllCompileCommands();
88+
89+
for (auto const& cmd : allCommands) {
90+
if ( ! cmd.CommandLine.empty()) {
91+
auto const& compilerPath = cmd.CommandLine[0];
92+
if (res.contains(compilerPath)) {
93+
continue;
94+
}
95+
96+
auto const compilerOutput = getCompilerVerboseOutput(compilerPath);
97+
if ( ! compilerOutput) {
98+
report::warn("Warning: could not get compiler info for \"{}\"", compilerPath);
99+
continue;
100+
}
101+
std::vector<std::string> includePaths = parseIncludePaths(*compilerOutput);
102+
res.emplace(compilerPath, std::move(includePaths));
103+
}
104+
}
105+
106+
return res;
107+
}
108+
109+
} // mrdocs
110+
} // clang

src/tool/CompilerInfo.hpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
7+
//
8+
// Official repository: https://github.com/cppalliance/mrdocs
9+
//
10+
11+
#ifndef MRDOCS_TOOL_COMPILER_HPP
12+
#define MRDOCS_TOOL_COMPILER_HPP
13+
14+
#include <optional>
15+
#include <string>
16+
#include <vector>
17+
#include <unordered_map>
18+
19+
#include <clang/Tooling/CompilationDatabase.h>
20+
#include <llvm/ADT/StringRef.h>
21+
22+
namespace clang {
23+
namespace mrdocs {
24+
25+
/**
26+
* @brief Get the compiler verbose output.
27+
*
28+
* @param compilerPath The compiler path.
29+
* @return std::optional<std::string> The compiler verbose output.
30+
*/
31+
std::optional<std::string>
32+
getCompilerVerboseOutput(llvm::StringRef compilerPath);
33+
34+
/**
35+
* @brief Parse the include paths.
36+
*
37+
* @param compilerOutput The compiler output.
38+
* @return std::vector<std::string> The include paths.
39+
*/
40+
std::vector<std::string>
41+
parseIncludePaths(std::string const& compilerOutput);
42+
43+
/**
44+
* @brief Get the compiler default include dir.
45+
*
46+
* @param compDb The compilation database.
47+
* @return std::unordered_map<std::string, std::vector<std::string>> The compiler default include dir.
48+
*/
49+
std::unordered_map<std::string, std::vector<std::string>>
50+
getCompilersDefaultIncludeDir(clang::tooling::CompilationDatabase const& compDb);
51+
52+
} // mrdocs
53+
} // clang
54+
55+
#endif

0 commit comments

Comments
 (0)