clang: lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

27#include "llvm/ADT/ScopeExit.h"

28#include "llvm/Support/Allocator.h"

29#include "llvm/Support/Error.h"

30#include "llvm/TargetParser/Host.h"

31#include

32

33using namespace clang;

34using namespace tooling;

35using namespace dependencies;

36

37namespace {

38

39

41public:

42 DependencyConsumerForwarder(std::unique_ptr Opts,

45 Opts(std::move(Opts)), C(C) {}

46

48 C.handleDependencyOutputOpts(*Opts);

51 CanonPath = File;

52 llvm::sys::path::remove_dots(CanonPath, true);

53 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);

54 C.handleFileDependency(CanonPath);

55 }

56 }

57

58private:

59 StringRef WorkingDirectory;

60 std::unique_ptr Opts;

62};

63

68 if (LangOpts.Modules) {

70 if (Diags) {

71 Diags->Report(diag::warn_pch_vfsoverlay_mismatch);

73 if (VFSOverlays.empty()) {

74 Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;

75 } else {

76 std::string Files = llvm::join(VFSOverlays, "\n");

77 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;

78 }

79 };

82 }

83 }

84 }

85 return false;

86}

87

89

90

91

93public:

94 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,

99 : PrebuiltModuleFiles(PrebuiltModuleFiles),

100 NewModuleFiles(NewModuleFiles),

101 PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),

102 ExistingLangOpts(LangOpts), Diags(Diags) {}

103

105

107 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)

108 NewModuleFiles.push_back(Filename.str());

109 }

110

114 }

115

117 bool Complain) override {

118 std::vectorstd::string VFSOverlayFiles = HSOpts.VFSOverlayFiles;

119 PrebuiltModuleVFSMap.insert(

120 {CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});

121 return checkHeaderSearchPaths(

122 HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);

123 }

124

125private:

126 PrebuiltModuleFilesT &PrebuiltModuleFiles;

132 std::string CurrentFile;

133};

134

135

136

137static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,

139 PrebuiltModuleFilesT &ModuleFiles,

142

144 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,

146 Diags);

147

148 Listener.visitModuleFile(PrebuiltModuleFilename,

153 false, Listener,

155 return true;

156

157 while (!Worklist.empty()) {

162 false, Listener,

163 false))

164 return true;

165 }

166 return false;

167}

168

169

170static std::string makeObjFileName(StringRef FileName) {

172 llvm::sys::path::replace_extension(ObjFileName, "o");

173 return std::string(ObjFileName);

174}

175

176

177static std::string

178deduceDepTarget(const std::string &OutputFile,

180 if (OutputFile != "-")

181 return OutputFile;

182

183 if (InputFiles.empty() || !InputFiles.front().isFile())

184 return "clang-scan-deps\\ dependency";

185

186 return makeObjFileName(InputFiles.front().getFile());

187}

188

189

191

192 DiagOpts.ShowCarets = false;

193

195

196

197

198

199

200

201

202 llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {

203 return llvm::StringSwitch(Warning)

204 .Cases("pch-vfs-diff", "error=pch-vfs-diff", false)

205 .StartsWith("no-error=", false)

206 .Default(true);

207 });

208}

209

210

211

212

213

214

215

216

217

218

219

220static std::optional getSimpleMacroName(StringRef Macro) {

221 StringRef Name = Macro.split("=").first.ltrim(" \t");

222 std::size_t I = 0;

223

224 auto FinishName = [&]() -> std::optional {

225 StringRef SimpleName = Name.slice(0, I);

226 if (SimpleName.empty())

227 return std::nullopt;

228 return SimpleName;

229 };

230

231 for (; I != Name.size(); ++I) {

232 switch (Name[I]) {

233 case '(':

234 case ' ':

235 case '\t':

236 return FinishName();

237 case '_':

238 continue;

239 default:

240 if (llvm::isAlnum(Name[I]))

241 continue;

242 return std::nullopt;

243 }

244 }

245 return FinishName();

246}

247

249 using MacroOpt = std::pair<StringRef, std::size_t>;

250 std::vector SimpleNames;

251 SimpleNames.reserve(PPOpts.Macros.size());

252 std::size_t Index = 0;

253 for (const auto &M : PPOpts.Macros) {

254 auto SName = getSimpleMacroName(M.first);

255

256 if (!SName)

257 return;

258 SimpleNames.emplace_back(*SName, Index);

259 ++Index;

260 }

261

262 llvm::stable_sort(SimpleNames, llvm::less_first());

263

264 auto NewEnd = std::unique(

265 SimpleNames.rbegin(), SimpleNames.rend(),

266 [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });

267 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());

268

269

270 decltype(PPOpts.Macros) NewMacros;

