6565#include " llvm/Support/FileCollector.h"
6666#include " llvm/Support/Memory.h"
6767#include " llvm/Support/Path.h"
68+ #include " llvm/Support/YAMLTraits.h"
69+ #include " llvm/Support/YAMLParser.h"
6870#include < algorithm>
6971#include < memory>
7072
@@ -1731,6 +1733,11 @@ bool ClangImporter::isModuleImported(const clang::Module *M) {
17311733 return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible;
17321734}
17331735
1736+ static std::string getScalaNodeText (llvm::yaml::Node *N) {
1737+ SmallString<32 > Buffer;
1738+ return cast<llvm::yaml::ScalarNode>(N)->getValue (Buffer).str ();
1739+ }
1740+
17341741bool ClangImporter::canImportModule (ImportPath::Element moduleID,
17351742 llvm::VersionTuple version,
17361743 bool underlyingVersion) {
@@ -1748,7 +1755,66 @@ bool ClangImporter::canImportModule(ImportPath::Element moduleID,
17481755 clang::Module::UnresolvedHeaderDirective mh;
17491756 clang::Module *m;
17501757 auto &ctx = Impl.getClangASTContext ();
1751- return clangModule->isAvailable (ctx.getLangOpts (), getTargetInfo (), r, mh, m);
1758+ auto available = clangModule->isAvailable (ctx.getLangOpts (), getTargetInfo (),
1759+ r, mh, m);
1760+ if (!available)
1761+ return false ;
1762+ if (version.empty ())
1763+ return true ;
1764+ assert (available);
1765+ assert (!version.empty ());
1766+ llvm::VersionTuple currentVersion;
1767+ StringRef path = getClangASTContext ().getSourceManager ()
1768+ .getFilename (clangModule->DefinitionLoc );
1769+ // Look for the .tbd file inside .framework dir to get the project version
1770+ // number.
1771+ std::string fwName = (llvm::Twine (moduleID.Item .str ()) + " .framework" ).str ();
1772+ auto pos = path.find (fwName);
1773+ while (pos != StringRef::npos) {
1774+ llvm::SmallString<256 > buffer (path.substr (0 , pos + fwName.size ()));
1775+ llvm::sys::path::append (buffer, llvm::Twine (moduleID.Item .str ()) + " .tbd" );
1776+ auto tbdPath = buffer.str ();
1777+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> tbdBufOrErr =
1778+ llvm::MemoryBuffer::getFile (tbdPath);
1779+ // .tbd file doesn't exist, break.
1780+ if (!tbdBufOrErr) {
1781+ break ;
1782+ }
1783+ StringRef tbdBuffer = tbdBufOrErr->get ()->getBuffer ();
1784+
1785+ // Use a new source manager instead of the one from ASTContext because we
1786+ // don't want the Json file to be persistent.
1787+ SourceManager SM;
1788+ llvm::yaml::Stream Stream (llvm::MemoryBufferRef (tbdBuffer, tbdPath),
1789+ SM.getLLVMSourceMgr ());
1790+ auto DI = Stream.begin ();
1791+ assert (DI != Stream.end () && " Failed to read a document" );
1792+ llvm::yaml::Node *N = DI->getRoot ();
1793+ assert (N && " Failed to find a root" );
1794+ auto *pairs = dyn_cast_or_null<llvm::yaml::MappingNode>(N);
1795+ if (!pairs)
1796+ break ;
1797+ for (auto &keyValue: *pairs) {
1798+ auto key = getScalaNodeText (keyValue.getKey ());
1799+ // Look for field "current-version" in the .tbd file.
1800+ if (key == " current-version" ) {
1801+ auto ver = getScalaNodeText (keyValue.getValue ());
1802+ currentVersion.tryParse (ver);
1803+ break ;
1804+ }
1805+ }
1806+ break ;
1807+ }
1808+ // Diagnose unable to checking the current version.
1809+ if (currentVersion.empty ()) {
1810+ Impl.diagnose (moduleID.Loc , diag::cannot_find_project_version, " Clang" ,
1811+ moduleID.Item .str ());
1812+ return true ;
1813+ }
1814+ assert (!currentVersion.empty ());
1815+ // Give a green light if the version on disk is greater or equal to the version
1816+ // specified in the canImport condition.
1817+ return currentVersion >= version;
17521818}
17531819
17541820ModuleDecl *ClangImporter::Implementation::loadModuleClang (
0 commit comments