[ClangImporter] Use TextAPI apis for capturing current-version (#67204)

Use LLVM apis for understanding TBD files instead of parsing the yaml
directly. This prevents breaking the compiler when TBD-v5 exists which
is in json.
This commit is contained in:
Cyndy Ishida
2023-07-11 07:47:58 -07:00
committed by GitHub
parent dc92742ccc
commit 65d819e615
6 changed files with 39 additions and 50 deletions

View File

@@ -77,7 +77,8 @@
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/VirtualOutputBackend.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/TextAPIReader.h"
#include <algorithm>
#include <memory>
#include <string>
@@ -1934,9 +1935,29 @@ bool ClangImporter::isModuleImported(const clang::Module *M) {
return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible;
}
static std::string getScalaNodeText(llvm::yaml::Node *N) {
SmallString<32> Buffer;
return cast<llvm::yaml::ScalarNode>(N)->getValue(Buffer).str();
static llvm::VersionTuple getCurrentVersionFromTBD(StringRef path,
StringRef moduleName) {
std::string fwName = (moduleName + ".framework").str();
auto pos = path.find(fwName);
if (pos == StringRef::npos)
return {};
llvm::SmallString<256> buffer(path.substr(0, pos + fwName.size()));
llvm::sys::path::append(buffer, moduleName + ".tbd");
auto tbdPath = buffer.str();
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> tbdBufOrErr =
llvm::MemoryBuffer::getFile(tbdPath);
// .tbd file doesn't exist, exit.
if (!tbdBufOrErr)
return {};
auto tbdFileOrErr =
llvm::MachO::TextAPIReader::get(tbdBufOrErr.get()->getMemBufferRef());
if (auto err = tbdFileOrErr.takeError()) {
consumeError(std::move(err));
return {};
}
auto tbdCV = (*tbdFileOrErr)->getCurrentVersion();
return llvm::VersionTuple(tbdCV.getMajor(), tbdCV.getMinor(),
tbdCV.getSubminor());
}
bool ClangImporter::canImportModule(ImportPath::Module modulePath,
@@ -1988,49 +2009,13 @@ bool ClangImporter::canImportModule(ImportPath::Module modulePath,
return true;
assert(available);
llvm::VersionTuple currentVersion;
StringRef path = getClangASTContext().getSourceManager()
.getFilename(clangModule->DefinitionLoc);
// Look for the .tbd file inside .framework dir to get the project version
// number.
std::string fwName = (llvm::Twine(topModule.Item.str()) + ".framework").str();
auto pos = path.find(fwName);
while (pos != StringRef::npos) {
llvm::SmallString<256> buffer(path.substr(0, pos + fwName.size()));
llvm::sys::path::append(buffer, llvm::Twine(topModule.Item.str()) + ".tbd");
auto tbdPath = buffer.str();
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> tbdBufOrErr =
llvm::MemoryBuffer::getFile(tbdPath);
// .tbd file doesn't exist, break.
if (!tbdBufOrErr) {
break;
}
StringRef tbdBuffer = tbdBufOrErr->get()->getBuffer();
// Use a new source manager instead of the one from ASTContext because we
// don't want the Json file to be persistent.
SourceManager SM;
llvm::yaml::Stream Stream(llvm::MemoryBufferRef(tbdBuffer, tbdPath),
SM.getLLVMSourceMgr());
auto DI = Stream.begin();
assert(DI != Stream.end() && "Failed to read a document");
llvm::yaml::Node *N = DI->getRoot();
assert(N && "Failed to find a root");
auto *pairs = dyn_cast_or_null<llvm::yaml::MappingNode>(N);
if (!pairs)
break;
for (auto &keyValue: *pairs) {
auto key = getScalaNodeText(keyValue.getKey());
// Look for field "current-version" in the .tbd file.
if (key == "current-version") {
auto ver = getScalaNodeText(keyValue.getValue());
currentVersion.tryParse(ver);
break;
}
}
break;
}
llvm::VersionTuple currentVersion =
getCurrentVersionFromTBD(path, topModule.Item.str());
versionInfo->setVersion(currentVersion,
ModuleVersionSourceKind::ClangModuleTBD);
return true;

View File

@@ -0,0 +1,8 @@
--- !tapi-tbd
tbd-version: 4
targets: [ arm64-macos, arm64-ios, arm64-watchos, arm64-tvos,
arm64-ios-simulator, arm64-watchos-simulator, arm64-tvos-simulator ]
flags: [ not_app_extension_safe ]
install-name: '/System/Library/Frameworks/Simple.framework/Versions/A/Simple'
current-version: 1830.100
...

View File

@@ -3,9 +3,6 @@
// RUN: %empty-directory(%t/frameworks)
// RUN: cp -rf %S/Inputs/frameworks/Simple.framework %t/frameworks/
// RUN: echo "current-version: 1830.100" > %t/frameworks/Simple.framework/Simple.tbd
// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -F %t/frameworks
import Simple

View File

@@ -11,7 +11,7 @@
// conditional, the version in the `.tbd` should be honored. When `_version`
// is specified, the version from the `.swiftmodule` should be honored.
// RUN: echo "current-version: 3" > %t/frameworks/Simple.framework/Simple.tbd
// RUN: sed -i -e "s/1830\.100/3/g" %t/frameworks/Simple.framework/Simple.tbd
// RUN: echo "@_exported import Simple" > %t.overlay.swift
// RUN: echo "public func additional() {}" >> %t.overlay.swift

View File

@@ -3,8 +3,7 @@
// RUN: %empty-directory(%t/frameworks)
// RUN: cp -rf %S/Inputs/frameworks/Simple.framework %t/frameworks/
// RUN: echo "" > %t/frameworks/Simple.framework/Simple.tbd
// RUN: rm -rf %t/frameworks/Simple.framework/Simple.tbd
// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -F %t/frameworks
import Simple

View File

@@ -12,7 +12,7 @@
// should be no diagnostics and the version in the `.swiftmodule` should be
// honored.
// RUN: echo "" > %t/frameworks/Simple.framework/Simple.tbd
// RUN: rm -rf %t/frameworks/Simple.framework/Simple.tbd
// RUN: echo "@_exported import Simple" > %t.overlay.swift
// RUN: echo "public func additional() {}" >> %t.overlay.swift