@@ -317,6 +317,132 @@ void clang_experimental_DependencyScannerWorkerScanSettings_dispose(
317
317
delete unwrap (Settings);
318
318
}
319
319
320
+ namespace {
321
+ // Helper class to capture a returnable error code and to return a formatted
322
+ // message in a provided CXString pointer.
323
+ class MessageEmitter {
324
+ const CXErrorCode ErrorCode;
325
+ CXString *OutputString;
326
+ std::string Buffer;
327
+ llvm::raw_string_ostream Stream;
328
+
329
+ public:
330
+ MessageEmitter (CXErrorCode Code, CXString *Output)
331
+ : ErrorCode(Code), OutputString(Output), Stream(Buffer) {}
332
+ ~MessageEmitter () {
333
+ if (OutputString)
334
+ *OutputString = clang::cxstring::createDup (Buffer.c_str ());
335
+ }
336
+
337
+ operator CXErrorCode () const { return ErrorCode; }
338
+
339
+ template <typename T> MessageEmitter &operator <<(const T &t) {
340
+ Stream << t;
341
+ return *this ;
342
+ }
343
+ };
344
+ } // end anonymous namespace
345
+
346
+ enum CXErrorCode clang_experimental_DependencyScanner_generateReproducer (
347
+ int argc, const char *const *argv, const char *WorkingDirectory,
348
+ CXString *messageOut) {
349
+ auto report = [messageOut](CXErrorCode errorCode) -> MessageEmitter {
350
+ return MessageEmitter (errorCode, messageOut);
351
+ };
352
+ auto reportFailure = [&report]() -> MessageEmitter {
353
+ return report (CXError_Failure);
354
+ };
355
+
356
+ if (argc < 2 || !argv)
357
+ return report (CXError_InvalidArguments) << " missing compilation command" ;
358
+ if (!WorkingDirectory)
359
+ return report (CXError_InvalidArguments) << " missing working directory" ;
360
+
361
+ CASOptions CASOpts;
362
+ IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> FS;
363
+ DependencyScanningService DepsService (
364
+ ScanningMode::DependencyDirectivesScan, ScanningOutputFormat::Full,
365
+ CASOpts, /* CAS=*/ nullptr , /* ActionCache=*/ nullptr , FS);
366
+ DependencyScanningTool DepsTool (DepsService);
367
+
368
+ llvm::SmallString<128 > ReproScriptPath;
369
+ int ScriptFD;
370
+ if (auto EC = llvm::sys::fs::createTemporaryFile (" reproducer" , " sh" , ScriptFD,
371
+ ReproScriptPath)) {
372
+ return reportFailure () << " failed to create a reproducer script file" ;
373
+ }
374
+ SmallString<128 > FileCachePath = ReproScriptPath;
375
+ llvm::sys::path::replace_extension (FileCachePath, " .cache" );
376
+
377
+ std::string FileCacheName = llvm::sys::path::filename (FileCachePath).str ();
378
+ auto LookupOutput = [&FileCacheName](const ModuleDeps &MD,
379
+ ModuleOutputKind MOK) -> std::string {
380
+ if (MOK != ModuleOutputKind::ModuleFile)
381
+ return " " ;
382
+ return FileCacheName + " /explicitly-built-modules/" +
383
+ MD.ID .ModuleName + " -" + MD.ID .ContextHash + " .pcm" ;
384
+ };
385
+
386
+ std::vector<std::string> Compilation{argv, argv + argc};
387
+ llvm::DenseSet<ModuleID> AlreadySeen;
388
+ auto TUDepsOrErr = DepsTool.getTranslationUnitDependencies (
389
+ Compilation, WorkingDirectory, AlreadySeen, std::move (LookupOutput));
390
+ if (!TUDepsOrErr)
391
+ return reportFailure () << " failed to generate a reproducer\n "
392
+ << toString (TUDepsOrErr.takeError ());
393
+
394
+ TranslationUnitDeps TU = *TUDepsOrErr;
395
+ llvm::raw_fd_ostream ScriptOS (ScriptFD, /* shouldClose=*/ true );
396
+ ScriptOS << " # Original command:\n #" ;
397
+ for (StringRef cliArg : Compilation) {
398
+ ScriptOS << ' ' << cliArg;
399
+ }
400
+ ScriptOS << " \n\n " ;
401
+
402
+ ScriptOS << " # Dependencies:\n " ;
403
+ std::string ReproExecutable = std::string (argv[0 ]);
404
+ auto PrintArguments = [&ReproExecutable,
405
+ &FileCacheName](llvm::raw_fd_ostream &OS,
406
+ ArrayRef<std::string> Arguments) {
407
+ OS << ReproExecutable;
408
+ for (int I = 0 , E = Arguments.size (); I < E; ++I)
409
+ OS << ' ' << Arguments[I];
410
+ OS << " -ivfsoverlay \" " << FileCacheName << " /vfs/vfs.yaml\" " ;
411
+ OS << ' \n ' ;
412
+ };
413
+ for (ModuleDeps &dep : TU.ModuleGraph )
414
+ PrintArguments (ScriptOS, dep.getBuildArguments ());
415
+ ScriptOS << " \n # Translation unit:\n " ;
416
+ for (const Command &buildCommand : TU.Commands )
417
+ PrintArguments (ScriptOS, buildCommand.Arguments );
418
+
419
+ SmallString<128 > VFSCachePath = FileCachePath;
420
+ llvm::sys::path::append (VFSCachePath, " vfs" );
421
+ std::string VFSCachePathStr = VFSCachePath.str ().str ();
422
+ llvm::FileCollector fileCollector (VFSCachePathStr,
423
+ /* OverlayRoot=*/ VFSCachePathStr);
424
+ for (const auto &fileDep : TU.FileDeps ) {
425
+ fileCollector.addFile (fileDep);
426
+ }
427
+ for (ModuleDeps &dep : TU.ModuleGraph ) {
428
+ dep.forEachFileDep ([&fileCollector](StringRef fileDep) {
429
+ fileCollector.addFile (fileDep);
430
+ });
431
+ }
432
+ if (fileCollector.copyFiles (/* StopOnError=*/ true ))
433
+ return reportFailure ()
434
+ << " failed to copy the files used for the compilation" ;
435
+ SmallString<128 > VFSOverlayPath = VFSCachePath;
436
+ llvm::sys::path::append (VFSOverlayPath, " vfs.yaml" );
437
+ if (fileCollector.writeMapping (VFSOverlayPath))
438
+ return reportFailure () << " failed to write a VFS overlay mapping" ;
439
+
440
+ return report (CXError_Success)
441
+ << " Created a reproducer. Sources and associated run script(s) are "
442
+ " located at:\n "
443
+ << FileCachePath << " \n " << ReproScriptPath;
444
+ }
445
+
320
446
enum CXErrorCode clang_experimental_DependencyScannerWorker_getDepGraph (
321
447
CXDependencyScannerWorker W,
322
448
CXDependencyScannerWorkerScanSettings CXSettings, CXDepGraph *Out) {
0 commit comments