@@ -640,3 +640,161 @@ std::string OutputLookup::lookupModuleOutput(const ModuleDeps &MD,
640
640
PCMPath.first ->second = ::lookupModuleOutput (MD, MOK, MLOContext, MLO);
641
641
return PCMPath.first ->second ;
642
642
}
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