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+ };
49+
50+ static std::string getScalaNodeText (Node *N) {
51+ SmallString<32 > Buffer;
52+ return cast<ScalarNode>(N)->getValue (Buffer).str ();
53+ }
54+
55+ // / Parse an entry like this, where the "platforms" key-value pair is optional:
56+ // / {
57+ // / "module": "Foo.pcm",
58+ // / "arguments": "-target 10.15",
59+ // / "output": "../Foo.json"
60+ // / },
61+ static bool parseBatchInputEntries (ASTContext &Ctx, llvm::StringSaver &saver,
62+ Node *Node, std::vector<BatchScanInput> &result) {
63+ auto *SN = cast<SequenceNode>(Node);
64+ if (!SN)
65+ return true ;
66+ for (auto It = SN->begin (); It != SN->end (); ++It) {
67+ auto *MN = cast<MappingNode>(&*It);
68+ BatchScanInput entry;
69+ Optional<std::set<int8_t >> Platforms;
70+ for (auto &Pair: *MN) {
71+ auto Key = getScalaNodeText (Pair.getKey ());
72+ auto * Value = Pair.getValue ();
73+ if (Key == " module" ) {
74+ entry.moduleName = saver.save (getScalaNodeText (Value));
75+ } else if (Key == " arguments" ) {
76+ entry.arguments = saver.save (getScalaNodeText (Value));
77+ } else if (Key == " output" ) {
78+ entry.outputPath = saver.save (getScalaNodeText (Value));
79+ } else {
80+ // Future proof.
81+ continue ;
82+ }
83+ }
84+ if (entry.moduleName .empty ())
85+ return true ;
86+ if (entry.outputPath .empty ())
87+ return true ;
88+ auto ext = llvm::sys::path::extension (entry.moduleName );
89+ if (ext != " .swiftmodule" && ext != " .pcm" )
90+ return true ;
91+ result.emplace_back (std::move (entry));
92+ }
93+ return false ;
94+ }
4095
96+ static Optional<std::vector<BatchScanInput>>
97+ parseBatchScanInputFile (ASTContext &ctx, StringRef batchInputPath,
98+ llvm::StringSaver &saver) {
99+ assert (!batchInputPath.empty ());
100+ namespace yaml = llvm::yaml;
101+ std::vector<BatchScanInput> result;
102+
103+ // Load the input file.
104+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
105+ llvm::MemoryBuffer::getFile (batchInputPath);
106+ if (!FileBufOrErr) {
107+ ctx.Diags .diagnose (SourceLoc (), diag::batch_scan_input_file_missing,
108+ batchInputPath);
109+ return None;
110+ }
111+ StringRef Buffer = FileBufOrErr->get ()->getBuffer ();
112+
113+ // Use a new source manager instead of the one from ASTContext because we
114+ // don't want the Json file to be persistent.
115+ SourceManager SM;
116+ yaml::Stream Stream (llvm::MemoryBufferRef (Buffer, batchInputPath),
117+ SM.getLLVMSourceMgr ());
118+ for (auto DI = Stream.begin (); DI != Stream.end (); ++ DI) {
119+ assert (DI != Stream.end () && " Failed to read a document" );
120+ yaml::Node *N = DI->getRoot ();
121+ assert (N && " Failed to find a root" );
122+ if (parseBatchInputEntries (ctx, saver, N, result)) {
123+ ctx.Diags .diagnose (SourceLoc (), diag::batch_scan_input_file_corrupted,
124+ batchInputPath);
125+ return None;
126+ }
127+ }
128+ return result;
129+ }
41130}
42131
43132// / Find all of the imported Clang modules starting with the given module name.
@@ -533,15 +622,17 @@ static bool diagnoseCycle(CompilerInstance &instance,
533622 return false ;
534623}
535624
536- bool swift::scanClangDependencies (CompilerInstance &instance) {
625+ static bool scanModuleDependencies (CompilerInstance &instance,
626+ StringRef moduleName,
627+ StringRef arguments,
628+ bool isClang,
629+ StringRef outputPath) {
537630 ASTContext &ctx = instance.getASTContext ();
538- ModuleDecl *mainModule = instance.getMainModule ();
539631 auto &FEOpts = instance.getInvocation ().getFrontendOptions ();
540632 ModuleInterfaceLoaderOptions LoaderOpts (FEOpts);
541633 auto ModuleCachePath = getModuleCachePathFromClang (ctx
542634 .getClangModuleLoader ()->getClangInstance ());
543635
544- StringRef mainModuleName = mainModule->getNameStr ();
545636 llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
546637 std::set<ModuleDependencyID>> allModules;
547638 // Create the module dependency cache.
@@ -555,16 +646,23 @@ bool swift::scanClangDependencies(CompilerInstance &instance) {
555646 FEOpts.PrebuiltModuleCachePath ,
556647 FEOpts.SerializeModuleInterfaceDependencyHashes ,
557648 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);
649+ Optional<ModuleDependencies> rootDeps;
650+ if (isClang) {
651+ // Loading the clang module using Clang importer.
652+ // This action will populate the cache with the main module's dependencies.
653+ rootDeps = ctx.getModuleDependencies (moduleName, /* IsClang*/ true , cache,
654+ ASTDelegate);
655+ } else {
656+ // Loading the swift module's dependencies.
657+ rootDeps = ctx.getSwiftModuleDependencies (moduleName, cache, ASTDelegate);
658+ }
562659 if (!rootDeps.hasValue ()) {
563660 // We cannot find the clang module, abort.
564661 return true ;
565662 }
566663 // Add the main module.
567- allModules.insert ({mainModuleName.str (), ModuleDependenciesKind::Clang});
664+ allModules.insert ({moduleName.str (), isClang ? ModuleDependenciesKind::Clang:
665+ ModuleDependenciesKind::Swift});
568666
569667 // Explore the dependencies of every module.
570668 for (unsigned currentModuleIdx = 0 ;
@@ -576,13 +674,40 @@ bool swift::scanClangDependencies(CompilerInstance &instance) {
576674 allModules.insert (discoveredModules.begin (), discoveredModules.end ());
577675 }
578676 // Write out the JSON description.
579- std::string path = FEOpts.InputsAndOutputs .getSingleOutputFilename ();
580677 std::error_code EC;
581- llvm::raw_fd_ostream out (path , EC, llvm::sys::fs::F_None);
678+ llvm::raw_fd_ostream out (outputPath , EC, llvm::sys::fs::F_None);
582679 writeJSON (out, instance, cache, ASTDelegate, allModules.getArrayRef ());
583680 return false ;
584681}
585682
683+ bool swift::scanClangDependencies (CompilerInstance &instance) {
684+ return scanModuleDependencies (instance,
685+ instance.getMainModule ()->getNameStr (),
686+ StringRef (),
687+ /* isClang*/ true ,
688+ instance.getInvocation ().getFrontendOptions ()
689+ .InputsAndOutputs .getSingleOutputFilename ());
690+ }
691+
692+ bool swift::batchScanModuleDependencies (CompilerInstance &instance,
693+ llvm::StringRef batchInputFile) {
694+ (void )instance.getMainModule ();
695+ llvm::BumpPtrAllocator alloc;
696+ llvm::StringSaver saver (alloc);
697+ auto results = parseBatchScanInputFile (instance.getASTContext (),
698+ batchInputFile, saver);
699+ if (!results.hasValue ())
700+ return true ;
701+ for (auto &entry: *results) {
702+ auto moduleName = llvm::sys::path::stem (entry.moduleName );
703+ auto isClang = llvm::sys::path::extension (entry.moduleName ) == " .pcm" ;
704+ if (scanModuleDependencies (instance, moduleName, entry.arguments , isClang,
705+ 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