50 #include "llvm/ADT/ArrayRef.h"
51 #include "llvm/ADT/DenseMap.h"
52 #include "llvm/ADT/Optional.h"
53 #include "llvm/ADT/StringExtras.h"
54 #include "llvm/ADT/StringSwitch.h"
55 #include "llvm/Option/ArgList.h"
56 #include "llvm/Option/OptTable.h"
57 #include "llvm/Support/Debug.h"
58 #include "llvm/Support/Path.h"
59 #include "llvm/Support/StringSaver.h"
60 #include "llvm/Support/raw_ostream.h"
68 namespace path = llvm::sys::path;
71 size_t matchingPrefix(StringRef L, StringRef R) {
72 size_t Limit =
std::min(L.size(), R.size());
73 for (
size_t I = 0; I < Limit; ++I)
81 template <
bool Prefix>
struct Less {
82 bool operator()(StringRef Key, std::pair<StringRef, size_t>
Value)
const {
83 StringRef
V = Prefix ?
Value.first.substr(0, Key.size()) :
Value.first;
86 bool operator()(std::pair<StringRef, size_t>
Value, StringRef Key)
const {
87 StringRef
V = Prefix ?
Value.first.substr(0, Key.size()) :
Value.first;
108 case types::TY_CHeader:
111 case types::TY_ObjCHeader:
112 return types::TY_ObjC;
114 case types::TY_CXXHeader:
115 return types::TY_CXX;
116 case types::TY_ObjCXX:
117 case types::TY_ObjCXXHeader:
118 return types::TY_ObjCXX;
120 case types::TY_CUDA_DEVICE:
121 return types::TY_CUDA;
128 struct TransferableCommand {
138 TransferableCommand(CompileCommand C)
140 std::vector<std::string> OldArgs = std::move(
Cmd.CommandLine);
141 Cmd.CommandLine.clear();
144 llvm::opt::InputArgList ArgList;
148 TmpArgv.push_back(S.c_str());
151 TmpArgv.front(), llvm::makeArrayRef(TmpArgv).slice(1)));
152 ArgList = {TmpArgv.begin(), TmpArgv.end()};
160 if (!OldArgs.empty())
161 Cmd.CommandLine.emplace_back(OldArgs.front());
162 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
163 using namespace driver::options;
165 const unsigned OldPos = Pos;
166 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
174 const llvm::opt::Option &Opt = Arg->getOption();
177 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
179 Opt.matches(OPT__SLASH_Fe) ||
180 Opt.matches(OPT__SLASH_Fi) ||
181 Opt.matches(OPT__SLASH_Fo))))
185 if (Opt.matches(OPT__DASH_DASH))
189 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
195 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
201 Cmd.CommandLine.insert(
Cmd.CommandLine.end(),
202 OldArgs.data() + OldPos, OldArgs.data() + Pos);
216 CompileCommand transferTo(StringRef
Filename) && {
217 CompileCommand Result = std::move(
Cmd);
218 Result.Heuristic =
"inferred from " + Result.Filename;
221 auto TargetType = guessType(
Filename, &TypeCertain);
223 if ((!TargetType || !TypeCertain) && Type) {
231 const StringRef Flag = toCLFlag(TargetType);
235 Result.CommandLine.push_back(
"-x");
242 Result.CommandLine.emplace_back((
246 Result.CommandLine.push_back(
"--");
258 return types::TY_CXX;
260 return types::TY_ObjC;
262 return types::TY_ObjCXX;
269 static StringRef toCLFlag(
types::ID Type) {
272 case types::TY_CHeader:
275 case types::TY_CXXHeader:
284 const llvm::opt::Option &Opt = Arg.getOption();
285 using namespace driver::options;
287 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
289 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
290 return types::TY_CXX;
292 if (Opt.matches(driver::options::OPT_x))
300 using namespace driver::options;
301 if (Arg.getOption().matches(
ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
320 FileIndex(std::vector<std::string> Files)
321 : OriginalPaths(
std::move(Files)), Strings(Arena) {
323 llvm::sort(OriginalPaths);
324 Paths.reserve(OriginalPaths.size());
325 Types.reserve(OriginalPaths.size());
326 Stems.reserve(OriginalPaths.size());
327 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
328 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
330 Paths.emplace_back(Path, I);
331 Types.push_back(foldType(guessType(OriginalPaths[I])));
332 Stems.emplace_back(sys::path::stem(Path), I);
333 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
334 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
335 if (Dir->size() > ShortDirectorySegment)
336 Components.emplace_back(*Dir, I);
340 llvm::sort(Components);
343 bool empty()
const {
return Paths.empty(); }
348 StringRef chooseProxy(StringRef OriginalFilename,
350 assert(!empty() &&
"need at least one candidate!");
352 auto Candidates = scoreCandidates(
Filename);
353 std::pair<size_t, int> Best =
354 pickWinner(Candidates,
Filename, PreferLanguage);
358 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
359 <<
" as proxy for " << OriginalFilename <<
" preferring "
363 <<
" score=" << Best.second <<
"\n");
364 return OriginalPaths[Best.first];
368 using SubstringAndIndex = std::pair<StringRef, size_t>;
372 constexpr
static int DirectorySegmentsIndexed = 4;
373 constexpr
static int DirectorySegmentsQueried = 2;
374 constexpr
static int ShortDirectorySegment = 1;
378 DenseMap<size_t, int> scoreCandidates(StringRef
Filename)
const {
382 StringRef Stem = sys::path::stem(
Filename);
384 llvm::StringRef Prefix;
385 auto Dir = ++sys::path::rbegin(
Filename),
387 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
388 if (Dir->size() > ShortDirectorySegment)
389 Dirs.push_back(*Dir);
390 Prefix =
Filename.substr(0, Dir - DirEnd);
394 DenseMap<size_t, int> Candidates;
396 for (
const auto &Entry : Range)
397 Candidates[Entry.second] += Points;
401 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
402 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
405 for (StringRef Dir : Dirs)
406 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
408 if (sys::path::root_directory(Prefix) != Prefix)
409 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
415 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
418 struct ScoredCandidate {
425 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
426 for (
const auto &Candidate : Candidates) {
428 S.Index = Candidate.first;
430 PreferredLanguage == Types[S.Index];
431 S.Points = Candidate.second;
432 if (!S.Preferred && Best.Preferred)
434 if (S.Preferred == Best.Preferred) {
435 if (S.Points < Best.Points)
437 if (S.Points == Best.Points) {
438 S.PrefixLength = matchingPrefix(
Filename, Paths[S.Index].first);
439 if (S.PrefixLength < Best.PrefixLength)
442 if (S.PrefixLength == Best.PrefixLength)
443 if (S.Index > Best.Index)
449 S.PrefixLength = matchingPrefix(
Filename, Paths[S.Index].first);
454 if (Best.Index ==
size_t(-1))
455 return {longestMatch(
Filename, Paths).second, 0};
456 return {Best.Index, Best.Points};
461 template <
bool Prefix>
465 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
471 SubstringAndIndex longestMatch(StringRef Key,
473 assert(!Idx.empty());
475 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
476 if (It == Idx.begin())
481 size_t Prefix = matchingPrefix(Key, It->first);
482 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
483 return Prefix > PrevPrefix ? *It : *--It;
487 std::vector<std::string> OriginalPaths;
488 BumpPtrAllocator Arena;
492 std::vector<SubstringAndIndex> Paths;
495 std::vector<types::ID> Types;
496 std::vector<SubstringAndIndex> Stems;
497 std::vector<SubstringAndIndex> Components;
503 class InterpolatingCompilationDatabase :
public CompilationDatabase {
505 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
506 : Inner(
std::move(Inner)), Index(this->Inner->getAllFiles()) {}
508 std::vector<CompileCommand>
509 getCompileCommands(StringRef
Filename)
const override {
510 auto Known = Inner->getCompileCommands(
Filename);
511 if (Index.empty() || !Known.empty())
514 auto Lang = guessType(
Filename, &TypeCertain);
518 Inner->getCompileCommands(Index.chooseProxy(
Filename, foldType(Lang)));
519 if (ProxyCommands.empty())
524 std::vector<std::string> getAllFiles()
const override {
525 return Inner->getAllFiles();
528 std::vector<CompileCommand> getAllCompileCommands()
const override {
529 return Inner->getAllCompileCommands();
533 std::unique_ptr<CompilationDatabase> Inner;
539 std::unique_ptr<CompilationDatabase>
541 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
546 return TransferableCommand(std::move(
Cmd)).transferTo(
Filename);