271 NewMacros.reserve(SimpleNames.size());

272 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {

273 std::size_t OriginalIndex = SimpleNames[I].second;

274

275 NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));

276 }

277 std::swap(PPOpts.Macros, NewMacros);

278}

279

280

281

283public:

284 DependencyScanningAction(

289 bool EagerLoadModules, bool DisableFree,

290 std::optional ModuleName = std::nullopt)

291 : WorkingDirectory(WorkingDirectory), Consumer(Consumer),

292 Controller(Controller), DepFS(std::move(DepFS)), Format(Format),

293 OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),

294 DisableFree(DisableFree), ModuleName(ModuleName) {}

295

296 bool runInvocation(std::shared_ptr Invocation,

298 std::shared_ptr PCHContainerOps,

300

302

303 OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;

304 if (any(OptimizeArgs & ScanningOptimizations::Macros))

305 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());

306

307 if (Scanned) {

308

309

310

311

312 setLastCC1Arguments(std::move(OriginalInvocation));

313 return true;

314 }

315

316 Scanned = true;

317

318

319 ScanInstanceStorage.emplace(std::move(PCHContainerOps));

321 ScanInstance.setInvocation(std::move(Invocation));

322

323

326 DiagConsumer, false);

328 return false;

329

330

331 auto DiagConsumerFinisher =

332 llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });

333

335 true;

336

339

340

344 any(OptimizeArgs & ScanningOptimizations::VFS);

345

346

350

351

352 if (DepFS) {

353 StringRef ModulesCachePath =

355

356 DepFS->resetBypassedPathPrefix();

357 if (!ModulesCachePath.empty())

358 DepFS->setBypassedPathPrefix(ModulesCachePath);

359

363 if (llvm::ErrorOr Entry =

364 LocalDepFS->getOrCreateFileSystemEntry(File.getName()))

365 if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))

366 return Entry->getDirectiveTokens();

367 return std::nullopt;

368 };

369 }

370

371

374

375

376

377

380 if (visitPrebuiltModule(

382 ScanInstance,

384 PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))

385 return false;

386

387

388

389

390

391

392

393

394 auto Opts = std::make_unique();

396

397

398 if (Opts->Targets.empty())

399 Opts->Targets = {

402 Opts->IncludeSystemHeaders = true;

403

404 switch (Format) {

405 case ScanningOutputFormat::Make:

407 std::make_shared(

408 std::move(Opts), WorkingDirectory, Consumer));

409 break;

410 case ScanningOutputFormat::P1689:

411 case ScanningOutputFormat::Full:

412 MDC = std::make_shared(

413 std::move(Opts), ScanInstance, Consumer, Controller,

414 OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,

415 EagerLoadModules, Format == ScanningOutputFormat::P1689);

417 break;

418 }

419

420

421

422

423

424

430 true;

431

432

434

435 std::unique_ptr Action;

436

437 if (Format == ScanningOutputFormat::P1689)

438 Action = std::make_unique();

439 else if (ModuleName)

440 Action = std::make_unique(*ModuleName);

441 else

442 Action = std::make_unique();

443

445 return false;

446

447

448 DiagConsumerFinisher.release();

449 const bool Result = ScanInstance.ExecuteAction(*Action);

450

451 if (Result)

452 setLastCC1Arguments(std::move(OriginalInvocation));

453

454

456

458 }

459

460 bool hasScanned() const { return Scanned; }

461

462

463

464

465 std::vectorstd::string takeLastCC1Arguments() {

466 std::vectorstd::string Result;

467 std::swap(Result, LastCC1Arguments);

469 }

470

471private:

473 if (MDC)

474 MDC->applyDiscoveredDependencies(CI);

475 LastCC1Arguments = CI.getCC1CommandLine();

476 }

477

478private:

479 StringRef WorkingDirectory;

485 bool EagerLoadModules;

486 bool DisableFree;

487 std::optional ModuleName;

488 std::optional ScanInstanceStorage;

489 std::shared_ptr MDC;

490 std::vectorstd::string LastCC1Arguments;

491 bool Scanned = false;

492};

493

494}

495

499 : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),

500 EagerLoadModules(Service.shouldEagerLoadModules()) {

501 PCHContainerOps = std::make_shared();

502

503 PCHContainerOps->registerReader(

504 std::make_unique());

505

506 PCHContainerOps->registerWriter(std::make_unique());

507

509 FS = llvm::makeIntrusiveRefCntllvm::vfs::TracingFileSystem(std::move(FS));

510

511 switch (Service.getMode()) {

513 DepFS =

515 BaseFS = DepFS;

516 break;

518 DepFS = nullptr;

519 BaseFS = FS;

520 break;

521 }

522}

