diff --git a/llvm/include/llvm/Object/TapiUniversal.h b/llvm/include/llvm/Object/TapiUniversal.h --- a/llvm/include/llvm/Object/TapiUniversal.h +++ b/llvm/include/llvm/Object/TapiUniversal.h @@ -101,6 +101,8 @@ return make_range(begin_objects(), end_objects()); } + const MachO::InterfaceFile &getInterfaceFile() { return *ParsedFile; } + uint32_t getNumberOfObjects() const { return Libraries.size(); } static bool classof(const Binary *v) { return v->isTapiUniversal(); } diff --git a/llvm/include/llvm/TextAPI/Platform.h b/llvm/include/llvm/TextAPI/Platform.h --- a/llvm/include/llvm/TextAPI/Platform.h +++ b/llvm/include/llvm/TextAPI/Platform.h @@ -40,6 +40,8 @@ PlatformSet mapToPlatformSet(ArrayRef Targets); StringRef getPlatformName(PlatformKind Platform); PlatformKind getPlatformFromName(StringRef Name); +std::string getOSAndEnvironmentName(PlatformKind Platform, + std::string Version = ""); } // end namespace MachO. } // end namespace llvm. diff --git a/llvm/include/llvm/TextAPI/Symbol.h b/llvm/include/llvm/TextAPI/Symbol.h --- a/llvm/include/llvm/TextAPI/Symbol.h +++ b/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; diff --git a/llvm/include/llvm/TextAPI/Target.h b/llvm/include/llvm/TextAPI/Target.h --- a/llvm/include/llvm/TextAPI/Target.h +++ b/llvm/include/llvm/TextAPI/Target.h @@ -60,6 +60,8 @@ PlatformSet mapToPlatformSet(ArrayRef Targets); ArchitectureSet mapToArchitectureSet(ArrayRef Targets); +std::string getTargetTripleName(const Target &Targ); + raw_ostream &operator<<(raw_ostream &OS, const Target &Target); } // namespace MachO diff --git a/llvm/lib/TextAPI/Platform.cpp b/llvm/lib/TextAPI/Platform.cpp --- a/llvm/lib/TextAPI/Platform.cpp +++ b/llvm/lib/TextAPI/Platform.cpp @@ -105,5 +105,34 @@ .Default(PlatformKind::unknown); } +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; + } + llvm_unreachable("Unknown llvm::MachO::PlatformKind enum"); +} + } // end namespace MachO. } // end namespace llvm. diff --git a/llvm/lib/TextAPI/Target.cpp b/llvm/lib/TextAPI/Target.cpp --- a/llvm/lib/TextAPI/Target.cpp +++ b/llvm/lib/TextAPI/Target.cpp @@ -72,5 +72,11 @@ return Result; } +std::string getTargetTripleName(const Target &Targ) { + return (getArchitectureName(Targ.Arch) + "-apple-" + + getOSAndEnvironmentName(Targ.Platform)) + .str(); +} + } // end namespace MachO. } // end namespace llvm. diff --git a/llvm/test/tools/llvm-tapi-diff/Inputs/macho.yaml b/llvm/test/tools/llvm-tapi-diff/Inputs/macho.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/Inputs/macho.yaml @@ -0,0 +1,141 @@ +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x1000007 + cpusubtype: 0x3 + filetype: 0x6 + ncmds: 13 + sizeofcmds: 584 + flags: 0x100085 + reserved: 0x0 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __TEXT + vmaddr: 0 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 7 + initprot: 5 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x1000 + size: 0 + offset: 0x1000 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: '' + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4096 + vmsize: 4096 + fileoff: 4096 + filesize: 112 + maxprot: 7 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_ID_DYLIB + cmdsize: 48 + dylib: + name: 24 + timestamp: 1 + current_version: 65536 + compatibility_version: 65536 + PayloadString: macho-no-exports.dylib + ZeroPadBytes: 2 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 0 + export_size: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 4168 + nsyms: 1 + stroff: 4184 + strsize: 24 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 0 + iundefsym: 0 + nundefsym: 1 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_UUID + cmdsize: 24 + uuid: A24CBA21-865D-372D-B267-4964F4ADCDFC + - cmd: LC_VERSION_MIN_MACOSX + cmdsize: 16 + version: 657920 + sdk: 657920 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 79495168 + compatibility_version: 65536 + PayloadString: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 4096 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 4104 + datasize: 0 + - cmd: LC_DYLIB_CODE_SIGN_DRS + cmdsize: 16 + dataoff: 4104 + datasize: 64 +LinkEditData: + NameList: + - n_strx: 2 + n_type: 0x1 + n_sect: 0 + n_desc: 256 + n_value: 0 + StringTable: + - ' ' + - dyld_stub_binder + - '' + - '' + - '' + - '' + - '' +... diff --git a/llvm/test/tools/llvm-tapi-diff/Inputs/v4A.tbd b/llvm/test/tools/llvm-tapi-diff/Inputs/v4A.tbd new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/Inputs/v4A.tbd @@ -0,0 +1,49 @@ +--- !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: [] diff --git a/llvm/test/tools/llvm-tapi-diff/Inputs/v4B.tbd b/llvm/test/tools/llvm-tapi-diff/Inputs/v4B.tbd new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/Inputs/v4B.tbd @@ -0,0 +1,55 @@ +--- !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 ] diff --git a/llvm/test/tools/llvm-tapi-diff/Inputs/v4C.tbd b/llvm/test/tools/llvm-tapi-diff/Inputs/v4C.tbd new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/Inputs/v4C.tbd @@ -0,0 +1,49 @@ +--- !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: [] diff --git a/llvm/test/tools/llvm-tapi-diff/Inputs/v4D.tbd b/llvm/test/tools/llvm-tapi-diff/Inputs/v4D.tbd new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/Inputs/v4D.tbd @@ -0,0 +1,104 @@ +--- !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: [] diff --git a/llvm/test/tools/llvm-tapi-diff/Inputs/v4E.tbd b/llvm/test/tools/llvm-tapi-diff/Inputs/v4E.tbd new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/Inputs/v4E.tbd @@ -0,0 +1,55 @@ +--- !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 ] diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-incorrect-format.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-incorrect-format.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/tapi-diff-incorrect-format.test @@ -0,0 +1,7 @@ +; RUN: mkdir -p %t +; RUN: yaml2obj %S/Inputs/macho.yaml -o %t/macho.dylib +; RUN: not llvm-tapi-diff %S/Inputs/v4A.tbd %t/macho.dylib 2>&1 | FileCheck %s + +; CHECK: {{.*}}: error: {{.*}}/macho.dylib: Error when parsing file, unsupported file format +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-matching-tbd.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-matching-tbd.test new file mode 100644 --- /dev/null +++ b/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: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-mismatched-number-of-inlines.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-mismatched-number-of-inlines.test new file mode 100644 --- /dev/null +++ b/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 +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > _symAB +; CHECK-NEXT: > _symB + +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-misspelled-tbd.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-misspelled-tbd.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-tapi-diff/tapi-diff-misspelled-tbd.test @@ -0,0 +1,5 @@ +; RUN: not llvm-tapi-diff %S/Inputs/v4A.tbd %S/Inputs/v4.tbd 2>&1 | FileCheck %s + +; CHECK: {{.*}}: error: {{.*}}/Inputs/v4.tbd: No such file or directory +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-no-inlines.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-no-inlines.test new file mode 100644 --- /dev/null +++ b/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 + +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-same-number-of-inlines.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-same-number-of-inlines.test new file mode 100644 --- /dev/null +++ b/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 +; CHECK-NEXT: > _symE + +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-multiple-inlines.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-multiple-inlines.test new file mode 100644 --- /dev/null +++ b/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 +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < _symAB +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < _symAB +; CHECK-NEXT: < _symB +; 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 +; CHECK-NEXT: > _symA +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > _symB +; 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 +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > _symAB +; CHECK-NEXT: > _symB + +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineA.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineA.test new file mode 100644 --- /dev/null +++ b/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 +; CHECK-NEXT: x86_64-apple-macos +; CHECK-NEXT: < _symAB +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: < _symAB +; CHECK-NEXT: < _symB +; 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 +; CHECK-NEXT: > _symA +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: > _symB + + + +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineB.test b/llvm/test/tools/llvm-tapi-diff/tapi-diff-tbd-has-single-inlineB.test new file mode 100644 --- /dev/null +++ b/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 +; CHECK-NEXT: > _symC - Reexported +; CHECK-NEXT: > _symD - Undefined +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: < _symB +; CHECK-NEXT: x86_64-apple-ios +; CHECK-NEXT: > _symAB +; 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 +; CHECK-NEXT: < _symA +; CHECK-NEXT: x86_64-apple-ios-simulator +; CHECK-NEXT: < _symB + +; CHECK-NOT: error: +; CHECK-NOT: warning: diff --git a/llvm/tools/llvm-tapi-diff/CMakeLists.txt b/llvm/tools/llvm-tapi-diff/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/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 + ) diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.h b/llvm/tools/llvm-tapi-diff/DiffEngine.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/DiffEngine.h @@ -0,0 +1,231 @@ +//===-- 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-tapi difference engine, +// which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#ifndef LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H +#define LLVM_TOOLS_LLVM_TAPI_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 { + +/// InterfaceInputOrder determines from which file the diff attribute belongs +/// to. +enum InterfaceInputOrder { lhs, rhs }; + +/// DiffAttrKind is the enum that holds the concrete bases for RTTI. +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, +}; + +/// AttributeDiff is the abstract class for RTTI. +class AttributeDiff { +public: + AttributeDiff(DiffAttrKind Kind) : Kind(Kind){}; + DiffAttrKind getKind() const { return Kind; } + +private: + const DiffAttrKind Kind; +}; + +/// DiffOutput is the representation of a diff for a single attribute. +struct DiffOutput { + /// The name of the attribute. + std::string Name; + /// The kind for RTTI + DiffAttrKind Kind; + /// Different scalar values (DiffScalarVal or SymScalar) for the attribute + /// from each file where a diff is present. + std::vector> DiffVec; + DiffOutput(std::string Name) : Name(Name){}; +}; + +/// DiffScalarVal is a template class for the different types of scalar values. +template class DiffScalarVal : public AttributeDiff { +public: + /// The order is the file from which the diff is found + 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"; +} + +/// SymScalar is the diff symbol and the order. j +class SymScalar { +public: + /// The order is the file from which the diff is found. + InterfaceInputOrder Order; + const MachO::Symbol *Val; + SymScalar(InterfaceInputOrder Order, const MachO::Symbol *Sym) + : Order(Order), Val(Sym){}; + + std::string getFlagString(MachO::SymbolFlags Flags) { + return Flags != MachO::SymbolFlags::None + ? " - " + stringifySymbolFlag(Flags) + : stringifySymbolFlag(Flags); + } + + 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() + << getFlagString(Val->getFlags()) << "\n"; + return; + } + OS << Indent << "\t\t" << ((this->Order == lhs) ? "< " : "> ") + << "_OBJC_CLASS_$_" << Val->getName() << getFlagString(Val->getFlags()) + << "\n"; + } + OS << Indent << "\t\t" << ((this->Order == lhs) ? "< " : "> ") + << stringifySymbolKind(Val->getKind()) << Val->getName() + << getFlagString(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_$_"; + } + llvm_unreachable("Unknown llvm::MachO::SymbolKind enum"); + } + + std::string stringifySymbolFlag(MachO::SymbolFlags Flag) { + switch (Flag) { + case MachO::SymbolFlags::None: + return ""; + 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"; + } + llvm_unreachable("Unknown llvm::MachO::SymbolFlags enum"); + } +}; + +class DiffStrVec : public AttributeDiff { +public: + MachO::Target Targ; + /// vector of StringRef values associated with the target + 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; + /// vector of symbol values associated with the target + 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; + } +}; + +/// InlineDoc represents an inlined framework/library in a TBD File. +class InlineDoc : public AttributeDiff { +public: + /// Install name of the framework/library. + std::string InstallName; + /// Differences found from each file. + 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; + } +}; + +/// DiffEngine contains the methods to compare the input files and print the +/// output of the differences found in the files. +class DiffEngine { +public: + DiffEngine(object::TapiUniversal *InputFileNameLHS, + object::TapiUniversal *InputFileNameRHS) + : FileLHS(InputFileNameLHS), FileRHS(InputFileNameRHS){}; + bool compareFiles(); + +private: + object::TapiUniversal *FileLHS; + object::TapiUniversal *FileRHS; + + /// Function that prints the differences found in the files + void printDifferences(std::vector, int); + /// Function that does the comparison of the TBD files and returns the + /// differences + std::vector findDifferences(const MachO::InterfaceFile *, + const MachO::InterfaceFile *); +}; + +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.cpp b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp @@ -0,0 +1,425 @@ +//===-- 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-tapi difference +// engine, which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#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) { + 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 (TargetVecT *TVec = dyn_cast(RawTVec.get())) + return TVec->Targ == Targ; + return false; + }); + if (TargetVector != Diff.DiffVec.end()) { + ValVecT NewValVec(Order, Val); + cast(TargetVector->get())->DiffVec.push_back(NewValVec); + } else { + auto NewTargetVec = std::make_unique(Targ); + ValVecT NewValVec(Order, Val); + NewTargetVec->DiffVec.push_back(NewValVec); + 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(const InterfaceFile *IFLHS, + const InterfaceFile *IFRHS) { + std::vector ReturnedOutput; + if (IFLHS->getInstallName() != IFRHS->getInstallName()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal(lhs, + IFLHS->getInstallName()), + DiffScalarVal(rhs, + IFRHS->getInstallName()), + "Install Name")); + + if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal( + lhs, IFLHS->getCurrentVersion()), + DiffScalarVal( + rhs, IFRHS->getCurrentVersion()), + "Current Version")); + if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal( + lhs, IFLHS->getCompatibilityVersion()), + DiffScalarVal( + rhs, IFRHS->getCompatibilityVersion()), + "Compatibility Version")); + if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) + ReturnedOutput.push_back( + recordDifferences(DiffScalarVal( + lhs, IFLHS->getSwiftABIVersion()), + DiffScalarVal( + rhs, IFRHS->getSwiftABIVersion()), + "Swift ABI Version")); + if (IFLHS->isInstallAPI() != IFRHS->isInstallAPI()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal(lhs, IFLHS->isInstallAPI()), + DiffScalarVal(rhs, IFRHS->isInstallAPI()), + "InstallAPI")); + + if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) + ReturnedOutput.push_back(recordDifferences( + DiffScalarVal(lhs, + IFLHS->isTwoLevelNamespace()), + DiffScalarVal(rhs, + IFRHS->isTwoLevelNamespace()), + "Two Level Namespace")); + + if (IFLHS->isApplicationExtensionSafe() != + IFRHS->isApplicationExtensionSafe()) + ReturnedOutput.push_back( + recordDifferences(DiffScalarVal( + lhs, IFLHS->isApplicationExtensionSafe()), + DiffScalarVal( + rhs, IFRHS->isApplicationExtensionSafe()), + "Application Extension Safe")); + + if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) + ReturnedOutput.push_back(recordDifferences(IFLHS->reexportedLibraries(), + IFRHS->reexportedLibraries(), + "Reexported Libraries")); + + if (IFLHS->allowableClients() != IFRHS->allowableClients()) + ReturnedOutput.push_back(recordDifferences(IFLHS->allowableClients(), + IFRHS->allowableClients(), + "Allowable Clients")); + + if (IFLHS->umbrellas() != IFRHS->umbrellas()) + ReturnedOutput.push_back(recordDifferences( + IFLHS->umbrellas(), IFRHS->umbrellas(), "Parent Umbrellas")); + + if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols())) + ReturnedOutput.push_back( + recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols")); + + if (IFLHS->documents() != IFRHS->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 : IFLHS->documents()) { + auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &Doc2) { + return (Doc1->getInstallName() == Doc2->getInstallName()); + }); + // If a match found, recursively get differences between the pair. + if (Pair != IFRHS->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 : IFRHS->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" << getTargetTripleName(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" << getTargetTripleName(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() { + const auto *IFLHS = &(FileLHS->getInterfaceFile()); + const auto *IFRHS = &(FileRHS->getInterfaceFile()); + if (*IFLHS == *IFRHS) + return false; + outs() << "< " << std::string(IFLHS->getPath().data()) << "\n> " + << std::string(IFRHS->getPath().data()) << "\n\n"; + std::vector Diffs = findDifferences(IFLHS, IFRHS); + printDifferences(std::move(Diffs), 0); + return true; +} diff --git a/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp b/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp @@ -0,0 +1,100 @@ +//===-- llvm-tapi-diff.cpp - (tapi|tbd) 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-tapi difference +// engine. +// +//===----------------------------------------------------------------------===// +#include "DiffEngine.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" +#include + +using namespace llvm; +using namespace MachO; +using namespace object; + +namespace { +cl::OptionCategory NMCat("llvm-tapi-diff Options"); +cl::opt InputFileNameLHS(cl::Positional, cl::desc(""), + cl::cat(NMCat)); +cl::opt InputFileNameRHS(cl::Positional, cl::desc(""), + cl::cat(NMCat)); + +std::string ToolName; +} // anonymous namespace + +static void error(llvm::Error E, std::string FileName) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName), + (FileName + ": ")); +} + +Expected> convertFileToBinary(std::string &Filename) { + ErrorOr> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (BufferOrErr.getError()) + return errorCodeToError(BufferOrErr.getError()); + + Expected> BinaryOrErr = + createBinary(BufferOrErr.get()->getMemBufferRef()); + if (!BinaryOrErr) + return BinaryOrErr; + 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 (InputFileNameLHS.empty() || InputFileNameRHS.empty()) { + cl::PrintHelpMessage(); + return EXIT_FAILURE; + } + + ToolName = Argv[0]; + + auto BinLHS = convertFileToBinary(InputFileNameLHS); + if (auto Err = BinLHS.takeError()) { + error(std::move(Err), InputFileNameLHS); + return EXIT_FAILURE; + } + + auto BinRHS = convertFileToBinary(InputFileNameRHS); + if (auto Err = BinRHS.takeError()) { + error(std::move(Err), InputFileNameRHS); + return EXIT_FAILURE; + } + + TapiUniversal *FileLHS = dyn_cast(BinLHS.get().release()); + if (!FileLHS) { + auto Err = + createStringError(std::errc::executable_format_error, + "Error when parsing file, unsupported file format"); + error(std::move(Err), InputFileNameLHS); + return EXIT_FAILURE; + } + + TapiUniversal *FileRHS = dyn_cast(BinRHS.get().release()); + if (!FileRHS) { + auto Err = + createStringError(std::errc::executable_format_error, + "Error when parsing file, unsupported file format"); + error(std::move(Err), InputFileNameRHS); + return EXIT_FAILURE; + } + + return DiffEngine(FileLHS, FileRHS).compareFiles(); +}