Skip to content

Commit e4585ff

Browse files
committed
[clang][deps] Handle response files in dep scanner
Extract the code the driver uses to expand response files and reuse it in the dependency scanner. rdar://106155880 Differential Revision: https://reviews.llvm.org/D145838 (cherry picked from commit fcab930) Conflicts: clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/Driver/Driver.h clang/lib/Driver/Driver.cpp clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp clang/tools/driver/driver.cpp
1 parent 84ec9db commit e4585ff

File tree

6 files changed

+137
-46
lines changed

6 files changed

+137
-46
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,4 +704,7 @@ def warn_drv_sarif_format_unstable : Warning<
704704

705705
def err_drv_riscv_unsupported_with_linker_relaxation : Error<
706706
"%0 is unsupported with RISC-V linker relaxation (-mrelax)">;
707+
708+
def err_drv_expand_response_file : Error<
709+
"failed to expand response file: %0">;
707710
}

clang/include/clang/Driver/Driver.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,16 @@ bool IsClangCL(StringRef DriverMode);
788788
/// Checks whether the value produced by getDriverMode is for 'cache' mode.
789789
bool isClangCache(StringRef DriverMode);
790790

791+
/// Expand response files from a clang driver or cc1 invocation.
792+
///
793+
/// \param Args The arguments that will be expanded.
794+
/// \param ClangCLMode Whether clang is in CL mode.
795+
/// \param Alloc Allocator for new arguments.
796+
/// \param FS Filesystem to use when expanding files.
797+
llvm::Error expandResponseFiles(SmallVectorImpl<const char *> &Args,
798+
bool ClangCLMode, llvm::BumpPtrAllocator &Alloc,
799+
llvm::vfs::FileSystem *FS = nullptr);
800+
791801
} // end namespace driver
792802
} // end namespace clang
793803

clang/lib/Driver/Driver.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6340,3 +6340,58 @@ bool driver::IsClangCL(StringRef DriverMode) { return DriverMode.equals("cl"); }
63406340
bool driver::isClangCache(StringRef DriverMode) {
63416341
return DriverMode == "cache";
63426342
}
6343+
6344+
llvm::Error driver::expandResponseFiles(SmallVectorImpl<const char *> &Args,
6345+
bool ClangCLMode,
6346+
llvm::BumpPtrAllocator &Alloc,
6347+
llvm::vfs::FileSystem *FS) {
6348+
// Parse response files using the GNU syntax, unless we're in CL mode. There
6349+
// are two ways to put clang in CL compatibility mode: ProgName is either
6350+
// clang-cl or cl, or --driver-mode=cl is on the command line. The normal
6351+
// command line parsing can't happen until after response file parsing, so we
6352+
// have to manually search for a --driver-mode=cl argument the hard way.
6353+
// Finally, our -cc1 tools don't care which tokenization mode we use because
6354+
// response files written by clang will tokenize the same way in either mode.
6355+
enum { Default, POSIX, Windows } RSPQuoting = Default;
6356+
for (const char *F : Args) {
6357+
if (strcmp(F, "--rsp-quoting=posix") == 0)
6358+
RSPQuoting = POSIX;
6359+
else if (strcmp(F, "--rsp-quoting=windows") == 0)
6360+
RSPQuoting = Windows;
6361+
}
6362+
6363+
// Determines whether we want nullptr markers in Args to indicate response
6364+
// files end-of-lines. We only use this for the /LINK driver argument with
6365+
// clang-cl.exe on Windows.
6366+
bool MarkEOLs = ClangCLMode;
6367+
6368+
llvm::cl::TokenizerCallback Tokenizer;
6369+
if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode))
6370+
Tokenizer = &llvm::cl::TokenizeWindowsCommandLine;
6371+
else
6372+
Tokenizer = &llvm::cl::TokenizeGNUCommandLine;
6373+
6374+
if (MarkEOLs && Args.size() > 1 && StringRef(Args[1]).startswith("-cc1"))
6375+
MarkEOLs = false;
6376+
6377+
llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
6378+
ECtx.setMarkEOLs(MarkEOLs);
6379+
if (FS)
6380+
ECtx.setVFS(FS);
6381+
6382+
// Note: on this branch the driver expects us to *not* propagate any errors.
6383+
ECtx.expandResponseFiles(Args);
6384+
6385+
// If -cc1 came from a response file, remove the EOL sentinels.
6386+
auto FirstArg = llvm::find_if(llvm::drop_begin(Args),
6387+
[](const char *A) { return A != nullptr; });
6388+
if (FirstArg != Args.end() && StringRef(*FirstArg).startswith("-cc1")) {
6389+
// If -cc1 came from a response file, remove the EOL sentinels.
6390+
if (MarkEOLs) {
6391+
auto newEnd = std::remove(Args.begin(), Args.end(), nullptr);
6392+
Args.resize(newEnd - Args.begin());
6393+
}
6394+
}
6395+
6396+
return llvm::Error::success();
6397+
}

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
1010
#include "clang/Basic/DiagnosticCAS.h"
11+
#include "clang/Basic/DiagnosticDriver.h"
1112
#include "clang/Basic/DiagnosticFrontend.h"
1213
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
1314
#include "clang/Driver/Compilation.h"
@@ -27,6 +28,8 @@
2728
#include "clang/Tooling/Tooling.h"
2829
#include "llvm/CAS/CachingOnDiskFileSystem.h"
2930
#include "llvm/CAS/ObjectStore.h"
31+
#include "llvm/Support/Allocator.h"
32+
#include "llvm/Support/Error.h"
3033
#include "llvm/Support/Host.h"
3134
#include "llvm/Support/PrefixMapper.h"
3235

