@@ -312,6 +312,132 @@ void clang_experimental_DependencyScannerWorkerScanSettings_dispose(
312312 delete unwrap (Settings);
313313}
314314
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+
315441enum CXErrorCode clang_experimental_DependencyScannerWorker_getDepGraph (
316442 CXDependencyScannerWorker W,
317443 CXDependencyScannerWorkerScanSettings CXSettings, CXDepGraph *Out) {
0 commit comments