@@ -317,6 +317,131 @@ 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 *ModuleName,
348
+ const char *WorkingDirectory, 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 Arg : Compilation)
398
+ ScriptOS << ' ' << Arg;
399
+ ScriptOS << " \n\n " ;
400
+
401
+ ScriptOS << " # Dependencies:\n " ;
402
+ std::string ReproExecutable = std::string (argv[0 ]);
403
+ auto PrintArguments = [&ReproExecutable,
404
+ &FileCacheName](llvm::raw_fd_ostream &OS,
405
+ ArrayRef<std::string> Arguments) {
406
+ OS << ReproExecutable;
407
+ for (int I = 0 , E = Arguments.size (); I < E; ++I)
408
+ OS << ' ' << Arguments[I];
409
+ OS << " -ivfsoverlay \" " << FileCacheName << " /vfs/vfs.yaml\" " ;
410
+ OS << ' \n ' ;
411
+ };
412
+ for (ModuleDeps &Dep : TU.ModuleGraph )
413
+ PrintArguments (ScriptOS, Dep.getBuildArguments ());
414
+ ScriptOS << " \n # Translation unit:\n " ;
415
+ for (const Command &BuildCommand : TU.Commands )
416
+ PrintArguments (ScriptOS, BuildCommand.Arguments );
417
+
418
+ SmallString<128 > VFSCachePath = FileCachePath;
419
+ llvm::sys::path::append (VFSCachePath, " vfs" );
420
+ std::string VFSCachePathStr = VFSCachePath.str ().str ();
421
+ llvm::FileCollector FileCollector (VFSCachePathStr,
422
+ /* OverlayRoot=*/ VFSCachePathStr);
423
+ for (const auto &FileDep : TU.FileDeps ) {
424
+ FileCollector.addFile (FileDep);
425
+ }
426
+ for (ModuleDeps &ModuleDep : TU.ModuleGraph ) {
427
+ ModuleDep.forEachFileDep ([&FileCollector](StringRef FileDep) {
428
+ FileCollector.addFile (FileDep);
429
+ });
430
+ }
431
+ if (FileCollector.copyFiles (/* StopOnError=*/ true ))
432
+ return ReportFailure ()
433
+ << " failed to copy the files used for the compilation" ;
434
+ SmallString<128 > VFSOverlayPath = VFSCachePath;
435
+ llvm::sys::path::append (VFSOverlayPath, " vfs.yaml" );
436
+ if (FileCollector.writeMapping (VFSOverlayPath))
437
+ return ReportFailure () << " failed to write a VFS overlay mapping" ;
438
+
439
+ return Report (CXError_Success)
440
+ << " Created a reproducer. Sources and associated run script(s) are "
441
+ " located at:\n "
442
+ << FileCachePath << " \n " << ReproScriptPath;
443
+ }
444
+
320
445
enum CXErrorCode clang_experimental_DependencyScannerWorker_getDepGraph (
321
446
CXDependencyScannerWorker W,
322
447
CXDependencyScannerWorkerScanSettings CXSettings, CXDepGraph *Out) {
0 commit comments