@@ -619,16 +622,29 @@ llvm::Error DependencyScanningWorker::computeDependencies(
619622
}
620623

621624
static bool forEachDriverJob(
622-
ArrayRef<std::string> Args, DiagnosticsEngine &Diags, FileManager &FM,
625+
ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,
623626
llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
627+
SmallVector<const char *, 256> Argv;
628+
Argv.reserve(ArgStrs.size());
629+
for (const std::string &Arg : ArgStrs)
630+
Argv.push_back(Arg.c_str());
631+
632+
llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
633+
624634
std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
625-
Args[0], llvm::sys::getDefaultTargetTriple(), Diags,
626-
"clang LLVM compiler", &FM.getVirtualFileSystem());
635+
Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
636+
"clang LLVM compiler", FS);
627637
Driver->setTitle("clang_based_tool");
628638

629-
std::vector<const char *> Argv;
630-
for (const std::string &Arg : Args)
631-
Argv.push_back(Arg.c_str());
639+
llvm::BumpPtrAllocator Alloc;
640+
bool CLMode = driver::IsClangCL(
641+
driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
642+
643+
if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {
644+
Diags.Report(diag::err_drv_expand_response_file)
645+
<< llvm::toString(std::move(E));
646+
return false;
647+
}
632648

633649
const std::unique_ptr<driver::Compilation> Compilation(
634650
Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Check that the scanner can handle a response file input.
2+
3+
// RUN: rm -rf %t
4+
// RUN: split-file %s %t
5+
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
6+
7+
// RUN: clang-scan-deps -format experimental-full -compilation-database %t/cdb.json > %t/deps.json
8+
9+
// RUN: cat %t/deps.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s
10+
11+
// CHECK: "command-line": [
12+
// CHECK: "-fsyntax-only"
13+
// CHECK: "-x"
14+
// CHECK-NEXT: "c"
15+
// CHECK: "tu.c"
16+
// CHECK: "-I"
17+
// CHECK-NEXT: "include"
18+
// CHECK: ],
19+
// CHECK: "file-deps": [
20+
// CHECK-NEXT: "[[PREFIX]]/tu.c"
21+
// CHECK-NEXT: "[[PREFIX]]/include/header.h"
22+
// CHECK-NEXT: ]
23+
24+
//--- cdb.json.template
25+
[{
26+
"file": "DIR/t.c",
27+
"directory": "DIR",
28+
"command": "clang @DIR/args.txt"
29+
}]
30+
31+
//--- args.txt
32+
@args_nested.txt
33+
-fsyntax-only tu.c
34+
35+
//--- args_nested.txt
36+
-I include
37+
38+
//--- include/header.h
39+
40+
//--- tu.c
41+
#include "header.h"

clang/tools/driver/driver.cpp

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -411,50 +411,16 @@ int clang_main(int Argc, char **Argv) {
411411
return *ExitCode;
412412
}
413413

414-
// Parse response files using the GNU syntax, unless we're in CL mode. There
415-
// are two ways to put clang in CL compatibility mode: Args[0] is either
416-
// clang-cl or cl, or --driver-mode=cl is on the command line. The normal
417-
// command line parsing can't happen until after response file parsing, so we
418-
// have to manually search for a --driver-mode=cl argument the hard way.
419-
// Finally, our -cc1 tools don't care which tokenization mode we use because
420-
// response files written by clang will tokenize the same way in either mode.
421414
bool ClangCLMode = IsClangCL(DriverMode);
422-
enum { Default, POSIX, Windows } RSPQuoting = Default;
423-
for (const char *F : Args) {
424-
if (strcmp(F, "--rsp-quoting=posix") == 0)
425-
RSPQuoting = POSIX;
426-
else if (strcmp(F, "--rsp-quoting=windows") == 0)
427-
RSPQuoting = Windows;
415+
416+
if (llvm::Error Err = expandResponseFiles(Args, ClangCLMode, A)) {
417+
llvm::errs() << toString(std::move(Err)) << '\n';
418+
return 1;
428419
}
429420

430-
// Determines whether we want nullptr markers in Args to indicate response
431-
// files end-of-lines. We only use this for the /LINK driver argument with
432-
// clang-cl.exe on Windows.
433-
bool MarkEOLs = ClangCLMode;
434-
435-
llvm::cl::TokenizerCallback Tokenizer;
436-
if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode))
437-
Tokenizer = &llvm::cl::TokenizeWindowsCommandLine;
438-
else
439-
Tokenizer = &llvm::cl::TokenizeGNUCommandLine;
440-
441-
if (MarkEOLs && Args.size() > 1 && StringRef(Args[1]).startswith("-cc1"))
442-
MarkEOLs = false;
443-
llvm::cl::ExpansionContext ECtx(A, Tokenizer);
444-
ECtx.setMarkEOLs(MarkEOLs).expandResponseFiles(Args);
445-
446-
// Handle -cc1 integrated tools, even if -cc1 was expanded from a response
447-
// file.
448-
auto FirstArg = llvm::find_if(llvm::drop_begin(Args),
449-
[](const char *A) { return A != nullptr; });
450-
if (FirstArg != Args.end() && StringRef(*FirstArg).startswith("-cc1")) {
451-
// If -cc1 came from a response file, remove the EOL sentinels.
452-
if (MarkEOLs) {
453-
auto newEnd = std::remove(Args.begin(), Args.end(), nullptr);
454-
Args.resize(newEnd - Args.begin());
455-
}
421+
// Handle -cc1 integrated tools.
422+
if (Args.size() >= 2 && StringRef(Args[1]).startswith("-cc1"))
456423
return ExecuteCC1Tool(Args);
457-
}
458424

459425
// Handle options that need handling before the real command line parsing in
460426
// Driver::BuildCompilation()

0 commit comments

Comments
 (0)