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