523

525 StringRef WorkingDirectory, const std::vectorstd::string &CommandLine,

527 std::optional ModuleName) {

528 std::vector<const char *> CLI;

529 for (const std::string &Arg : CommandLine)

530 CLI.push_back(Arg.c_str());

532 sanitizeDiagOpts(*DiagOpts);

533

534

535

536 std::string DiagnosticOutput;

537 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);

539

540 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,

541 DiagPrinter, ModuleName))

542 return llvm::Error::success();

543 return llvm::make_errorllvm::StringError(DiagnosticsOS.str(),

544 llvm::inconvertibleErrorCode());

545}

546

551 Argv.reserve(ArgStrs.size());

552 for (const std::string &Arg : ArgStrs)

553 Argv.push_back(Arg.c_str());

554

556

557 std::unique_ptrdriver::Driver Driver = std::make_uniquedriver::Driver(

558 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,

559 "clang LLVM compiler", FS);

560 Driver->setTitle("clang_based_tool");

561

562 llvm::BumpPtrAllocator Alloc;

565

567 Diags.Report(diag::err_drv_expand_response_file)

568 << llvm::toString(std::move(E));

569 return false;

570 }

571

572 const std::unique_ptrdriver::Compilation Compilation(

574 if (!Compilation)

575 return false;

576

577 if (Compilation->containsError())

578 return false;

579

580 for (const driver::Command &Job : Compilation->getJobs()) {

581 if (!Callback(Job))

582 return false;

583 }

584 return true;

585}

586

588 std::vectorstd::string CommandLine, DependencyScanningAction &Action,

590 std::shared_ptrclang::PCHContainerOperations &PCHContainerOps,

592

593

594 std::string Executable = CommandLine[0];

595 ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,

596 PCHContainerOps);

599 if (!Invocation.run())

600 return false;

601

602 std::vectorstd::string Args = Action.takeLastCC1Arguments();

603 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});

604 return true;

605}

606

608 StringRef WorkingDirectory, const std::vectorstd::string &CommandLine,

611

612 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);

613

614 std::optional<std::vectorstd::string> ModifiedCommandLine;

616

617

618

619

620 if (ModuleName) {

621 auto OverlayFS =

622 llvm::makeIntrusiveRefCntllvm::vfs::OverlayFileSystem(BaseFS);

623 auto InMemoryFS =

624 llvm::makeIntrusiveRefCntllvm::vfs::InMemoryFileSystem();

625 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);

626 OverlayFS->pushOverlay(InMemoryFS);

627 ModifiedFS = OverlayFS;

628

630

631 llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",

632 FakeInputPath,

633 false);

634 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));

635

636 ModifiedCommandLine = CommandLine;

637 ModifiedCommandLine->emplace_back(FakeInputPath);

638 }

639

640 const std::vectorstd::string &FinalCommandLine =

641 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;

642 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;

643

644 auto FileMgr =

645 llvm::makeIntrusiveRefCnt(FileSystemOptions{}, FinalFS);

646

647 std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);

648 llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),

649 [](const std::string &Str) { return Str.c_str(); });

650

652 sanitizeDiagOpts(*DiagOpts);

655 false);

656

657

658

660 Diags->setSourceManager(&SrcMgr);

661

662

663

664 bool DisableFree = true;

665 DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,

666 Format, OptimizeArgs, EagerLoadModules,

667 DisableFree, ModuleName);

668

670 if (FinalCommandLine[1] == "-cc1") {

672 PCHContainerOps, *Diags, Consumer);

673 } else {

675 FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {

676 if (StringRef(Cmd.getCreator().getName()) != "clang") {

677

678

679 Consumer.handleBuildCommand(

680 {Cmd.getExecutable(),

681 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});

682 return true;

683 }

684

685

686 std::vectorstd::string Argv;

687 Argv.push_back(Cmd.getExecutable());

688 Argv.insert(Argv.end(), Cmd.getArguments().begin(),

689 Cmd.getArguments().end());

690

691

692

693

694

696 PCHContainerOps, *Diags, Consumer);

697 });

698 }

699

700 if (Success && !Action.hasScanned())

701 Diags->Report(diag::err_fe_expected_compiler_job)

702 << llvm::join(FinalCommandLine, " ");

703 return Success && Action.hasScanned();

704}

705

static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, FileManager &FM, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)

static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, FileManager &FM, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)

Abstract interface for callback invocations by the ASTReader.

virtual bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, bool Complain)

Receives the header search paths.

virtual void visitImport(StringRef ModuleName, StringRef Filename)

If needsImportVisitation returns true, this is called for each AST file imported by this AST file.

virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)

