@@ -317,6 +317,132 @@ void clang_experimental_DependencyScannerWorkerScanSettings_dispose(
317317 delete unwrap (Settings);
318318}
319319
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+
320446enum CXErrorCode clang_experimental_DependencyScannerWorker_getDepGraph (
321447 CXDependencyScannerWorker W,
322448 CXDependencyScannerWorkerScanSettings CXSettings, CXDepGraph *Out) {
0 commit comments