@@ -640,3 +640,161 @@ std::string OutputLookup::lookupModuleOutput(const ModuleDeps &MD,
640640 PCMPath.first ->second = ::lookupModuleOutput (MD, MOK, MLOContext, MLO);
641641 return PCMPath.first ->second ;
642642}
643+
644+ namespace {
645+ struct DependencyScannerReproducerOptions {
646+ std::vector<std::string> BuildArgs;
647+ std::optional<std::string> ModuleName;
648+ std::optional<std::string> WorkingDirectory;
649+
650+ DependencyScannerReproducerOptions (int argc, const char *const *argv,
651+ const char *ModuleName,
652+ const char *WorkingDirectory) {
653+ if (argv)
654+ BuildArgs.assign (argv, argv + argc);
655+ if (ModuleName)
656+ this ->ModuleName = ModuleName;
657+ if (WorkingDirectory)
658+ this ->WorkingDirectory = WorkingDirectory;
659+ }
660+ };
661+
662+ // Helper class to capture a returnable error code and to return a formatted
663+ // message in a provided CXString pointer.
664+ class MessageEmitter {
665+ const CXErrorCode ErrorCode;
666+ CXString *OutputString;
667+ std::string Buffer;
668+ llvm::raw_string_ostream Stream;
669+
670+ public:
671+ MessageEmitter (CXErrorCode Code, CXString *Output)
672+ : ErrorCode(Code), OutputString(Output), Stream(Buffer) {}
673+ ~MessageEmitter () {
674+ if (OutputString)
675+ *OutputString = clang::cxstring::createDup (Buffer.c_str ());
676+ }
677+
678+ operator CXErrorCode () const { return ErrorCode; }
679+
680+ template <typename T> MessageEmitter &operator <<(const T &t) {
681+ Stream << t;
682+ return *this ;
683+ }
684+ };
685+ } // end anonymous namespace
686+
687+ DEFINE_SIMPLE_CONVERSION_FUNCTIONS (DependencyScannerReproducerOptions,
688+ CXDependencyScannerReproducerOptions)
689+
690+ CXDependencyScannerReproducerOptions
691+ clang_experimental_DependencyScannerReproducerOptions_create(
692+ int argc, const char *const *argv, const char *ModuleName,
693+ const char *WorkingDirectory) {
694+ return wrap (new DependencyScannerReproducerOptions{argc, argv, ModuleName,
695+ WorkingDirectory});
696+ }
697+
698+ void clang_experimental_DependencyScannerReproducerOptions_dispose (
699+ CXDependencyScannerReproducerOptions Options) {
700+ delete unwrap (Options);
701+ }
702+
703+ enum CXErrorCode clang_experimental_DependencyScanner_generateReproducer (
704+ CXDependencyScannerReproducerOptions CXOptions, CXString *MessageOut) {
705+ auto Report = [MessageOut](CXErrorCode ErrorCode) -> MessageEmitter {
706+ return MessageEmitter (ErrorCode, MessageOut);
707+ };
708+ auto ReportFailure = [&Report]() -> MessageEmitter {
709+ return Report (CXError_Failure);
710+ };
711+
712+ DependencyScannerReproducerOptions &Opts = *unwrap (CXOptions);
713+ if (Opts.BuildArgs .size () < 2 )
714+ return Report (CXError_InvalidArguments) << " missing compilation command" ;
715+ if (!Opts.WorkingDirectory )
716+ return Report (CXError_InvalidArguments) << " missing working directory" ;
717+
718+ CASOptions CASOpts;
719+ IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> FS;
720+ DependencyScanningService DepsService (
721+ ScanningMode::DependencyDirectivesScan, ScanningOutputFormat::Full,
722+ CASOpts, /* CAS=*/ nullptr , /* ActionCache=*/ nullptr , FS);
723+ DependencyScanningTool DepsTool (DepsService);
724+
725+ llvm::SmallString<128 > ReproScriptPath;
726+ int ScriptFD;
727+ if (auto EC = llvm::sys::fs::createTemporaryFile (" reproducer" , " sh" , ScriptFD,
728+ ReproScriptPath)) {
729+ return ReportFailure () << " failed to create a reproducer script file" ;
730+ }
731+ SmallString<128 > FileCachePath = ReproScriptPath;
732+ llvm::sys::path::replace_extension (FileCachePath, " .cache" );
733+
734+ std::string FileCacheName = llvm::sys::path::filename (FileCachePath).str ();
735+ auto LookupOutput = [&FileCacheName](const ModuleDeps &MD,
736+ ModuleOutputKind MOK) -> std::string {
737+ if (MOK != ModuleOutputKind::ModuleFile)
738+ return " " ;
739+ return FileCacheName + " /explicitly-built-modules/" +
740+ MD.ID .ModuleName + " -" + MD.ID .ContextHash + " .pcm" ;
741+ };
742+
743+ llvm::DenseSet<ModuleID> AlreadySeen;
744+ auto TUDepsOrErr = DepsTool.getTranslationUnitDependencies (
745+ Opts.BuildArgs , *Opts.WorkingDirectory , AlreadySeen,
746+ std::move (LookupOutput));
747+ if (!TUDepsOrErr)
748+ return ReportFailure () << " failed to generate a reproducer\n "
749+ << toString (TUDepsOrErr.takeError ());
750+
751+ TranslationUnitDeps TU = *TUDepsOrErr;
752+ llvm::raw_fd_ostream ScriptOS (ScriptFD, /* shouldClose=*/ true );
753+ ScriptOS << " # Original command:\n #" ;
754+ for (StringRef Arg : Opts.BuildArgs )
755+ ScriptOS << ' ' << Arg;
756+ ScriptOS << " \n\n " ;
757+
758+ ScriptOS << " # Dependencies:\n " ;
759+ std::string ReproExecutable = Opts.BuildArgs .front ();
760+ auto PrintArguments = [&ReproExecutable,
761+ &FileCacheName](llvm::raw_fd_ostream &OS,
762+ ArrayRef<std::string> Arguments) {
763+ OS << ReproExecutable;
764+ for (int I = 0 , E = Arguments.size (); I < E; ++I)
765+ OS << ' ' << Arguments[I];
766+ OS << " -ivfsoverlay \" " << FileCacheName << " /vfs/vfs.yaml\" " ;
767+ OS << ' \n ' ;
768+ };
769+ for (ModuleDeps &Dep : TU.ModuleGraph )
770+ PrintArguments (ScriptOS, Dep.getBuildArguments ());
771+ ScriptOS << " \n # Translation unit:\n " ;
772+ for (const Command &BuildCommand : TU.Commands )
773+ PrintArguments (ScriptOS, BuildCommand.Arguments );
774+
775+ SmallString<128 > VFSCachePath = FileCachePath;
776+ llvm::sys::path::append (VFSCachePath, " vfs" );
777+ std::string VFSCachePathStr = VFSCachePath.str ().str ();
778+ llvm::FileCollector FileCollector (VFSCachePathStr,
779+ /* OverlayRoot=*/ VFSCachePathStr);
780+ for (const auto &FileDep : TU.FileDeps ) {
781+ FileCollector.addFile (FileDep);
782+ }
783+ for (ModuleDeps &ModuleDep : TU.ModuleGraph ) {
784+ ModuleDep.forEachFileDep ([&FileCollector](StringRef FileDep) {
785+ FileCollector.addFile (FileDep);
786+ });
787+ }
788+ if (FileCollector.copyFiles (/* StopOnError=*/ true ))
789+ return ReportFailure ()
790+ << " failed to copy the files used for the compilation" ;
791+ SmallString<128 > VFSOverlayPath = VFSCachePath;
792+ llvm::sys::path::append (VFSOverlayPath, " vfs.yaml" );
793+ if (FileCollector.writeMapping (VFSOverlayPath))
794+ return ReportFailure () << " failed to write a VFS overlay mapping" ;
795+
796+ return Report (CXError_Success)
797+ << " Created a reproducer. Sources and associated run script(s) are "
798+ " located at:\n "
799+ << FileCachePath << " \n " << ReproScriptPath;
800+ }
0 commit comments