This is called for each AST file loaded.

virtual bool needsImportVisitation() const

Returns true if this ASTReaderListener wants to receive the imports of the AST file via visitImport,...

@ ARR_OutOfDate

The client can handle an AST file that cannot load because it is out-of-date relative to its input fi...

static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, const InMemoryModuleCache &ModuleCache, const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, ASTReaderListener &Listener, bool ValidateDiagnosticOptions, unsigned ClientLoadCapabilities=ARR_ConfigurationMismatch|ARR_OutOfDate)

Read the control block for the named AST file.

CompilerInstance - Helper class for managing a single instance of the Clang compiler.

void createSourceManager(FileManager &FileMgr)

Create the source manager and replace any existing one with it.

FileManager * createFileManager(IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)

Create the file manager and replace any existing one with it.

const PCHContainerReader & getPCHContainerReader() const

Return the appropriate PCHContainerReader depending on the current CodeGenOptions.

DiagnosticsEngine & getDiagnostics() const

Get the current diagnostics engine.

void createDiagnostics(llvm::vfs::FileSystem &VFS, DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)

Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...

void setInvocation(std::shared_ptr< CompilerInvocation > Value)

setInvocation - Replace the current invocation.

FileManager & getFileManager() const

Return the current file manager to the caller.

InMemoryModuleCache & getModuleCache() const

void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)

FrontendOptions & getFrontendOpts()

bool hasDiagnostics() const

HeaderSearchOptions & getHeaderSearchOpts()

CompilerInvocation & getInvocation()

PreprocessorOptions & getPreprocessorOpts()

bool ExecuteAction(FrontendAction &Act)

ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.

DiagnosticOptions & getDiagnosticOpts()

LangOptions & getLangOpts()

Helper class for holding the data necessary to invoke the compiler.

DependencyOutputOptions & getDependencyOutputOpts()

ArrayRef< std::string > getDependencies() const

Builds a dependency file when attached to a Preprocessor (for includes) and ASTReader (for module imp...

void finishedMainFile(DiagnosticsEngine &Diags) override

Called when the end of the main file is reached.

Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...

virtual void finish()

Callback to inform the diagnostic client that processing of all source files has ended.

Options for controlling the compiler diagnostics engine.

std::vector< std::string > Warnings

The list of -W... options used to alter the diagnostic mappings, with the prefixes removed.

std::string DiagnosticSerializationFile

The file to serialize diagnostics to (non-appending).

Concrete class used by the front-end to report problems and issues.

DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)

Issue the message to the client.

bool hasErrorOccurred() const

DiagnosticOptions & getDiagnosticOptions() const

Retrieve the diagnostic options.

DiagnosticConsumer * getClient()

A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...

Implements support for file system lookup, file system caching, and directory search management.

void AddStats(const FileManager &Other)

Import statistics from a child FileManager and add them to this current FileManager.

llvm::vfs::FileSystem & getVirtualFileSystem() const

llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const

Keeps track of options that affect how file operations are performed.

unsigned ModulesShareFileManager

Whether to share the FileManager when building modules.

std::string OutputFile

The output file, if any.

unsigned GenerateGlobalModuleIndex

Whether we can generate the global module index if needed.

SmallVector< FrontendInputFile, 0 > Inputs

The input files and their types.

unsigned UseGlobalModuleIndex

Whether we can use the global module index if available.

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...

bool ModulesCheckRelocated

Perform extra checks when loading PCM files for mutable file systems.

std::string ImplicitPCHInclude

The implicit PCH included at the start of the translation unit, or empty.

bool AllowPCHWithDifferentModulesCachePath

When true, a PCH with modules cache path different to the current compilation will not be rejected.

std::vector< std::pair< std::string, bool > > Macros

std::function< std::optional< ArrayRef< dependency_directives_scan::Directive > >(FileEntryRef)> DependencyDirectivesForFile

Function for getting the dependency preprocessor directives of a file.

This class handles loading and caching of source files into memory.

The base class of the type hierarchy.

Command - An executable path/name and argument vector to execute.

llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)

Returns the driver mode option's value, i.e.

llvm::Error expandResponseFiles(SmallVectorImpl< const char * > &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS=nullptr)

Expand response files from a clang driver or cc1 invocation.

bool IsClangCL(StringRef DriverMode)

Checks whether the value produced by getDriverMode is for CL mode.

ModuleKind

Specifies the kind of module that has been loaded.

@ MK_ExplicitModule

File is an explicitly-loaded module.

The JSON file list parser is used to communicate input to InstallAPI.

std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)

@ Result

The result type of a method or function.

IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)

@ Success

Template argument deduction was successful.

int __ovld __cnfn any(char)

Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.