Skip to content

Commit 11ee699

Browse files
committed
[clang][tooling] Accept Clang invocations with multiple jobs
When `-fno-integrated-as` is passed to the Clang driver (or set by default by a specific toolchain), it will construct an assembler job in addition to the cc1 job. Similarly, the `-fembed-bitcode` driver flag will create additional cc1 job that reads LLVM IR file. The Clang tooling library only cares about the job that reads a source file. Instead of relying on the fact that the client injected `-fsyntax-only` to the driver invocation to get a single `-cc1` invocation that reads the source file, this patch filters out such jobs from `Compilation` automatically and ignores the rest. This fixes a test failure in `ClangScanDeps/headerwithname.cpp` and `ClangScanDeps/headerwithnamefollowedbyinclude.cpp` on AIX reported here: https://reviews.llvm.org/D103461#2841918 and `clang-scan-deps` failures with `-fembed-bitcode`. Depends on D106788. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D105695
1 parent 2e27c4e commit 11ee699

File tree

4 files changed

+149
-24
lines changed

4 files changed

+149
-24
lines changed

clang/include/clang/Tooling/Tooling.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ namespace tooling {
6666

6767
class CompilationDatabase;
6868

69+
/// Retrieves the flags of the `-cc1` job in `Compilation` that has only source
70+
/// files as its inputs.
71+
/// Returns nullptr if there are no such jobs or multiple of them. Note that
72+
/// offloading jobs are ignored.
73+
const llvm::opt::ArgStringList *
74+
getCC1Arguments(DiagnosticsEngine *Diagnostics,
75+
driver::Compilation *Compilation);
76+
6977
/// Interface to process a clang::CompilerInvocation.
7078
///
7179
/// If your tool is based on FrontendAction, you should be deriving from

clang/lib/Tooling/Tooling.cpp

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,20 @@ newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName,
8383
return CompilerDriver;
8484
}
8585

