Index: llvm/include/llvm/Object/TapiUniversal.h =================================================================== --- llvm/include/llvm/Object/TapiUniversal.h +++ llvm/include/llvm/Object/TapiUniversal.h @@ -101,6 +101,8 @@ return make_range(begin_objects(), end_objects()); } + MachO::InterfaceFile *getInterfaceFile() { return ParsedFile.get(); } + uint32_t getNumberOfObjects() const { return Libraries.size(); } static bool classof(const Binary *v) { return v->isTapiUniversal(); } Index: llvm/include/llvm/TextAPI/Platform.h =================================================================== --- llvm/include/llvm/TextAPI/Platform.h +++ llvm/include/llvm/TextAPI/Platform.h @@ -39,6 +39,8 @@ PlatformKind mapToPlatformKind(const Triple &Target); PlatformSet mapToPlatformSet(ArrayRef Targets); StringRef getPlatformName(PlatformKind Platform); +std::string getOSAndEnvironmentName(PlatformKind Platform, + std::string Version = ""); } // end namespace MachO. } // end namespace llvm. Index: llvm/include/llvm/TextAPI/Symbol.h =================================================================== --- llvm/include/llvm/TextAPI/Symbol.h +++ llvm/include/llvm/TextAPI/Symbol.h @@ -105,12 +105,17 @@ #endif bool operator==(const Symbol &O) const { - return (Kind == O.Kind) && (Name == O.Name) && (Targets == O.Targets) && - (Flags == O.Flags); + return std::tie(Name, Kind, Targets, Flags) == + std::tie(O.Name, O.Kind, O.Targets, O.Flags); } bool operator!=(const Symbol &O) const { return !(*this == O); } + bool operator<(const Symbol &O) const { + return std::tie(Name, Kind, Targets, Flags) < + std::tie(O.Name, O.Kind, O.Targets, O.Flags); + } + private: StringRef Name; TargetList Targets; Index: llvm/include/llvm/TextAPI/Target.h =================================================================== --- llvm/include/llvm/TextAPI/Target.h +++ llvm/include/llvm/TextAPI/Target.h @@ -60,6 +60,8 @@ PlatformSet mapToPlatformSet(ArrayRef Targets); ArchitectureSet mapToArchitectureSet(ArrayRef Targets); +std::string getTargetTriple(Target Targ); + raw_ostream &operator<<(raw_ostream &OS, const Target &Target); } // namespace MachO Index: llvm/lib/TextAPI/Platform.cpp =================================================================== --- llvm/lib/TextAPI/Platform.cpp +++ llvm/lib/TextAPI/Platform.cpp @@ -89,5 +89,33 @@ llvm_unreachable("Unknown llvm.MachO.PlatformKind enum"); } +std::string getOSAndEnvironmentName(PlatformKind Platform, + std::string Version) { + switch (Platform) { + case PlatformKind::unknown: + return "darwin" + Version; + case PlatformKind::macOS: + return "macos" + Version; + case PlatformKind::iOS: + return "ios" + Version; + case PlatformKind::tvOS: + return "tvos" + Version; + case PlatformKind::watchOS: + return "watchos" + Version; + case PlatformKind::bridgeOS: + return "bridgeos" + Version; + case PlatformKind::macCatalyst: + return "ios" + Version + "-macabi"; + case PlatformKind::iOSSimulator: + return "ios" + Version + "-simulator"; + case PlatformKind::tvOSSimulator: + return "tvos" + Version + "-simulator"; + case PlatformKind::watchOSSimulator: + return "watchos" + Version + "-simulator"; + case PlatformKind::driverKit: + return "driverkit" + Version; + } +} + } // end namespace MachO. } // end namespace llvm. Index: llvm/lib/TextAPI/Target.cpp =================================================================== --- llvm/lib/TextAPI/Target.cpp +++ llvm/lib/TextAPI/Target.cpp @@ -72,5 +72,11 @@ return Result; } +std::string getTargetTriple(Target Targ) { + return (getArchitectureName(Targ.Arch) + "-apple-" + + getOSAndEnvironmentName(Targ.Platform)) + .str(); +} + } // end namespace MachO. } // end namespace llvm. Index: llvm/test/tools/llvm-tapi-diff/Inputs/v4A.tbd =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/Inputs/v4A.tbd @@ -0,0 +1,50 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-macos, x86_64-ios ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-macos + value: 11111111-1111-1111-1111-111111111111 + - target: x86_64-ios + value: 11111111-1111-1111-1111-111111111111 +flags: [ flat_namespace, installapi ] +install-name: Umbrella.framework/Umbrella +current-version: 1.2.3 +compatibility-version: 1.2 +swift-abi-version: 5 +parent-umbrella: + - targets: [ i386-macos, x86_64-macos, x86_64-ios ] + umbrella: System +allowable-clients: + - targets: [ i386-macos, x86_64-macos, x86_64-ios ] + clients: [ ClientA ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] + - targets: [ x86_64-ios ] + symbols: [_symB] + - targets: [ x86_64-macos, x86_64-ios ] + symbols: [_symAB] +reexports: + - targets: [ i386-macos ] + symbols: [_symC] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +undefineds: + - targets: [ i386-macos ] + symbols: [ _symD ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +... \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/Inputs/v4B.tbd =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/Inputs/v4B.tbd @@ -0,0 +1,56 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios-simulator ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-ios-simulator + value: 11111111-1111-1111-1111-111111111111 +flags: [ installapi ] +install-name: 'Umbrella.framework/Umbrella' +current-version: 1.2.3 +compatibility-version: 0 +swift-abi-version: 5 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios-simulator ] + umbrella: System +allowable-clients: + - targets: [ i386-macos ] + clients: [ ClientA ] +reexported-libraries: + - targets: [ i386-macos ] + libraries: [ 'Alpine.framework/Alpine' ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [ Class1 ] + weak-symbols: [ _symC ] + - targets: [ x86_64-ios-simulator ] + symbols: [ _symB ] +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios-simulator ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-ios-simulator + value: 11111111-1111-1111-1111-111111111111 +flags: [] +install-name: 'Alpine.framework/Alpine' +current-version: 1.2.3 +compatibility-version: 0 +swift-abi-version: 5 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios-simulator ] + umbrella: System +allowable-clients: + - targets: [ i386-macos ] + clients: [ ClientD ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [ Class1 ] + weak-symbols: [ _symC ] + - targets: [ x86_64-ios-simulator ] + symbols: [ _symB ] +... \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/Inputs/v4C.tbd =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/Inputs/v4C.tbd @@ -0,0 +1,50 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-macos + value: 11111111-1111-1111-1111-111111111111 + - target: x86_64-ios + value: 22222222-2222-2222-2222-222222222222 +flags: [ installapi ] +install-name: Umbrella.framework/Umbrella +current-version: 1.3.3 +compatibility-version: 1.2 +swift-abi-version: 3 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios ] + umbrella: System +allowable-clients: + - targets: [ i386-macos, x86_64-ios ] + clients: [ ClientA ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] + - targets: [ x86_64-ios ] + symbols: [_symB] + - targets: [ x86_64-ios ] + symbols: [_symAB] +reexports: + - targets: [ i386-macos ] + symbols: [_symC] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +undefineds: + - targets: [ i386-macos ] + symbols: [ _symD ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +... \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/Inputs/v4D.tbd =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/Inputs/v4D.tbd @@ -0,0 +1,105 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios-simulator ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-ios-simulator + value: 11111111-1111-1111-1111-111111111111 +flags: [ installapi ] +install-name: 'Umbrella.framework/Umbrella' +current-version: 1.2.3 +compatibility-version: 0 +swift-abi-version: 5 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios-simulator ] + umbrella: System +allowable-clients: + - targets: [ i386-macos ] + clients: [ ClientA ] +reexported-libraries: + - targets: [ i386-macos, x86_64-ios-simulator ] + libraries: [ 'Alpine.framework/Alpine', 'System.framework/System' ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [ Class1 ] + weak-symbols: [ _symC ] + - targets: [ x86_64-ios-simulator ] + symbols: [ _symB ] +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios-simulator ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-ios-simulator + value: 11111111-1111-1111-1111-111111111111 +flags: [] +install-name: 'Alpine.framework/Alpine' +current-version: 1.2.3 +compatibility-version: 0 +swift-abi-version: 5 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios-simulator ] + umbrella: System +allowable-clients: + - targets: [ i386-macos ] + clients: [ ClientD ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [ Class1 ] + weak-symbols: [ _symC ] + - targets: [ x86_64-ios-simulator ] + symbols: [ _symB ] +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-macos + value: 11111111-1111-1111-1111-111111111111 + - target: x86_64-ios + value: 22222222-2222-2222-2222-222222222222 +flags: [] +install-name: 'System.framework/System' +current-version: 1.3.3 +compatibility-version: 1.2 +swift-abi-version: 3 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios ] + umbrella: System +allowable-clients: + - targets: [ i386-macos, x86_64-ios ] + clients: [ ClientA ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] + - targets: [ x86_64-ios ] + symbols: [_symB] + - targets: [ x86_64-ios ] + symbols: [_symAB] +reexports: + - targets: [ i386-macos ] + symbols: [_symC] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +undefineds: + - targets: [ i386-macos ] + symbols: [ _symD ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +... \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/Inputs/v4E.tbd =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/Inputs/v4E.tbd @@ -0,0 +1,56 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios-simulator ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-ios-simulator + value: 11111111-1111-1111-1111-111111111111 +flags: [ installapi ] +install-name: 'Umbrella.framework/Umbrella' +current-version: 1.2.3 +compatibility-version: 0 +swift-abi-version: 4 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios-simulator ] + umbrella: System +allowable-clients: + - targets: [ i386-macos ] + clients: [ ClientC ] +reexported-libraries: + - targets: [ i386-macos, x86_64-ios-simulator ] + libraries: [ Alpine.framework/Alpine ] +exports: + - targets: [ i386-macos ] + symbols: [ _symA ] + objc-classes: [ Class1 ] + weak-symbols: [ _symQ ] + - targets: [ x86_64-ios-simulator ] + symbols: [ _symB ] +--- !tapi-tbd +tbd-version: 4 +targets: [ i386-macos, x86_64-ios-simulator ] +uuids: + - target: i386-macos + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-ios-simulator + value: 11111111-1111-1111-1111-111111111111 +flags: [] +install-name: 'Alpine.framework/Alpine' +current-version: 1.4.3 +compatibility-version: 0 +swift-abi-version: 3 +parent-umbrella: + - targets: [ i386-macos, x86_64-ios-simulator ] + umbrella: System +allowable-clients: + - targets: [ i386-macos ] + clients: [ ClientB ] +exports: + - targets: [ i386-macos ] + symbols: [ _symE ] + objc-classes: [ Class1 ] + weak-symbols: [ _symC ] + - targets: [ x86_64-ios-simulator ] + symbols: [ _symB ] +... \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-matching-tbd.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-matching-tbd.test @@ -0,0 +1,4 @@ +; RUN: llvm-tapi-diff %S/Inputs/v4A.tbd %S/Inputs/v4A.tbd 2>&1 | FileCheck %s --allow-empty + +; CHECK-NOT: error: +; CHECK-NOT: warning: \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-mismatched-number-of-inlines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-mismatched-number-of-inlines.test @@ -0,0 +1,46 @@ +; RUN: NOT llvm-tapi-diff %S/Inputs/v4B.tbd %S/Inputs/v4D.tbd 2>&1 | FileCheck %s + +; CHECK:< {{.*}}/Inputs/v4B.tbd +; CHECK:> {{.*}}/Inputs/v4D.tbd + +; CHECK: Reexported Libraries +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > Alpine.framework/Alpine +; CHECK-NEXT: > System.framework/System +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > System.framework/System +; CHECK-NEXT:Inlined Reexported Frameworks/Libraries +; CHECK-NEXT: System.framework/System +; CHECK-NEXT: Current Version +; CHECK-NEXT: > 1.3.3 +; CHECK-NEXT: Compatibility Version +; CHECK-NEXT: > 1.2 +; CHECK-NEXT: Swift ABI Version +; CHECK-NEXT: > 3 +; CHECK-NEXT: InstallAPI +; CHECK-NEXT: > false +; CHECK-NEXT: Two Level Namespace +; CHECK-NEXT: > true +; CHECK-NEXT: Application Extension Safe +; CHECK-NEXT: > true +; CHECK-NEXT: Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > ClientA +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > ClientA +; CHECK-NEXT: Parent Umbrellas +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > System +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > System +; CHECK-NEXT: Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > _symC - Reexported +; CHECK-NEXT: > _symD - Undefined +; CHECK-NEXT: > _symA - None +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > _symAB - None +; CHECK-NEXT: > _symB - None + +; CHECK-NOT: error: +; CHECK-NOT: warning: \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-no-inlines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-no-inlines.test @@ -0,0 +1,26 @@ +; RUN: NOT llvm-tapi-diff %S/Inputs/v4A.tbd %S/Inputs/v4C.tbd 2>&1 | FileCheck %s + +; CHECK:< {{.*}}/Inputs/v4A.tbd +; CHECK:> {{.*}}/Inputs/v4C.tbd + +; CHECK:Current Version +; CHECK-NEXT:< 1.2.3 +; CHECK-NEXT:> 1.3.3 +; CHECK-NEXT:Swift ABI Version +; CHECK-NEXT:< 5 +; CHECK-NEXT:> 3 +; CHECK-NEXT:Two Level Namespace +; CHECK-NEXT:< false +; CHECK-NEXT:> true +; CHECK-NEXT:Allowable Clients +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < ClientA +; CHECK-NEXT:Parent Umbrellas +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < System +; CHECK-NEXT:Symbols +; CHECK-NEXT: x86_64-apple-macos +; CHECK: < _symAB - None + +; CHECK-NOT: error: +; CHECK-NOT: warning: Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-same-number-of-inlines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-same-number-of-inlines.test @@ -0,0 +1,38 @@ +; RUN: NOT llvm-tapi-diff %S/Inputs/v4B.tbd %S/Inputs/v4E.tbd 2>&1 | FileCheck %s + +; CHECK:< {{.*}}/Inputs/v4B.tbd +; CHECK:> {{.*}}/Inputs/v4E.tbd + +; CHECK:Swift ABI Version +; CHECK-NEXT:< 5 +; CHECK-NEXT:> 4 +; CHECK-NEXT:Reexported Libraries +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > Alpine.framework/Alpine +; CHECK-NEXT:Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < ClientA +; CHECK-NEXT: > ClientC +; CHECK-NEXT:Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < _symC - Weak-Defined +; CHECK-NEXT: > _symQ - Weak-Defined +; CHECK-NEXT:Inlined Reexported Frameworks/Libraries +; CHECK-NEXT: Alpine.framework/Alpine +; CHECK-NEXT: Current Version +; CHECK-NEXT: < 1.2.3 +; CHECK-NEXT: > 1.4.3 +; CHECK-NEXT: Swift ABI Version +; CHECK-NEXT: < 5 +; CHECK-NEXT: > 3 +; CHECK-NEXT: Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < ClientD +; CHECK-NEXT: > ClientB +; CHECK-NEXT: Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < _symA - None +; CHECK-NEXT: > _symE - None + +; CHECK-NOT: error: +; CHECK-NOT: warning: \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-multiple-inlines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-multiple-inlines.test @@ -0,0 +1,106 @@ +; RUN: NOT llvm-tapi-diff %S/Inputs/v4A.tbd %S/Inputs/v4D.tbd 2>&1 | FileCheck %s + +; CHECK:< {{.*}}/Inputs/v4A.tbd +; CHECK:> {{.*}}/Inputs/v4D.tbd + +; CHECK:Compatibility Version +; CHECK-NEXT:< 1.2 +; CHECK-NEXT:> 0 +; CHECK-NEXT:Two Level Namespace +; CHECK-NEXT:< false +; CHECK-NEXT:> true +; CHECK-NEXT:Reexported Libraries +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > Alpine.framework/Alpine +; CHECK-NEXT: > System.framework/System +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > Alpine.framework/Alpine +; CHECK-NEXT: > System.framework/System +; CHECK-NEXT:Allowable Clients +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < ClientA +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < ClientA +; CHECK-NEXT:Parent Umbrellas +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < System +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < System +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > System +; CHECK-NEXT:Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < _symC - Reexported +; CHECK-NEXT: < _symD - Undefined +; CHECK-NEXT: > _symC - Weak-Defined +; CHECK-NEXT: > .objc_class_name_Class1 - None +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < _symAB - None +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < _symAB - None +; CHECK-NEXT: < _symB - None +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > _symB - None +; CHECK-NEXT:Inlined Reexported Frameworks/Libraries +; CHECK-NEXT: Alpine.framework/Alpine +; CHECK-NEXT: Current Version +; CHECK-NEXT: > 1.2.3 +; CHECK-NEXT: Compatibility Version +; CHECK-NEXT: > 0 +; CHECK-NEXT: Swift ABI Version +; CHECK-NEXT: > 5 +; CHECK-NEXT: InstallAPI +; CHECK-NEXT: > false +; CHECK-NEXT: Two Level Namespace +; CHECK-NEXT: > true +; CHECK-NEXT: Application Extension Safe +; CHECK-NEXT: > true +; CHECK-NEXT: Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > ClientD +; CHECK-NEXT: Parent Umbrellas +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > System +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > System +; CHECK-NEXT: Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > _symC - Weak-Defined +; CHECK-NEXT: > .objc_class_name_Class1 - None +; CHECK-NEXT: > _symA - None +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > _symB - None +; CHECK-NEXT: System.framework/System +; CHECK-NEXT: Current Version +; CHECK-NEXT: > 1.3.3 +; CHECK-NEXT: Compatibility Version +; CHECK-NEXT: > 1.2 +; CHECK-NEXT: Swift ABI Version +; CHECK-NEXT: > 3 +; CHECK-NEXT: InstallAPI +; CHECK-NEXT: > false +; CHECK-NEXT: Two Level Namespace +; CHECK-NEXT: > true +; CHECK-NEXT: Application Extension Safe +; CHECK-NEXT: > true +; CHECK-NEXT: Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > ClientA +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > ClientA +; CHECK-NEXT: Parent Umbrellas +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > System +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > System +; CHECK-NEXT: Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > _symC - Reexported +; CHECK-NEXT: > _symD - Undefined +; CHECK-NEXT: > _symA - None +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > _symAB - None +; CHECK-NEXT: > _symB - None + +; CHECK-NOT: error: +; CHECK-NOT: warning: \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineA.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineA.test @@ -0,0 +1,73 @@ +; RUN: NOT llvm-tapi-diff %S/Inputs/v4A.tbd %S/Inputs/v4B.tbd 2>&1 | FileCheck %s + +; CHECK:< {{.*}}/Inputs/v4A.tbd +; CHECK:> {{.*}}/Inputs/v4B.tbd + +; CHECK:Compatibility Version +; CHECK-NEXT:< 1.2 +; CHECK-NEXT:> 0 +; CHECK-NEXT:Two Level Namespace +; CHECK-NEXT:< false +; CHECK-NEXT:> true +; CHECK-NEXT:Reexported Libraries +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > Alpine.framework/Alpine +; CHECK-NEXT:Allowable Clients +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < ClientA +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < ClientA +; CHECK-NEXT:Parent Umbrellas +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < System +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < System +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > System +; CHECK-NEXT:Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < _symC - Reexported +; CHECK-NEXT: < _symD - Undefined +; CHECK-NEXT: > _symC - Weak-Defined +; CHECK-NEXT: > .objc_class_name_Class1 - None +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < _symAB - None +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < _symAB - None +; CHECK-NEXT: < _symB - None +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > _symB +; CHECK-NEXT:Inlined Reexported Frameworks/Libraries +; CHECK-NEXT: Alpine.framework/Alpine +; CHECK-NEXT: Current Version +; CHECK-NEXT: > 1.2.3 +; CHECK-NEXT: Compatibility Version +; CHECK-NEXT: > 0 +; CHECK-NEXT: Swift ABI Version +; CHECK-NEXT: > 5 +; CHECK-NEXT: InstallAPI +; CHECK-NEXT: > false +; CHECK-NEXT: Two Level Namespace +; CHECK-NEXT: > true +; CHECK-NEXT: Application Extension Safe +; CHECK-NEXT: > true +; CHECK-NEXT: Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > ClientD +; CHECK-NEXT: Parent Umbrellas +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > System +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > System +; CHECK-NEXT: Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: > _symC - Weak-Defined +; CHECK-NEXT: > .objc_class_name_Class1 - None +; CHECK-NEXT: > _symA - None +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > _symB - None + + + +; CHECK-NOT: error: +; CHECK-NOT: warning: \ No newline at end of file Index: llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineB.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineB.test @@ -0,0 +1,68 @@ +; RUN: NOT llvm-tapi-diff %S/Inputs/v4B.tbd %S/Inputs/v4C.tbd 2>&1 | FileCheck %s + +; CHECK:< {{.*}}/Inputs/v4B.tbd +; CHECK:> {{.*}}/Inputs/v4C.tbd + +; CHECK:Current Version +; CHECK-NEXT:< 1.2.3 +; CHECK-NEXT:> 1.3.3 +; CHECK-NEXT:Compatibility Version +; CHECK-NEXT:< 0 +; CHECK-NEXT:> 1.2 +; CHECK-NEXT:Swift ABI Version +; CHECK-NEXT:< 5 +; CHECK-NEXT:> 3 +; CHECK-NEXT:Reexported Libraries +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < Alpine.framework/Alpine +; CHECK-NEXT:Allowable Clients +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > ClientA +; CHECK-NEXT:Parent Umbrellas +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: < System +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > System +; CHECK-NEXT:Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < _symC - Weak-Defined +; CHECK-NEXT: < .objc_class_name_Class1 - None +; CHECK-NEXT: > _symC - Reexported +; CHECK-NEXT: > _symD - Undefined +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: < _symB - None +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > _symAB - None +; CHECK-NEXT: > _symB - None +; CHECK-NEXT:Inlined Reexported Frameworks/Libraries +; CHECK-NEXT: Alpine.framework/Alpine +; CHECK-NEXT: Current Version +; CHECK-NEXT: < 1.2.3 +; CHECK-NEXT: Compatibility Version +; CHECK-NEXT: < 0 +; CHECK-NEXT: Swift ABI Version +; CHECK-NEXT: < 5 +; CHECK-NEXT: InstallAPI +; CHECK-NEXT: < false +; CHECK-NEXT: Two Level Namespace +; CHECK-NEXT: < true +; CHECK-NEXT: Application Extension Safe +; CHECK-NEXT: < true +; CHECK-NEXT: Allowable Clients +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < ClientD +; CHECK-NEXT: Parent Umbrellas +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < System +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: < System +; CHECK-NEXT: Symbols +; CHECK-NEXT: i386-apple-macos +; CHECK-NEXT: < _symC - Weak-Defined +; CHECK-NEXT: < .objc_class_name_Class1 - None +; CHECK-NEXT: < _symA - None +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: < _symB - None + +; CHECK-NOT: error: +; CHECK-NOT: warning: \ No newline at end of file Index: llvm/tools/llvm-tapi-diff/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi-diff/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Object + Support + TextAPI + ) + +add_llvm_tool(llvm-tapi-diff + llvm-tapi-diff.cpp + DiffEngine.cpp + ) \ No newline at end of file Index: llvm/tools/llvm-tapi-diff/DiffEngine.h =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi-diff/DiffEngine.h @@ -0,0 +1,201 @@ +//===-- DiffEngine.h - File comparator ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header defines the interface to the llvm-nm difference engine, +// which structurally compares two tbd files. (To be updated) +// +//===----------------------------------------------------------------------===/ +#ifndef LLVM_TOOLS_LLVM_NM_DIFF_DIFFENGINE_H +#define LLVM_TOOLS_LLVM_NM_DIFF_DIFFENGINE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/Target.h" + +namespace llvm { + +enum InterfaceInputOrder { lhs, rhs }; + +enum DiffAttrKind { + AD_Diff_Scalar_PackedVersion, + AD_Diff_Scalar_Unsigned, + AD_Diff_Scalar_Bool, + AD_Diff_Scalar_Str, + AD_Str_Vec, + AD_Sym_Vec, + AD_Inline_Doc, +}; + +class AttributeDiff { +public: + AttributeDiff(DiffAttrKind Kind) : Kind(Kind){}; + DiffAttrKind getKind() const { return Kind; } + +private: + const DiffAttrKind Kind; +}; + +class DiffOutput { +public: + std::string Name; + DiffAttrKind Kind; + std::vector> DiffVec; + DiffOutput(std::string Name) : Name(Name){}; +}; + +template class DiffScalarVal : public AttributeDiff { +public: + InterfaceInputOrder Order; + T Val; + DiffScalarVal(InterfaceInputOrder Order, T Val) + : AttributeDiff(U), Order(Order), Val(Val){}; + + static bool classof(const AttributeDiff *A) { return A->getKind() == U; } + + void print(raw_ostream &OS, std::string Indent) { + OS << Indent << "\t" << ((this->Order == lhs) ? "< " : "> ") << this->Val + << "\n"; + } +}; + +template <> +inline void +DiffScalarVal::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t\t" << ((this->Order == lhs) ? "< " : "> ") << this->Val + << "\n"; +} + +template <> +inline void +DiffScalarVal::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << ((this->Order == lhs) ? "< " : "> ") + << std::to_string(this->Val) << "\n"; +} + +template <> +inline void +DiffScalarVal::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << ((this->Order == lhs) ? "< " : "> ") + << ((this->Val == true) ? "true" : "false") << "\n"; +} + +class SymScalar { +public: + InterfaceInputOrder Order; + const MachO::Symbol *Val; + SymScalar(InterfaceInputOrder Order, const MachO::Symbol *Sym) + : Order(Order), Val(Sym){}; + + void print(raw_ostream &OS, std::string Indent, MachO::Target Targ) { + if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) { + if (Targ.Arch == MachO::AK_i386 && + Targ.Platform == MachO::PlatformKind::macOS) { + OS << Indent << "\t\t" << ((this->Order == lhs) ? "< " : "> ") + << ".objc_class_name_" << Val->getName() << " - " + << stringifySymbolFlag(Val->getFlags()) << "\n"; + return; + } + OS << Indent << "\t\t" << ((this->Order == lhs) ? "< " : "> ") + << "_OBJC_CLASS_$_" << Val->getName() << " - " + << stringifySymbolFlag(Val->getFlags()) << "\n"; + } + OS << Indent << "\t\t" << ((this->Order == lhs) ? "< " : "> ") + << stringifySymbolKind(Val->getKind()) << Val->getName() << " - " + << stringifySymbolFlag(Val->getFlags()) << "\n"; + } + +private: + std::string stringifySymbolKind(MachO::SymbolKind Kind) { + switch (Kind) { + case MachO::SymbolKind::GlobalSymbol: + return ""; + case MachO::SymbolKind::ObjectiveCClass: + return "_OBJC_METACLASS_$_"; + case MachO::SymbolKind ::ObjectiveCClassEHType: + return "_OBJC_EHTYPE_$_"; + case MachO::SymbolKind ::ObjectiveCInstanceVariable: + return "_OBJC_IVAR_$_"; + } + } + + std::string stringifySymbolFlag(MachO::SymbolFlags Flag) { + switch (Flag) { + case MachO::SymbolFlags::None: + return "None"; + case MachO::SymbolFlags::ThreadLocalValue: + return "Thread-Local"; + case MachO::SymbolFlags::WeakDefined: + return "Weak-Defined"; + case MachO::SymbolFlags::WeakReferenced: + return "Weak-Referenced"; + case MachO::SymbolFlags::Undefined: + return "Undefined"; + case MachO::SymbolFlags::Rexported: + return "Reexported"; + } + } +}; + +class DiffStrVec : public AttributeDiff { +public: + MachO::Target Targ; + std::vector> DiffVec; + DiffStrVec(MachO::Target Targ) : AttributeDiff(AD_Str_Vec), Targ(Targ){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Str_Vec; + } +}; + +class DiffSymVec : public AttributeDiff { +public: + MachO::Target Targ; + std::vector DiffVec; + DiffSymVec(MachO::Target Targ) : AttributeDiff(AD_Sym_Vec), Targ(Targ){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Sym_Vec; + } +}; + +class InlineDoc : public AttributeDiff { +public: + std::string InstallName; + std::vector DiffVec; + InlineDoc(StringRef InstName, std::vector Diff) + : AttributeDiff(AD_Inline_Doc), InstallName(InstName), + DiffVec(std::move(Diff)){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Inline_Doc; + } +}; + +class DiffEngine { +public: + DiffEngine(object::TapiUniversal *InputFileNameOne, + object::TapiUniversal *InputFileNameTwo) + : FileOne(InputFileNameOne), FileTwo(InputFileNameTwo){}; + bool compareFiles(); + +private: + object::TapiUniversal *FileOne; + object::TapiUniversal *FileTwo; + + void printDifferences(std::vector, int); + std::vector findDifferences(MachO::InterfaceFile *, + MachO::InterfaceFile *); +}; + +} // namespace llvm + +#endif \ No newline at end of file Index: llvm/tools/llvm-tapi-diff/DiffEngine.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi-diff/DiffEngine.cpp @@ -0,0 +1,426 @@ +//===-- DiffEngine.cpp - Structural file comparison ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header defines the implementation of the llvm-nm difference +// engine, which structurally compares two tbd files. (To be updated) +// +//===----------------------------------------------------------------------===/ +#include "DiffEngine.h" +#include "llvm/Support/Casting.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/Target.h" + +using namespace llvm; +using namespace MachO; +using namespace object; + +bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS) { + if (std::distance(LHS.begin(), LHS.end()) != + (std::distance(RHS.begin(), RHS.end()))) + return false; + return std::equal(LHS.begin(), LHS.end(), RHS.begin(), + [&](auto LHS, auto RHS) { return *LHS == *RHS; }); +} + +template +void addValToDiffVec(V Val, Target Targ, DiffOutput &Diff, + InterfaceInputOrder Order) { + auto TargetVector = llvm::find_if( + Diff.DiffVec, [&](const std::unique_ptr &RawTVec) { + if (T *TVec = dyn_cast(RawTVec.get())) + return TVec->Targ == Targ; + return false; + }); + if (TargetVector != Diff.DiffVec.end()) { + U NewTargetVec(Order, Val); + cast(TargetVector->get())->DiffVec.push_back(NewTargetVec); + } else { + auto NewTargetVec = std::make_unique(Targ); + U NewNameVec(Order, Val); + NewTargetVec->DiffVec.push_back(NewNameVec); + Diff.DiffVec.push_back(std::move(NewTargetVec)); + } +} + +DiffOutput getSingleAttrDiff(const std::vector &IRefVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &IRef : IRefVec) + for (auto Targ : IRef.targets()) + addValToDiffVec>( + IRef.getInstallName(), Targ, Diff, Order); + return Diff; +} + +DiffOutput +getSingleAttrDiff(const std::vector> &PairVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &Pair : PairVec) + addValToDiffVec>( + StringRef(Pair.second), Pair.first, Diff, Order); + return Diff; +} + +DiffOutput +getSingleAttrDiff(llvm::MachO::InterfaceFile::const_symbol_range SymRange, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Sym_Vec; + for (const auto *Sym : SymRange) + for (auto Targ : Sym->targets()) + addValToDiffVec(Sym, Targ, Diff, Order); + return Diff; +} + +template +DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) { + DiffOutput Diff(Attribute); + Diff.Kind = SingleAttr.getKind(); + Diff.DiffVec.push_back(std::make_unique(SingleAttr)); + return Diff; +} + +std::vector getSingleIF(InterfaceFile *Interface, + InterfaceInputOrder Order) { + std::vector ReturnedOutput; + ReturnedOutput.push_back( + getSingleAttrDiff(DiffScalarVal( + Order, Interface->getInstallName()), + "Install Name")); + ReturnedOutput.push_back(getSingleAttrDiff( + DiffScalarVal( + Order, Interface->getCurrentVersion()), + "Current Version")); + ReturnedOutput.push_back(getSingleAttrDiff( + DiffScalarVal( + Order, Interface->getCompatibilityVersion()), + "Compatibility Version")); + ReturnedOutput.push_back( + getSingleAttrDiff(DiffScalarVal( + Order, Interface->getSwiftABIVersion()), + "Swift ABI Version")); + ReturnedOutput.push_back( + getSingleAttrDiff(DiffScalarVal( + Order, Interface->isInstallAPI()), + "InstallAPI")); + ReturnedOutput.push_back( + getSingleAttrDiff(DiffScalarVal( + Order, Interface->isTwoLevelNamespace()), + "Two Level Namespace")); + ReturnedOutput.push_back( + getSingleAttrDiff(DiffScalarVal( + Order, Interface->isApplicationExtensionSafe()), + "Application Extension Safe")); + ReturnedOutput.push_back(getSingleAttrDiff(Interface->reexportedLibraries(), + "Reexported Libraries", Order)); + ReturnedOutput.push_back(getSingleAttrDiff(Interface->allowableClients(), + "Allowable Clients", Order)); + ReturnedOutput.push_back( + getSingleAttrDiff(Interface->umbrellas(), "Parent Umbrellas", Order)); + ReturnedOutput.push_back( + getSingleAttrDiff(Interface->symbols(), "Symbols", Order)); + if (!Interface->documents().empty()) + for (auto Doc : Interface->documents()) { + DiffOutput Documents("Inlined Reexported Frameworks/Libraries"); + Documents.Kind = AD_Inline_Doc; + Documents.DiffVec.push_back(std::make_unique( + InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order)))); + ReturnedOutput.push_back(std::move(Documents)); + } + return ReturnedOutput; +} + +void findAndAddDiff(const std::vector &CollectedIRefVec, + const std::vector &LookupIRefVec, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &IRef : CollectedIRefVec) + for (auto Targ : IRef.targets()) { + auto FoundIRef = llvm::find_if(LookupIRefVec, [&](const auto LIRef) { + auto FoundTarg = llvm::find(LIRef.targets(), Targ); + return (FoundTarg != LIRef.targets().end() && + IRef.getInstallName() == LIRef.getInstallName()); + }); + if (FoundIRef == LookupIRefVec.end()) + addValToDiffVec>( + IRef.getInstallName(), Targ, Result, Order); + } +} + +void findAndAddDiff( + const std::vector> &CollectedPairs, + const std::vector> &LookupPairs, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &Pair : CollectedPairs) { + auto FoundPair = llvm::find(LookupPairs, Pair); + if (FoundPair == LookupPairs.end()) + addValToDiffVec>( + StringRef(Pair.second), Pair.first, Result, Order); + } +} + +void findAndAddDiff( + llvm::MachO::InterfaceFile::const_symbol_range CollectedSyms, + llvm::MachO::InterfaceFile::const_symbol_range LookupSyms, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Sym_Vec; + for (const auto *Sym : CollectedSyms) + for (const auto Targ : Sym->targets()) { + auto FoundSym = llvm::find_if(LookupSyms, [&](const auto LSym) { + auto FoundTarg = llvm::find(LSym->targets(), Targ); + return (Sym->getName() == LSym->getName() && + Sym->getKind() == LSym->getKind() && + Sym->getFlags() == LSym->getFlags() && + FoundTarg != LSym->targets().end()); + }); + if (FoundSym == LookupSyms.end()) + addValToDiffVec(Sym, Targ, Result, Order); + } +} + +template +DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) { + DiffOutput Diff(Attr); + if (LHS.getKind() == RHS.getKind()) { + Diff.Kind = LHS.getKind(); + Diff.DiffVec.push_back(std::make_unique(LHS)); + Diff.DiffVec.push_back(std::make_unique(RHS)); + } + return Diff; +} + +template +DiffOutput recordDifferences(const std::vector &LHS, + const std::vector &RHS, std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Str_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS, + std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Sym_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +std::vector DiffEngine::findDifferences(InterfaceFile *IF1, + InterfaceFile *IF2) { + std::vector ReturnedOutput; + if (IF1->getInstallName() != IF2->getInstallName()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal(lhs, + IF1->getInstallName()), + DiffScalarVal(rhs, + IF2->getInstallName()), + "Install Name")); + + if (IF1->getCurrentVersion() != IF2->getCurrentVersion()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal( + lhs, IF1->getCurrentVersion()), + DiffScalarVal( + rhs, IF2->getCurrentVersion()), + "Current Version")); + if (IF1->getCompatibilityVersion() != IF2->getCompatibilityVersion()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal( + lhs, IF1->getCompatibilityVersion()), + DiffScalarVal( + rhs, IF2->getCompatibilityVersion()), + "Compatibility Version")); + if (IF1->getSwiftABIVersion() != IF2->getSwiftABIVersion()) + ReturnedOutput.push_back( + recordDifferences(DiffScalarVal( + lhs, IF1->getSwiftABIVersion()), + DiffScalarVal( + rhs, IF2->getSwiftABIVersion()), + "Swift ABI Version")); + if (IF1->isInstallAPI() != IF2->isInstallAPI()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal(lhs, IF1->isInstallAPI()), + DiffScalarVal(rhs, IF2->isInstallAPI()), + "InstallAPI")); + + if (IF1->isTwoLevelNamespace() != IF2->isTwoLevelNamespace()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal(lhs, + IF1->isTwoLevelNamespace()), + DiffScalarVal(rhs, + IF2->isTwoLevelNamespace()), + "Two Level Namespace")); + + if (IF1->isApplicationExtensionSafe() != IF2->isApplicationExtensionSafe()) + ReturnedOutput.push_back( + recordDifferences(DiffScalarVal( + lhs, IF1->isApplicationExtensionSafe()), + DiffScalarVal( + rhs, IF2->isApplicationExtensionSafe()), + "Application Extension Safe")); + + if (IF1->reexportedLibraries() != IF2->reexportedLibraries()) + ReturnedOutput.push_back(recordDifferences(IF1->reexportedLibraries(), + IF2->reexportedLibraries(), + "Reexported Libraries")); + + if (IF1->allowableClients() != IF2->allowableClients()) + ReturnedOutput.push_back(recordDifferences( + IF1->allowableClients(), IF2->allowableClients(), "Allowable Clients")); + + if (IF1->umbrellas() != IF2->umbrellas()) + ReturnedOutput.push_back(recordDifferences( + IF1->umbrellas(), IF2->umbrellas(), "Parent Umbrellas")); + + if (!checkSymbolEquality(IF1->symbols(), IF2->symbols())) + ReturnedOutput.push_back( + recordDifferences(IF1->symbols(), IF2->symbols(), "Symbols")); + + if (IF1->documents() != IF2->documents()) { + DiffOutput Docs("Inlined Reexported Frameworks/Libraries"); + Docs.Kind = AD_Inline_Doc; + std::vector DocsInserted; + // Iterate through inline frameworks/libraries from interface file and find + // match based on install name. + for (auto Doc1 : IF1->documents()) { + auto Pair = llvm::find_if(IF2->documents(), [&](const auto &Doc2) { + return (Doc1->getInstallName() == Doc2->getInstallName()); + }); + // If a match found, recursively get differences between the pair. + if (Pair != IF2->documents().end()) { + InlineDoc PairDiff = InlineDoc( + Doc1->getInstallName(), findDifferences(Doc1.get(), Pair->get())); + if (!PairDiff.DiffVec.empty()) + Docs.DiffVec.push_back( + std::make_unique(std::move(PairDiff))); + } + // If a match is not found, get attributes from single item. + else + Docs.DiffVec.push_back(std::make_unique( + InlineDoc(Doc1->getInstallName(), getSingleIF(Doc1.get(), lhs)))); + DocsInserted.push_back(Doc1->getInstallName()); + } + for (auto Doc2 : IF2->documents()) { + auto WasGathered = + llvm::find_if(DocsInserted, [&](const auto &GatheredDoc) { + return (GatheredDoc == Doc2->getInstallName()); + }); + if (WasGathered == DocsInserted.end()) + Docs.DiffVec.push_back(std::make_unique( + InlineDoc(Doc2->getInstallName(), getSingleIF(Doc2.get(), rhs)))); + } + if (!Docs.DiffVec.empty()) { + ReturnedOutput.push_back(std::move(Docs)); + } + } + return ReturnedOutput; +} + +template +void printSingleVal(std::string Indent, DiffOutput &Attr, raw_ostream &OS) { + if (Attr.DiffVec.empty()) + return; + OS << Indent << Attr.Name << "\n"; + for (auto &RawItem : Attr.DiffVec) + if (T *Item = dyn_cast(RawItem.get())) + Item->print(OS, Indent); +} + +template +void printVecVal(std::string Indent, DiffOutput &Attr, raw_ostream &OS) { + if (Attr.DiffVec.empty()) + return; + OS << Indent << Attr.Name << "\n"; + for (auto &Item : Attr.DiffVec) + if (T *Vec = dyn_cast(Item.get())) { + OS << Indent << "\t" << getTargetTriple(Vec->Targ) << "\n"; + for (auto &Item : Vec->DiffVec) + Item.print(OS, Indent); + } +} + +template <> +void printVecVal(std::string Indent, DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.DiffVec.empty()) + return; + OS << Indent << Attr.Name << "\n"; + for (auto &RawSymVec : Attr.DiffVec) + if (DiffSymVec *SymVec = dyn_cast(RawSymVec.get())) { + OS << Indent << "\t" << getTargetTriple(SymVec->Targ) << "\n"; + for (auto &Item : SymVec->DiffVec) + Item.print(OS, Indent, SymVec->Targ); + } +} + +void DiffEngine::printDifferences(std::vector Diffs, + int IndentCounter) { + std::string Indent = std::string(IndentCounter, '\t'); + raw_ostream &OS = outs(); + for (auto &Attr : Diffs) { + switch (Attr.Kind) { + case AD_Diff_Scalar_Str: + if (IndentCounter == 0) + printSingleVal>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_PackedVersion: + printSingleVal< + DiffScalarVal>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Unsigned: + printSingleVal>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Bool: + printSingleVal>(Indent, Attr, + OS); + break; + case AD_Str_Vec: + printVecVal(Indent, Attr, OS); + break; + case AD_Sym_Vec: + printVecVal(Indent, Attr, OS); + break; + case AD_Inline_Doc: + if (!Attr.DiffVec.empty()) { + OS << Indent << Attr.Name << "\n"; + for (auto &Item : Attr.DiffVec) + if (InlineDoc *Doc = dyn_cast(Item.get())) + if (!Doc->DiffVec.empty()) { + OS << Indent << "\t" << Doc->InstallName << "\n"; + printDifferences(std::move(Doc->DiffVec), 2); + } + } + break; + } + } +} + +bool DiffEngine::compareFiles() { + auto *IF1 = FileOne->getInterfaceFile(); + auto *IF2 = FileTwo->getInterfaceFile(); + if (*IF1 == *IF2) + return false; + outs() << "< " << std::string(IF1->getPath().data()) << "\n> " + << std::string(IF2->getPath().data()) << "\n\n"; + std::vector Diffs = findDifferences(IF1, IF2); + printDifferences(std::move(Diffs), 0); + return true; +} Index: llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp @@ -0,0 +1,80 @@ +//===-- llvm-nm-diff.cpp - symbol table comparator command-line driver ---*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the command-line driver for the llvm-nm difference engine. +// +//===----------------------------------------------------------------------===// +#include "DiffEngine.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" + +using namespace llvm; +using namespace MachO; +using namespace object; + +namespace { +cl::OptionCategory NMCat("llvm-tapi-diff Options"); +cl::opt InputFileNameOne(cl::Positional, cl::desc(""), + cl::cat(NMCat)); +cl::opt InputFileNameTwo(cl::Positional, cl::desc(""), + cl::cat(NMCat)); +} // anonymous namespace + +static bool error(std::error_code EC, std::string Path) { + if (EC) { + errs() << Path << ": " << EC.message() << "\n"; + return true; + } + return false; +} + +static void error(llvm::Error E, std::string FileName) { + logAllUnhandledErrors(std::move(E), errs(), (FileName + ": ")); +} + +std::unique_ptr convertFileToBinary(std::string &Filename) { + ErrorOr> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (error(BufferOrErr.getError(), Filename)) + return {}; + + Expected> BinaryOrErr = + createBinary(BufferOrErr.get()->getMemBufferRef()); + if (!BinaryOrErr) { + error(BinaryOrErr.takeError(), Filename); + return {}; + } + return std::move(*BinaryOrErr); +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + cl::HideUnrelatedOptions(NMCat); + cl::ParseCommandLineOptions( + argc, argv, + "This tool will compare two tbd files and return the " + "differences in those files."); + if (InputFileNameOne.empty() || InputFileNameTwo.empty()) { + cl::PrintHelpMessage(); + return 1; + } + + auto BinOne = convertFileToBinary(InputFileNameOne); + auto BinTwo = convertFileToBinary(InputFileNameTwo); + + if (!BinOne || !BinTwo) + return 1; + if (TapiUniversal *FileOne = dyn_cast(BinOne.get())) + if (TapiUniversal *FileTwo = dyn_cast(BinTwo.get())) { + DiffEngine Engine(FileOne, FileTwo); + return Engine.compareFiles(); + } + return 1; +}