swift-api-digester: teach the tool to directly compare two module interfaces

We used to use a Json format to capture the ABI/API detail of Swift modules to
walk-around module compatibility issues across different compiler versions. Since
Swift module now is in stable format, we can compare two swiftinterface files
directly without dumping the content into Json format.
This commit is contained in:
Xi Ge
2019-05-31 18:52:12 -07:00
parent c9660106a7
commit d405fcd8ec
11 changed files with 233 additions and 175 deletions

View File

@@ -98,6 +98,10 @@ static llvm::cl::list<std::string>
FrameworkPaths("F", llvm::cl::desc("add a directory to the framework search path"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
BaselineModuleInputPaths("BI", llvm::cl::desc("add a module for baseline input"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
ModuleInputPaths("I", llvm::cl::desc("add a module for input"),
llvm::cl::cat(Category));
@@ -2074,6 +2078,33 @@ static void findTypeMemberDiffs(NodePtr leftSDKRoot, NodePtr rightSDKRoot,
}
}
static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule,
SDKNodeRoot *RightModule, StringRef OutputPath,
llvm::StringSet<> ProtocolReqWhitelist) {
assert(LeftModule);
assert(RightModule);
llvm::raw_ostream *OS = &llvm::errs();
std::unique_ptr<llvm::raw_ostream> FileOS;
if (!OutputPath.empty()) {
std::error_code EC;
FileOS.reset(new llvm::raw_fd_ostream(OutputPath, EC, llvm::sys::fs::F_None));
OS = FileOS.get();
}
ModuleDifferDiagsConsumer PDC(true, *OS);
Ctx.getDiags().addConsumer(PDC);
TypeAliasDiffFinder(LeftModule, RightModule,
Ctx.getTypeAliasUpdateMap()).search();
PrunePass Prune(Ctx, std::move(ProtocolReqWhitelist));
Prune.pass(LeftModule, RightModule);
ChangeRefinementPass RefinementPass(Ctx.getNodeUpdateMap());
RefinementPass.pass(LeftModule, RightModule);
// Find member hoist changes to help refine diagnostics.
findTypeMemberDiffs(LeftModule, RightModule, Ctx.getTypeMemberDiffs());
DiagnosisEmitter::diagnosis(LeftModule, RightModule, Ctx);
return 0;
}
static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath,
StringRef OutputPath,
CheckerOptions Opts,
@@ -2086,33 +2117,13 @@ static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath,
llvm::errs() << RightPath << " does not exist\n";
return 1;
}
llvm::raw_ostream *OS = &llvm::errs();
std::unique_ptr<llvm::raw_ostream> FileOS;
if (!OutputPath.empty()) {
std::error_code EC;
FileOS.reset(new llvm::raw_fd_ostream(OutputPath, EC, llvm::sys::fs::F_None));
OS = FileOS.get();
}
ModuleDifferDiagsConsumer PDC(true, *OS);
SDKContext Ctx(Opts);
Ctx.getDiags().addConsumer(PDC);
SwiftDeclCollector LeftCollector(Ctx);
LeftCollector.deSerialize(LeftPath);
SwiftDeclCollector RightCollector(Ctx);
RightCollector.deSerialize(RightPath);
auto LeftModule = LeftCollector.getSDKRoot();
auto RightModule = RightCollector.getSDKRoot();
TypeAliasDiffFinder(LeftModule, RightModule,
Ctx.getTypeAliasUpdateMap()).search();
PrunePass Prune(Ctx, std::move(ProtocolReqWhitelist));
Prune.pass(LeftModule, RightModule);
ChangeRefinementPass RefinementPass(Ctx.getNodeUpdateMap());
RefinementPass.pass(LeftModule, RightModule);
// Find member hoist changes to help refine diagnostics.
findTypeMemberDiffs(LeftModule, RightModule, Ctx.getTypeMemberDiffs());
DiagnosisEmitter::diagnosis(LeftModule, RightModule, Ctx);
diagnoseModuleChange(Ctx, LeftCollector.getSDKRoot(), RightCollector.getSDKRoot(),
OutputPath, std::move(ProtocolReqWhitelist));
return 0;
}
@@ -2261,7 +2272,8 @@ void anchorForGetMainExecutable() {}
static int prepareForDump(const char *Main,
CompilerInvocation &InitInvok,
llvm::StringSet<> &Modules) {
llvm::StringSet<> &Modules,
bool IsBaseline = false) {
InitInvok.setMainExecutablePath(fs::getMainExecutable(Main,
reinterpret_cast<void *>(&anchorForGetMainExecutable)));
InitInvok.setModuleName("swift_ide_test");
@@ -2311,17 +2323,18 @@ static int prepareForDump(const char *Main,
FramePaths.push_back({path, /*isSystem=*/true});
}
InitInvok.setFrameworkSearchPaths(FramePaths);
InitInvok.setImportSearchPaths(options::ModuleInputPaths);
if (IsBaseline) {
InitInvok.setImportSearchPaths(options::BaselineModuleInputPaths);
} else {
InitInvok.setImportSearchPaths(options::ModuleInputPaths);
}
if (!options::ModuleList.empty()) {
if (readFileLineByLine(options::ModuleList, Modules))
return 1;
return 1;
}
for (auto M : options::ModuleNames) {
Modules.insert(M);
}
if (Modules.empty()) {
llvm::errs() << "Need to specify -include-all or -module <name>\n";
return 1;
@@ -2384,6 +2397,15 @@ static CheckerOptions getCheckOpts() {
return Opts;
}
static SDKNodeRoot *getSDKRoot(const char *Main, SDKContext &Ctx,
CheckerOptions Opts, bool IsBaseline) {
CompilerInvocation Invok;
llvm::StringSet<> Modules;
if (prepareForDump(Main, Invok, Modules, IsBaseline))
return nullptr;
return getSDKNodeRoot(Ctx, Invok, Modules, Opts);
}
int main(int argc, char *argv[]) {
PROGRAM_START(argc, argv);
INITIALIZE_LLVM();
@@ -2405,7 +2427,8 @@ int main(int argc, char *argv[]) {
dumpSDKContent(InitInvok, Modules, options::OutputFile, Opts);
case ActionType::MigratorGen:
case ActionType::DiagnoseSDKs: {
if (options::SDKJsonPaths.size() != 2) {
bool CompareJson = options::SDKJsonPaths.size() == 2;
if (!CompareJson && options::BaselineModuleInputPaths.empty()) {
llvm::errs() << "Only two SDK versions can be compared\n";
llvm::cl::PrintHelpMessage();
return 1;
@@ -2419,11 +2442,18 @@ int main(int argc, char *argv[]) {
return generateMigrationScript(options::SDKJsonPaths[0],
options::SDKJsonPaths[1],
options::OutputFile, IgnoredUsrs, Opts);
else
else if (CompareJson)
return diagnoseModuleChange(options::SDKJsonPaths[0],
options::SDKJsonPaths[1],
options::OutputFile, Opts,
std::move(protocolWhitelist));
else {
SDKContext Ctx(Opts);
return diagnoseModuleChange(Ctx, getSDKRoot(argv[0], Ctx, Opts, true),
getSDKRoot(argv[0], Ctx, Opts, false),
options::OutputFile,
std::move(protocolWhitelist));
}
}
case ActionType::DeserializeSDK:
case ActionType::DeserializeDiffItems: {