3232#include " llvm/ADT/StringRef.h"
3333#include " llvm/ADT/StringSet.h"
3434#include " llvm/Support/FileSystem.h"
35+ #include " llvm/Support/StringSaver.h"
36+ #include " llvm/Support/YAMLTraits.h"
37+ #include " llvm/Support/YAMLParser.h"
3538#include < set>
3639
3740using namespace swift ;
41+ using namespace llvm ::yaml;
3842
3943namespace {
44+ struct BatchScanInput {
45+ StringRef moduleName;
46+ StringRef arguments;
47+ StringRef outputPath;
48+ bool isSwift;
49+ };
50+
51+ static std::string getScalaNodeText (Node *N) {
52+ SmallString<32 > Buffer;
53+ return cast<ScalarNode>(N)->getValue (Buffer).str ();
54+ }
55+
56+ // / Parse an entry like this, where the "platforms" key-value pair is optional:
57+ // / {
58+ // / "swiftModuleName": "Foo",
59+ // / "arguments": "-target 10.15",
60+ // / "output": "../Foo.json"
61+ // / },
62+ static bool parseBatchInputEntries (ASTContext &Ctx, llvm::StringSaver &saver,
63+ Node *Node, std::vector<BatchScanInput> &result) {
64+ auto *SN = cast<SequenceNode>(Node);
65+ if (!SN)
66+ return true ;
67+ for (auto It = SN->begin (); It != SN->end (); ++It) {
68+ auto *MN = cast<MappingNode>(&*It);
69+ BatchScanInput entry;
70+ Optional<std::set<int8_t >> Platforms;
71+ for (auto &Pair: *MN) {
72+ auto Key = getScalaNodeText (Pair.getKey ());
73+ auto * Value = Pair.getValue ();
74+ if (Key == " clangModuleName" ) {
75+ entry.moduleName = saver.save (getScalaNodeText (Value));
76+ entry.isSwift = false ;
77+ } else if (Key == " swiftModuleName" ) {
78+ entry.moduleName = saver.save (getScalaNodeText (Value));
79+ entry.isSwift = true ;
80+ } else if (Key == " arguments" ) {
81+ entry.arguments = saver.save (getScalaNodeText (Value));
82+ } else if (Key == " output" ) {
83+ entry.outputPath = saver.save (getScalaNodeText (Value));
84+ } else {
85+ // Future proof.
86+ continue ;
87+ }
88+ }
89+ if (entry.moduleName .empty ())
90+ return true ;
91+ if (entry.outputPath .empty ())
92+ return true ;
93+ result.emplace_back (std::move (entry));
94+ }
95+ return false ;
96+ }
4097
98+ static Optional<std::vector<BatchScanInput>>
99+ parseBatchScanInputFile (ASTContext &ctx, StringRef batchInputPath,
100+ llvm::StringSaver &saver) {
101+ assert (!batchInputPath.empty ());
102+ namespace yaml = llvm::yaml;
103+ std::vector<BatchScanInput> result;
104+
105+ // Load the input file.
106+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
107+ llvm::MemoryBuffer::getFile (batchInputPath);
108+ if (!FileBufOrErr) {
109+ ctx.Diags .diagnose (SourceLoc (), diag::batch_scan_input_file_missing,
110+ batchInputPath);
111+ return None;
112+ }
113+ StringRef Buffer = FileBufOrErr->get ()->getBuffer ();
114+
115+ // Use a new source manager instead of the one from ASTContext because we
116+ // don't want the Json file to be persistent.
117+ SourceManager SM;
118+ yaml::Stream Stream (llvm::MemoryBufferRef (Buffer, batchInputPath),
119+ SM.getLLVMSourceMgr ());
120+ for (auto DI = Stream.begin (); DI != Stream.end (); ++ DI) {
121+ assert (DI != Stream.end () && " Failed to read a document" );
122+ yaml::Node *N = DI->getRoot ();
123+ assert (N && " Failed to find a root" );
124+ if (parseBatchInputEntries (ctx, saver, N, result)) {
125+ ctx.Diags .diagnose (SourceLoc (), diag::batch_scan_input_file_corrupted,
126+ batchInputPath);
127+ return None;
128+ }
129+ }
130+ return result;
131+ }
41132}
42133
43134// / Find all of the imported Clang modules starting with the given module name.
@@ -533,15 +624,17 @@ static bool diagnoseCycle(CompilerInstance &instance,
533624 return false ;
534625}
535626
536- bool swift::scanClangDependencies (CompilerInstance &instance) {
627+ static bool scanModuleDependencies (CompilerInstance &instance,
628+ StringRef moduleName,
629+ StringRef arguments,
630+ bool isClang,
631+ StringRef outputPath) {
537632 ASTContext &ctx = instance.getASTContext ();
538- ModuleDecl *mainModule = instance.getMainModule ();
539633 auto &FEOpts = instance.getInvocation ().getFrontendOptions ();
540634 ModuleInterfaceLoaderOptions LoaderOpts (FEOpts);
541635 auto ModuleCachePath = getModuleCachePathFromClang (ctx
542636 .getClangModuleLoader ()->getClangInstance ());
543637
544- StringRef mainModuleName = mainModule->getNameStr ();
545638 llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
546639 std::set<ModuleDependencyID>> allModules;
547640 // Create the module dependency cache.
@@ -555,16 +648,23 @@ bool swift::scanClangDependencies(CompilerInstance &instance) {
555648 FEOpts.PrebuiltModuleCachePath ,
556649 FEOpts.SerializeModuleInterfaceDependencyHashes ,
557650 FEOpts.shouldTrackSystemDependencies ());
558- // Loading the clang module using Clang importer.
559- // This action will populate the cache with the main module's dependencies.
560- auto rootDeps = static_cast <ClangImporter*>(ctx.getClangModuleLoader ())
561- ->getModuleDependencies (mainModuleName, cache, ASTDelegate);
651+ Optional<ModuleDependencies> rootDeps;
652+ if (isClang) {
653+ // Loading the clang module using Clang importer.
654+ // This action will populate the cache with the main module's dependencies.
655+ rootDeps = ctx.getModuleDependencies (moduleName, /* IsClang*/ true , cache,
656+ ASTDelegate);
657+ } else {
658+ // Loading the swift module's dependencies.
659+ rootDeps = ctx.getSwiftModuleDependencies (moduleName, cache, ASTDelegate);
660+ }
562661 if (!rootDeps.hasValue ()) {
563662 // We cannot find the clang module, abort.
564663 return true ;
565664 }
566665 // Add the main module.
567- allModules.insert ({mainModuleName.str (), ModuleDependenciesKind::Clang});
666+ allModules.insert ({moduleName.str (), isClang ? ModuleDependenciesKind::Clang:
667+ ModuleDependenciesKind::Swift});
568668
569669 // Explore the dependencies of every module.
570670 for (unsigned currentModuleIdx = 0 ;
@@ -576,13 +676,38 @@ bool swift::scanClangDependencies(CompilerInstance &instance) {
576676 allModules.insert (discoveredModules.begin (), discoveredModules.end ());
577677 }
578678 // Write out the JSON description.
579- std::string path = FEOpts.InputsAndOutputs .getSingleOutputFilename ();
580679 std::error_code EC;
581- llvm::raw_fd_ostream out (path , EC, llvm::sys::fs::F_None);
680+ llvm::raw_fd_ostream out (outputPath , EC, llvm::sys::fs::F_None);
582681 writeJSON (out, instance, cache, ASTDelegate, allModules.getArrayRef ());
583682 return false ;
584683}
585684
685+ bool swift::scanClangDependencies (CompilerInstance &instance) {
686+ return scanModuleDependencies (instance,
687+ instance.getMainModule ()->getNameStr (),
688+ StringRef (),
689+ /* isClang*/ true ,
690+ instance.getInvocation ().getFrontendOptions ()
691+ .InputsAndOutputs .getSingleOutputFilename ());
692+ }
693+
694+ bool swift::batchScanModuleDependencies (CompilerInstance &instance,
695+ llvm::StringRef batchInputFile) {
696+ (void )instance.getMainModule ();
697+ llvm::BumpPtrAllocator alloc;
698+ llvm::StringSaver saver (alloc);
699+ auto results = parseBatchScanInputFile (instance.getASTContext (),
700+ batchInputFile, saver);
701+ if (!results.hasValue ())
702+ return true ;
703+ for (auto &entry: *results) {
704+ if (scanModuleDependencies (instance, entry.moduleName , entry.arguments ,
705+ !entry.isSwift , entry.outputPath ))
706+ return true ;
707+ }
708+ return false ;
709+ }
710+
586711bool swift::scanDependencies (CompilerInstance &instance) {
587712 ASTContext &Context = instance.getASTContext ();
588713 ModuleDecl *mainModule = instance.getMainModule ();
0 commit comments