1*45ef055dSKadir Cetinkaya //===- ExpandResponseFileCompilationDataBase.cpp --------------------------===//
2*45ef055dSKadir Cetinkaya //
3*45ef055dSKadir Cetinkaya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*45ef055dSKadir Cetinkaya // See https://llvm.org/LICENSE.txt for license information.
5*45ef055dSKadir Cetinkaya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*45ef055dSKadir Cetinkaya //
7*45ef055dSKadir Cetinkaya //===----------------------------------------------------------------------===//
8*45ef055dSKadir Cetinkaya 
9*45ef055dSKadir Cetinkaya #include "clang/Tooling/CompilationDatabase.h"
10*45ef055dSKadir Cetinkaya #include "llvm/ADT/Triple.h"
11*45ef055dSKadir Cetinkaya #include "llvm/Support/CommandLine.h"
12*45ef055dSKadir Cetinkaya #include "llvm/Support/ConvertUTF.h"
13*45ef055dSKadir Cetinkaya #include "llvm/Support/ErrorOr.h"
14*45ef055dSKadir Cetinkaya #include "llvm/Support/MemoryBuffer.h"
15*45ef055dSKadir Cetinkaya #include "llvm/Support/Path.h"
16*45ef055dSKadir Cetinkaya #include "llvm/Support/StringSaver.h"
17*45ef055dSKadir Cetinkaya 
18*45ef055dSKadir Cetinkaya namespace clang {
19*45ef055dSKadir Cetinkaya namespace tooling {
20*45ef055dSKadir Cetinkaya namespace {
21*45ef055dSKadir Cetinkaya 
22*45ef055dSKadir Cetinkaya class ExpandResponseFilesDatabase : public CompilationDatabase {
23*45ef055dSKadir Cetinkaya public:
24*45ef055dSKadir Cetinkaya   ExpandResponseFilesDatabase(
25*45ef055dSKadir Cetinkaya       std::unique_ptr<CompilationDatabase> Base,
26*45ef055dSKadir Cetinkaya       llvm::cl::TokenizerCallback Tokenizer,
27*45ef055dSKadir Cetinkaya       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
28*45ef055dSKadir Cetinkaya       : Base(std::move(Base)), Tokenizer(Tokenizer), FS(std::move(FS)) {
29*45ef055dSKadir Cetinkaya     assert(this->Base != nullptr);
30*45ef055dSKadir Cetinkaya     assert(this->Tokenizer != nullptr);
31*45ef055dSKadir Cetinkaya     assert(this->FS != nullptr);
32*45ef055dSKadir Cetinkaya   }
33*45ef055dSKadir Cetinkaya 
34*45ef055dSKadir Cetinkaya   std::vector<std::string> getAllFiles() const override {
35*45ef055dSKadir Cetinkaya     return Base->getAllFiles();
36*45ef055dSKadir Cetinkaya   }
37*45ef055dSKadir Cetinkaya 
38*45ef055dSKadir Cetinkaya   std::vector<CompileCommand>
39*45ef055dSKadir Cetinkaya   getCompileCommands(StringRef FilePath) const override {
40*45ef055dSKadir Cetinkaya     return expand(Base->getCompileCommands(FilePath));
41*45ef055dSKadir Cetinkaya   }
42*45ef055dSKadir Cetinkaya 
43*45ef055dSKadir Cetinkaya   std::vector<CompileCommand> getAllCompileCommands() const override {
44*45ef055dSKadir Cetinkaya     return expand(Base->getAllCompileCommands());
45*45ef055dSKadir Cetinkaya   }
46*45ef055dSKadir Cetinkaya 
47*45ef055dSKadir Cetinkaya private:
48*45ef055dSKadir Cetinkaya   std::vector<CompileCommand> expand(std::vector<CompileCommand> Cmds) const {
49*45ef055dSKadir Cetinkaya     for (auto &Cmd : Cmds) {
50*45ef055dSKadir Cetinkaya       // FIXME: we should rather propagate the current directory into
51*45ef055dSKadir Cetinkaya       // ExpandResponseFiles as well in addition to FS.
52*45ef055dSKadir Cetinkaya       if (std::error_code EC = FS->setCurrentWorkingDirectory(Cmd.Directory)) {
53*45ef055dSKadir Cetinkaya         llvm::consumeError(llvm::errorCodeToError(EC));
54*45ef055dSKadir Cetinkaya         continue;
55*45ef055dSKadir Cetinkaya       }
56*45ef055dSKadir Cetinkaya       bool SeenRSPFile = false;
57*45ef055dSKadir Cetinkaya       llvm::SmallVector<const char *, 20> Argv;
58*45ef055dSKadir Cetinkaya       Argv.reserve(Cmd.CommandLine.size());
59*45ef055dSKadir Cetinkaya       for (auto &Arg : Cmd.CommandLine) {
60*45ef055dSKadir Cetinkaya         Argv.push_back(Arg.c_str());
61*45ef055dSKadir Cetinkaya         SeenRSPFile |= Arg.front() == '@';
62*45ef055dSKadir Cetinkaya       }
63*45ef055dSKadir Cetinkaya       if (!SeenRSPFile)
64*45ef055dSKadir Cetinkaya         continue;
65*45ef055dSKadir Cetinkaya       llvm::BumpPtrAllocator Alloc;
66*45ef055dSKadir Cetinkaya       llvm::StringSaver Saver(Alloc);
67*45ef055dSKadir Cetinkaya       llvm::cl::ExpandResponseFiles(Saver, Tokenizer, Argv, false, false, *FS);
68*45ef055dSKadir Cetinkaya       Cmd.CommandLine.assign(Argv.begin(), Argv.end());
69*45ef055dSKadir Cetinkaya     }
70*45ef055dSKadir Cetinkaya     return Cmds;
71*45ef055dSKadir Cetinkaya   }
72*45ef055dSKadir Cetinkaya 
73*45ef055dSKadir Cetinkaya private:
74*45ef055dSKadir Cetinkaya   std::unique_ptr<CompilationDatabase> Base;
75*45ef055dSKadir Cetinkaya   llvm::cl::TokenizerCallback Tokenizer;
76*45ef055dSKadir Cetinkaya   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
77*45ef055dSKadir Cetinkaya };
78*45ef055dSKadir Cetinkaya 
79*45ef055dSKadir Cetinkaya } // namespace
80*45ef055dSKadir Cetinkaya 
81*45ef055dSKadir Cetinkaya std::unique_ptr<CompilationDatabase>
82*45ef055dSKadir Cetinkaya expandResponseFiles(std::unique_ptr<CompilationDatabase> Base,
83*45ef055dSKadir Cetinkaya                     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
84*45ef055dSKadir Cetinkaya   auto Tokenizer = llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows()
85*45ef055dSKadir Cetinkaya                        ? llvm::cl::TokenizeWindowsCommandLine
86*45ef055dSKadir Cetinkaya                        : llvm::cl::TokenizeGNUCommandLine;
87*45ef055dSKadir Cetinkaya   return std::make_unique<ExpandResponseFilesDatabase>(
88*45ef055dSKadir Cetinkaya       std::move(Base), Tokenizer, std::move(FS));
89*45ef055dSKadir Cetinkaya }
90*45ef055dSKadir Cetinkaya 
91*45ef055dSKadir Cetinkaya } // namespace tooling
92*45ef055dSKadir Cetinkaya } // namespace clang
93