86-
/// Retrieves the clang CC1 specific flags out of the compilation's jobs.
87-
///
88-
/// Returns nullptr on error.
89-
static const llvm::opt::ArgStringList *getCC1Arguments(
90-
DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation) {
91-
// We expect to get back exactly one Command job, if we didn't something
92-
// failed. Extract that job from the Compilation.
86+
/// Decide whether extra compiler frontend commands can be ignored.
87+
static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) {
9388
const driver::JobList &Jobs = Compilation->getJobs();
9489
const driver::ActionList &Actions = Compilation->getActions();
90+
9591
bool OffloadCompilation = false;
92+
93+
// Jobs and Actions look very different depending on whether the Clang tool
94+
// injected -fsyntax-only or not. Try to handle both cases here.
95+
96+
for (const auto &Job : Jobs)
97+
if (StringRef(Job.getExecutable()) == "clang-offload-bundler")
98+
OffloadCompilation = true;
99+
96100
if (Jobs.size() > 1) {
97101
for (auto A : Actions){
98102
// On MacOSX real actions may end up being wrapped in BindArchAction
@@ -117,8 +121,33 @@ static const llvm::opt::ArgStringList *getCC1Arguments(
117121
}
118122
}
119123
}
120-
if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) ||
121-
(Jobs.size() > 1 && !OffloadCompilation)) {
124+
125+
return OffloadCompilation;
126+
}
127+
128+
namespace clang {
129+
namespace tooling {
130+
131+
const llvm::opt::ArgStringList *
132+
getCC1Arguments(DiagnosticsEngine *Diagnostics,
133+
driver::Compilation *Compilation) {
134+
const driver::JobList &Jobs = Compilation->getJobs();
135+
136+
auto IsCC1Command = [](const driver::Command &Cmd) {
137+
return StringRef(Cmd.getCreator().getName()) == "clang";
138+
};
139+
140+
auto IsSrcFile = [](const driver::InputInfo &II) {
141+
return isSrcFile(II.getType());
142+
};
143+
144+
llvm::SmallVector<const driver::Command *, 1> CC1Jobs;
145+
for (const driver::Command &Job : Jobs)
146+
if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
147+
CC1Jobs.push_back(&Job);
148+
149+
if (CC1Jobs.empty() ||
150+
(CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) {
122151
SmallString<256> error_msg;
123152
llvm::raw_svector_ostream error_stream(error_msg);
124153
Jobs.Print(error_stream, "; ", true);
@@ -127,19 +156,9 @@ static const llvm::opt::ArgStringList *getCC1Arguments(
127156
return nullptr;
128157
}
129158

130-
// The one job we find should be to invoke clang again.
131-
const auto &Cmd = cast<driver::Command>(*Jobs.begin());
132-
if (StringRef(Cmd.getCreator().getName()) != "clang") {
133-
Diagnostics->Report(diag::err_fe_expected_clang_command);
134-
return nullptr;
135-
}
136-
137-
return &Cmd.getArguments();
159+
return &CC1Jobs[0]->getArguments();
138160
}
139161

140-
namespace clang {
141-
namespace tooling {
142-
143162
/// Returns a clang build invocation initialized from the CC1 flags.
144163
CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,
145164
const llvm::opt::ArgStringList &CC1Args,

clang/test/Tooling/clang-check-offload.cpp

Lines changed: 0 additions & 4 deletions
This file was deleted.

clang/unittests/Tooling/ToolingTest.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include "clang/AST/ASTConsumer.h"
1010
#include "clang/AST/DeclCXX.h"
1111
#include "clang/AST/DeclGroup.h"
12+
#include "clang/Driver/Compilation.h"
13+
#include "clang/Driver/Driver.h"
1214
#include "clang/Frontend/ASTUnit.h"
1315
#include "clang/Frontend/CompilerInstance.h"
1416
#include "clang/Frontend/FrontendAction.h"
@@ -18,6 +20,7 @@
1820
#include "clang/Tooling/Tooling.h"
1921
#include "llvm/ADT/STLExtras.h"
2022
#include "llvm/ADT/StringRef.h"
23+
#include "llvm/Support/Host.h"
2124
#include "llvm/Support/Path.h"
2225
#include "llvm/Support/TargetRegistry.h"
2326
#include "llvm/Support/TargetSelect.h"
@@ -258,6 +261,105 @@ TEST(ToolInvocation, DiagConsumerExpectingSourceManager) {
258261
EXPECT_TRUE(Consumer.SawSourceManager);
259262
}
260263

264+
namespace {
265+
/// Overlays the real filesystem with the given VFS and returns the result.
266+
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
267+
overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
268+
auto RFS = llvm::vfs::getRealFileSystem();
269+
auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(RFS);
270+
OverlayFS->pushOverlay(VFS);
271+
return OverlayFS;
272+
}
273+
274+
struct CommandLineExtractorTest : public ::testing::Test {
275+
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS;
276+
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
277+
driver::Driver Driver;
278+
279+
public:
280+
CommandLineExtractorTest()
281+
: InMemoryFS(new llvm::vfs::InMemoryFileSystem),
282+
Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)),
283+
Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags,
284+
"clang LLVM compiler", overlayRealFS(InMemoryFS)) {}
285+
286+
void addFile(StringRef Name, StringRef Content) {
287+
InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content));
288+
}
289+
290+
const llvm::opt::ArgStringList *
291+
extractCC1Arguments(llvm::ArrayRef<const char *> Argv) {
292+
const std::unique_ptr<driver::Compilation> Compilation(
293+
Driver.BuildCompilation(llvm::makeArrayRef(Argv)));
294+
295+
return getCC1Arguments(Diags.get(), Compilation.get());
296+
}
297+
};
298+
} // namespace
299+
300+
TEST_F(CommandLineExtractorTest, AcceptOffloading) {
301+
addFile("test.c", "int main() {}\n");
302+
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
303+
"-x", "hip", "test.c",
304+
"-nogpulib", "-nogpuinc"};
305+
EXPECT_NE(extractCC1Arguments(Args), nullptr);
306+
}
307+
308+
TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) {
309+
addFile("test.c", "int main() {}\n");
310+
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
311+
"-c", "-x", "hip",
312+
"test.c", "-nogpulib", "-nogpuinc"};
313+
EXPECT_NE(extractCC1Arguments(Args), nullptr);
314+
}
315+
316+
TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) {
317+
addFile("test.c", "int main() {}\n");
318+
const char *Args[] = {
319+
"clang", "-target", "arm64-apple-macosx11.0.0",
320+
"-fsyntax-only", "-x", "hip",
321+
"test.c", "-nogpulib", "-nogpuinc"};
322+
EXPECT_NE(extractCC1Arguments(Args), nullptr);
323+
}
324+
325+
TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) {
326+
addFile("test.c", "int main() {}\n");
327+
const char *Args[] = {
328+
"clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as",
329+
"-c", "test.c"};
330+
EXPECT_NE(extractCC1Arguments(Args), nullptr);
331+
}
332+
333+
TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) {
334+
addFile("test.c", "int main() {}\n");
335+
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
336+
"-c", "-fembed-bitcode", "test.c"};
337+
EXPECT_NE(extractCC1Arguments(Args), nullptr);
338+
}
339+
340+
TEST_F(CommandLineExtractorTest, AcceptSaveTemps) {
341+
addFile("test.c", "int main() {}\n");
342+
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
343+
"-c", "-save-temps", "test.c"};
344+
EXPECT_NE(extractCC1Arguments(Args), nullptr);
345+
}
346+
347+
TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) {
348+
addFile("test.c", "int main() {}\n");
349+
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
350+
"-arch", "x86_64", "-arch",
351+
"arm64", "-c", "test.c"};
352+
EXPECT_EQ(extractCC1Arguments(Args), nullptr);
353+
}
354+
355+
TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) {
356+
addFile("one.c", "void one() {}\n");
357+
addFile("two.c", "void two() {}\n");
358+
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
359+
"-c", "one.c", "two.c"};
360+
EXPECT_EQ(extractCC1Arguments(Args), nullptr);
361+
}
362+
261363
struct VerifyEndCallback : public SourceFileCallbacks {
262364
VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
263365
bool handleBeginSource(CompilerInstance &CI) override {

0 commit comments

Comments
 (0)