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