[llvm] [readtapi] Add Merge functionality (PR #72656)
Cyndy Ishida via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 17 10:05:58 PST 2023
https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/72656
>From 7231953f0b561244d46ae40b8093db0ab508239b Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 14 Nov 2023 15:00:30 -0800
Subject: [PATCH 1/2] [readtapi] Add Merge functionality
Merge allows a user to merge different files (tbds or dylibs) to emit
out a single tbd with all input contents. This does require that all
inputs represent the same library.
---
llvm/include/llvm/TextAPI/TextAPIWriter.h | 14 +++
.../compare-incorrect-format.test | 2 +-
.../tools/llvm-readtapi/merge-invalid.test | 52 ++++++++++
llvm/test/tools/llvm-readtapi/merge.test | 99 +++++++++++++++++++
llvm/tools/llvm-readtapi/DiffEngine.cpp | 10 +-
llvm/tools/llvm-readtapi/DiffEngine.h | 8 +-
llvm/tools/llvm-readtapi/TapiOpts.td | 3 +
llvm/tools/llvm-readtapi/llvm-readtapi.cpp | 77 ++++++++++-----
8 files changed, 231 insertions(+), 34 deletions(-)
create mode 100644 llvm/test/tools/llvm-readtapi/merge-invalid.test
create mode 100644 llvm/test/tools/llvm-readtapi/merge.test
diff --git a/llvm/include/llvm/TextAPI/TextAPIWriter.h b/llvm/include/llvm/TextAPI/TextAPIWriter.h
index 89fc984854dbae0..6c582b8bc0cca7e 100644
--- a/llvm/include/llvm/TextAPI/TextAPIWriter.h
+++ b/llvm/include/llvm/TextAPI/TextAPIWriter.h
@@ -9,6 +9,7 @@
#ifndef LLVM_TEXTAPI_TEXTAPIWRITER_H
#define LLVM_TEXTAPI_TEXTAPIWRITER_H
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/TextAPI/InterfaceFile.h"
namespace llvm {
@@ -32,6 +33,19 @@ class TextAPIWriter {
static Error writeToStream(raw_ostream &OS, const InterfaceFile &File,
const FileType FileKind = FileType::Invalid,
bool Compact = false);
+
+ /// Get TAPI Text FileType of string input.
+ ///
+ /// \param FT String of input to map to FileType.
+ static FileType parseFileType(const StringRef FT) {
+ return StringSwitch<FileType>(FT)
+ .Case("tbd-v1", FileType::TBD_V1)
+ .Case("tbd-v2", FileType::TBD_V2)
+ .Case("tbd-v3", FileType::TBD_V3)
+ .Case("tbd-v4", FileType::TBD_V4)
+ .Case("tbd-v5", FileType::TBD_V5)
+ .Default(FileType::Invalid);
+ }
};
} // end namespace MachO.
diff --git a/llvm/test/tools/llvm-readtapi/compare-incorrect-format.test b/llvm/test/tools/llvm-readtapi/compare-incorrect-format.test
index 32a350b28eb1e40..09dc768518ebc79 100644
--- a/llvm/test/tools/llvm-readtapi/compare-incorrect-format.test
+++ b/llvm/test/tools/llvm-readtapi/compare-incorrect-format.test
@@ -2,6 +2,6 @@
; RUN: yaml2obj %S/Inputs/macho.yaml -o %t/macho.dylib
; RUN: not llvm-readtapi --compare %S/Inputs/v4A.tbd %t/macho.dylib 2>&1 | FileCheck %s
-; CHECK: error: {{.*}}macho.dylib' unsupported file format
+; CHECK: error: {{.*}}macho.dylib' unsupported file type
; CHECK-NOT: error:
; CHECK-NOT: warning:
diff --git a/llvm/test/tools/llvm-readtapi/merge-invalid.test b/llvm/test/tools/llvm-readtapi/merge-invalid.test
new file mode 100644
index 000000000000000..08dc0b66d581cd3
--- /dev/null
+++ b/llvm/test/tools/llvm-readtapi/merge-invalid.test
@@ -0,0 +1,52 @@
+; RUN: rm -rf %t
+; RUN: split-file %s %t
+; RUN: not llvm-readtapi -merge %t/libfoo.tbd %t/libbar.tbd 2>&1| FileCheck %s --allow-empty --check-prefix DIFF
+; RUN: not llvm-readtapi -merge %t/libfoo.tbd 2>&1 | FileCheck %s --allow-empty --check-prefix INPUT
+
+; DIFF: install names do not match
+; INPUT: merge requires at least two input files
+
+;--- libfoo.tbd
+{
+ "main_library": {
+ "allowable_clients": [
+ {
+ "clients": [
+ "ClientAll"
+ ]
+ }
+ ],
+ "install_names": [
+ {
+ "name": "/usr/lib/libfoo.dylib"
+ }
+ ],
+ "target_info": [
+ {
+ "min_deployment": "13.1",
+ "target": "x86_64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
+
+;--- libbar.tbd
+--- !tapi-tbd
+tbd-version: 4
+targets: [ arm64-macos ]
+install-name: '/usr/lib/libbar.dylib'
+allowable-clients:
+ - targets: [ arm64-macos ]
+ clients: [ ClientAll ]
+reexported-libraries:
+ - targets: [ arm64-macos ]
+ libraries: [ '/usr/lib/liball.dylib' ]
+exports:
+ - targets: [ arm64-macos ]
+ symbols: [ _sym1 ]
+ objc-classes: [ _A ]
+ objc-ivars: [ _A._ivar1 ]
+ weak-symbols: [ _weak1 ]
+ thread-local-symbols: [ _tlv1 ]
+...
diff --git a/llvm/test/tools/llvm-readtapi/merge.test b/llvm/test/tools/llvm-readtapi/merge.test
new file mode 100644
index 000000000000000..59953f4bf5f4e8e
--- /dev/null
+++ b/llvm/test/tools/llvm-readtapi/merge.test
@@ -0,0 +1,99 @@
+; RUN: rm -rf %t
+; RUN: split-file %s %t
+; RUN: llvm-readtapi -merge %t/i386.tbd %t/x86_64.tbd %t/arm64.tbd --filetype tbd-v5 -o %t/out.tbd -compact 2>&1 | FileCheck %s --allow-empty
+; RUN: llvm-readtapi -merge %t/i386.tbd %t/x86_64.tbd %t/arm64.tbd --filetype=tbd-v5 --o %t/out.tbd -compact 2>&1 | FileCheck %s --allow-empty
+; RUN: llvm-readtapi -compare %t/out.tbd %t/expected.tbd 2>&1 | FileCheck %s --allow-empty
+
+; CHECK-NOT: error
+; CHECK-NOT: warning
+
+;--- expected.tbd
+{"main_library":{"allowable_clients":[{"clients":["ClientAll"]}],"exported_symbols":[{"data":{"global":["_sym1"],"objc_class":["_A"],"thread_local":["_tlv1"],"weak":["_weak1"]}},{"data":{"objc_ivar":["_A._ivar1"]},"targets":["x86_64-macos","arm64-macos"]}],"install_names":[{"name":"/usr/lib/libfat.dylib"}],"reexported_libraries":[{"names":["/usr/lib/liball.dylib"]}],"target_info":[{"min_deployment":"0","target":"i386-macos"},{"min_deployment":"13.1","target":"x86_64-macos"},{"min_deployment":"0","target":"arm64-macos"}]},"tapi_tbd_version":5}
+
+;--- i386.tbd
+--- !tapi-tbd-v3
+archs: [ i386 ]
+platform: macosx
+install-name: /usr/lib/libfat.dylib
+exports:
+ - archs: [ i386 ]
+ allowable-clients: [ ClientAll ]
+ re-exports: [ /usr/lib/liball.dylib ]
+ symbols: [ _sym1 ]
+ objc-classes: [ _A ]
+ weak-def-symbols: [ _weak1 ]
+ thread-local-symbols: [ _tlv1 ]
+...
+
+;--- x86_64.tbd
+{
+ "main_library": {
+ "allowable_clients": [
+ {
+ "clients": [
+ "ClientAll"
+ ]
+ }
+ ],
+ "exported_symbols": [
+ {
+ "data": {
+ "global": [
+ "_sym1"
+ ],
+ "objc_class": [
+ "_A"
+ ],
+ "objc_ivar": [
+ "_A._ivar1"
+ ],
+ "thread_local": [
+ "_tlv1"
+ ],
+ "weak": [
+ "_weak1"
+ ]
+ }
+ }
+ ],
+ "install_names": [
+ {
+ "name": "/usr/lib/libfat.dylib"
+ }
+ ],
+ "reexported_libraries": [
+ {
+ "names": [
+ "/usr/lib/liball.dylib"
+ ]
+ }
+ ],
+ "target_info": [
+ {
+ "min_deployment": "13.1",
+ "target": "x86_64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
+
+;--- arm64.tbd
+--- !tapi-tbd
+tbd-version: 4
+targets: [ arm64-macos ]
+install-name: '/usr/lib/libfat.dylib'
+allowable-clients:
+ - targets: [ arm64-macos ]
+ clients: [ ClientAll ]
+reexported-libraries:
+ - targets: [ arm64-macos ]
+ libraries: [ '/usr/lib/liball.dylib' ]
+exports:
+ - targets: [ arm64-macos ]
+ symbols: [ _sym1 ]
+ objc-classes: [ _A ]
+ objc-ivars: [ _A._ivar1 ]
+ weak-symbols: [ _weak1 ]
+ thread-local-symbols: [ _tlv1 ]
+...
diff --git a/llvm/tools/llvm-readtapi/DiffEngine.cpp b/llvm/tools/llvm-readtapi/DiffEngine.cpp
index 5f4b25ca6c0b2de..40722d2da180fd1 100644
--- a/llvm/tools/llvm-readtapi/DiffEngine.cpp
+++ b/llvm/tools/llvm-readtapi/DiffEngine.cpp
@@ -560,13 +560,11 @@ void DiffEngine::printDifferences(raw_ostream &OS,
}
bool DiffEngine::compareFiles(raw_ostream &OS) {
- const auto *IFLHS = &(FileLHS->getInterfaceFile());
- const auto *IFRHS = &(FileRHS->getInterfaceFile());
- if (*IFLHS == *IFRHS)
+ if (*FileLHS == *FileRHS)
return false;
- OS << "< " << std::string(IFLHS->getPath().data()) << "\n> "
- << std::string(IFRHS->getPath().data()) << "\n\n";
- std::vector<DiffOutput> Diffs = findDifferences(IFLHS, IFRHS);
+ OS << "< " << std::string(FileLHS->getPath().data()) << "\n> "
+ << std::string(FileRHS->getPath().data()) << "\n\n";
+ std::vector<DiffOutput> Diffs = findDifferences(FileLHS, FileRHS);
printDifferences(OS, Diffs, 0);
return true;
}
diff --git a/llvm/tools/llvm-readtapi/DiffEngine.h b/llvm/tools/llvm-readtapi/DiffEngine.h
index 27b72573d011e62..5f7c29c920814f7 100644
--- a/llvm/tools/llvm-readtapi/DiffEngine.h
+++ b/llvm/tools/llvm-readtapi/DiffEngine.h
@@ -141,14 +141,14 @@ class InlineDoc : public AttributeDiff {
/// output of the differences found in the files.
class DiffEngine {
public:
- DiffEngine(object::TapiUniversal *InputFileNameLHS,
- object::TapiUniversal *InputFileNameRHS)
+ DiffEngine(MachO::InterfaceFile *InputFileNameLHS,
+ MachO::InterfaceFile *InputFileNameRHS)
: FileLHS(InputFileNameLHS), FileRHS(InputFileNameRHS){};
bool compareFiles(raw_ostream &);
private:
- object::TapiUniversal *FileLHS;
- object::TapiUniversal *FileRHS;
+ MachO::InterfaceFile *FileLHS;
+ MachO::InterfaceFile *FileRHS;
/// Function that prints the differences found in the files.
void printDifferences(raw_ostream &, const std::vector<DiffOutput> &, int);
diff --git a/llvm/tools/llvm-readtapi/TapiOpts.td b/llvm/tools/llvm-readtapi/TapiOpts.td
index 932a21f7d071199..82c8ede2809997e 100644
--- a/llvm/tools/llvm-readtapi/TapiOpts.td
+++ b/llvm/tools/llvm-readtapi/TapiOpts.td
@@ -12,6 +12,9 @@ multiclass JS<string name, string help, string var = ""> {
//
def help : FF<"help", "display this help">;
defm output: JS<"o", "write output to <file>","<file>">;
+def compact: FF<"compact", "write compact tapi output file">;
+defm filetype: JS<"filetype", "specify the output file type (tbd-v3, tbd-v4 or tbd-v5)","<value>">;
+def merge: FF<"merge", "merge the input files that represent the same library">;
//
// Compare options
diff --git a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
index 1307916120e98e6..71cf5bf70df63c4 100644
--- a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
+++ b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
@@ -10,7 +10,6 @@
//
//===----------------------------------------------------------------------===//
#include "DiffEngine.h"
-#include "llvm/Object/TapiUniversal.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
@@ -21,6 +20,8 @@
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TextAPI/TextAPIError.h"
+#include "llvm/TextAPI/TextAPIReader.h"
+#include "llvm/TextAPI/TextAPIWriter.h"
#include <cstdlib>
using namespace llvm;
@@ -59,15 +60,22 @@ class TAPIOptTable : public opt::GenericOptTable {
struct Context {
std::vector<std::string> Inputs;
std::unique_ptr<llvm::raw_fd_stream> OutStream;
+ FileType WriteFT = FileType::TBD_V5;
+ bool Compact = false;
};
-Expected<std::unique_ptr<Binary>>
-convertFileToBinary(const StringRef Filename) {
+std::unique_ptr<InterfaceFile> getInterfaceFile(const StringRef Filename,
+ ExitOnError &ExitOnErr) {
+ ExitOnErr.setBanner("error: '" + Filename.str() + "' ");
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
- MemoryBuffer::getFileOrSTDIN(Filename);
+ MemoryBuffer::getFile(Filename);
if (BufferOrErr.getError())
- return errorCodeToError(BufferOrErr.getError());
- return createBinary(BufferOrErr.get()->getMemBufferRef());
+ ExitOnErr(errorCodeToError(BufferOrErr.getError()));
+ Expected<std::unique_ptr<InterfaceFile>> IF =
+ TextAPIReader::get((*BufferOrErr)->getMemBufferRef());
+ if (!IF)
+ ExitOnErr(IF.takeError());
+ return std::move(*IF);
}
// Use unique exit code to differentiate failures not directly caused from
@@ -81,28 +89,37 @@ bool handleCompareAction(const Context &Ctx) {
ExitOnErr(make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
"compare only supports 2 input files"));
}
- StringRef InputFileName = Ctx.Inputs.front();
- ExitOnErr.setBanner("error: '" + InputFileName.str() + "' ");
- auto BinLHS = ExitOnErr(convertFileToBinary(InputFileName));
-
- TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
- if (!FileLHS) {
- ExitOnErr(createStringError(std::errc::executable_format_error,
- "unsupported file format"));
- }
- StringRef CompareInputFileName = Ctx.Inputs.at(1);
- ExitOnErr.setBanner("error: '" + CompareInputFileName.str() + "' ");
- auto BinRHS = ExitOnErr(convertFileToBinary(CompareInputFileName));
+ auto LeftIF = getInterfaceFile(Ctx.Inputs.front(), ExitOnErr);
+ auto RightIF = getInterfaceFile(Ctx.Inputs.at(1), ExitOnErr);
+
+ raw_ostream &OS = Ctx.OutStream ? *Ctx.OutStream : outs();
+ return DiffEngine(LeftIF.get(), RightIF.get()).compareFiles(OS);
+}
- TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get());
- if (!FileRHS) {
- ExitOnErr(createStringError(std::errc::executable_format_error,
- "unsupported file format"));
+bool handleMergeAction(const Context &Ctx) {
+ ExitOnError ExitOnErr("error: ");
+ if (Ctx.Inputs.size() < 2) {
+ ExitOnErr(
+ make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
+ "merge requires at least two input files"));
+ }
+ std::unique_ptr<InterfaceFile> Out;
+ for (StringRef FileName : Ctx.Inputs) {
+ auto IF = getInterfaceFile(FileName, ExitOnErr);
+ if (!Out) {
+ Out = std::move(IF);
+ continue;
+ }
+ auto ResultIF = Out->merge(IF.get());
+ if (!ResultIF)
+ ExitOnErr(ResultIF.takeError());
+ Out = std::move(ResultIF.get());
}
raw_ostream &OS = Ctx.OutStream ? *Ctx.OutStream : outs();
- return DiffEngine(FileLHS, FileRHS).compareFiles(OS);
+ ExitOnErr(TextAPIWriter::writeToStream(OS, *Out, Ctx.WriteFT, Ctx.Compact));
+ return EXIT_SUCCESS;
}
} // anonymous namespace
@@ -138,8 +155,22 @@ int main(int Argc, char **Argv) {
}
}
+ if (Args.hasArg(OPT_compact))
+ Ctx.Compact = true;
+
+ if (opt::Arg *A = Args.getLastArg(OPT_filetype_EQ)) {
+ Ctx.WriteFT = TextAPIWriter::parseFileType(A->getValue());
+ if (Ctx.WriteFT < FileType::TBD_V3 || Ctx.WriteFT == FileType::Invalid) {
+ llvm::errs() << "error: unsupported filetype '" << A->getValue() << "'\n";
+ return EXIT_FAILURE;
+ }
+ }
+
if (Args.hasArg(OPT_compare))
return handleCompareAction(Ctx);
+ if (Args.hasArg(OPT_merge))
+ return handleMergeAction(Ctx);
+
return EXIT_SUCCESS;
}
>From ed3c7e59ff03c79113f150b6a934db7ab83d3785 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Fri, 17 Nov 2023 10:02:44 -0800
Subject: [PATCH 2/2] [readtapi] Introduce action groups for handling top level
operations
* Add a helper function for boilerplate fatal error handling.
* Add a simple & implicit writeAction
---
llvm/include/llvm/TextAPI/TextAPIWriter.h | 2 +-
.../tools/llvm-readtapi/command-line.test | 6 ++
.../tools/llvm-readtapi/merge-invalid.test | 2 +-
llvm/test/tools/llvm-readtapi/merge.test | 42 +++++++++-
llvm/test/tools/llvm-readtapi/write.test | 77 ++++++++++++++++++
llvm/tools/llvm-readtapi/TapiOpts.td | 13 +--
llvm/tools/llvm-readtapi/llvm-readtapi.cpp | 80 +++++++++++++------
7 files changed, 189 insertions(+), 33 deletions(-)
create mode 100644 llvm/test/tools/llvm-readtapi/write.test
diff --git a/llvm/include/llvm/TextAPI/TextAPIWriter.h b/llvm/include/llvm/TextAPI/TextAPIWriter.h
index 6c582b8bc0cca7e..7fd32c6fe2a9eff 100644
--- a/llvm/include/llvm/TextAPI/TextAPIWriter.h
+++ b/llvm/include/llvm/TextAPI/TextAPIWriter.h
@@ -34,7 +34,7 @@ class TextAPIWriter {
const FileType FileKind = FileType::Invalid,
bool Compact = false);
- /// Get TAPI Text FileType of string input.
+ /// Get TAPI FileType from the input string.
///
/// \param FT String of input to map to FileType.
static FileType parseFileType(const StringRef FT) {
diff --git a/llvm/test/tools/llvm-readtapi/command-line.test b/llvm/test/tools/llvm-readtapi/command-line.test
index 3b17194a7c14792..400e0670e5aa067 100644
--- a/llvm/test/tools/llvm-readtapi/command-line.test
+++ b/llvm/test/tools/llvm-readtapi/command-line.test
@@ -1,7 +1,13 @@
; RUN: llvm-readtapi --help 2>&1 | FileCheck %s
; RUN: llvm-readtapi -help 2>&1 | FileCheck %s
+; RUN: not llvm-readtapi -merge -compare -compact %t/tmp.tbd %t/tmp2.tbd 2>&1 | FileCheck %s --check-prefix MULTI_ACTION
+; RUN: not llvm-readtapi -merge -compact %t/tmp.tbd %t/tmp2.tbd --filetype=tbd-v2 2>&1 | FileCheck %s --check-prefix FILE_FORMAT
CHECK: OVERVIEW: LLVM TAPI file reader and manipulator
CHECK: USAGE: llvm-readtapi [options] <inputs>
CHECK: OPTIONS:
CHECK: -help display this help
+
+MULTI_ACTION: error: only one of the following actions can be specified: -merge -compare
+FILE_FORMAT: error: deprecated filetype 'tbd-v2' is not supported to write
+
diff --git a/llvm/test/tools/llvm-readtapi/merge-invalid.test b/llvm/test/tools/llvm-readtapi/merge-invalid.test
index 08dc0b66d581cd3..f66bfbe8998158e 100644
--- a/llvm/test/tools/llvm-readtapi/merge-invalid.test
+++ b/llvm/test/tools/llvm-readtapi/merge-invalid.test
@@ -1,6 +1,6 @@
; RUN: rm -rf %t
; RUN: split-file %s %t
-; RUN: not llvm-readtapi -merge %t/libfoo.tbd %t/libbar.tbd 2>&1| FileCheck %s --allow-empty --check-prefix DIFF
+; RUN: not llvm-readtapi -merge %t/libfoo.tbd %t/libbar.tbd 2>&1 | FileCheck %s --allow-empty --check-prefix DIFF
; RUN: not llvm-readtapi -merge %t/libfoo.tbd 2>&1 | FileCheck %s --allow-empty --check-prefix INPUT
; DIFF: install names do not match
diff --git a/llvm/test/tools/llvm-readtapi/merge.test b/llvm/test/tools/llvm-readtapi/merge.test
index 59953f4bf5f4e8e..a0aa7ac55e3cff2 100644
--- a/llvm/test/tools/llvm-readtapi/merge.test
+++ b/llvm/test/tools/llvm-readtapi/merge.test
@@ -8,7 +8,47 @@
; CHECK-NOT: warning
;--- expected.tbd
-{"main_library":{"allowable_clients":[{"clients":["ClientAll"]}],"exported_symbols":[{"data":{"global":["_sym1"],"objc_class":["_A"],"thread_local":["_tlv1"],"weak":["_weak1"]}},{"data":{"objc_ivar":["_A._ivar1"]},"targets":["x86_64-macos","arm64-macos"]}],"install_names":[{"name":"/usr/lib/libfat.dylib"}],"reexported_libraries":[{"names":["/usr/lib/liball.dylib"]}],"target_info":[{"min_deployment":"0","target":"i386-macos"},{"min_deployment":"13.1","target":"x86_64-macos"},{"min_deployment":"0","target":"arm64-macos"}]},"tapi_tbd_version":5}
+{
+ "main_library": {
+ "allowable_clients": [{ "clients": ["ClientAll"] }],
+ "exported_symbols": [
+ {
+ "data": {
+ "global": ["_sym1"],
+ "objc_class": ["_A"],
+ "thread_local": ["_tlv1"],
+ "weak": ["_weak1"]
+ }
+ },
+ {
+ "data": {
+ "objc_ivar": ["_A._ivar1"]
+ },
+ "targets": [ "x86_64-macos", "arm64-macos" ]
+ }
+ ],
+ "install_names": [
+ { "name": "/usr/lib/libfat.dylib" }
+ ],
+ "reexported_libraries": [
+ {
+ "names": [ "/usr/lib/liball.dylib" ]
+ }
+ ],
+ "target_info": [
+ { "target": "i386-macos" },
+ {
+ "min_deployment": "13.1",
+ "target": "x86_64-macos"
+ },
+ {
+ "target": "arm64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
+
;--- i386.tbd
--- !tapi-tbd-v3
diff --git a/llvm/test/tools/llvm-readtapi/write.test b/llvm/test/tools/llvm-readtapi/write.test
new file mode 100644
index 000000000000000..1ec7a40a2e40586
--- /dev/null
+++ b/llvm/test/tools/llvm-readtapi/write.test
@@ -0,0 +1,77 @@
+; RUN: rm -rf %t
+; RUN: split-file %s %t
+; RUN: llvm-readtapi %t/arm64.tbd 2>&1 | FileCheck %s
+
+; CHECK-NOT: error
+; CHECK-NOT: warning
+; CHECK: {
+; CHECK-NEXT: "main_library": {
+; CHECK-NEXT: "allowable_clients": [
+; CHECK-NEXT: {
+; CHECK-NEXT: "clients": [
+; CHECK-NEXT: "ClientAll"
+; CHECK-NEXT: ]
+; CHECK-NEXT: }
+; CHECK-NEXT: ],
+; CHECK-NEXT: "exported_symbols": [
+; CHECK-NEXT: {
+; CHECK-NEXT: "data": {
+; CHECK-NEXT: "global": [
+; CHECK-NEXT: "_sym1"
+; CHECK-NEXT: ],
+; CHECK-NEXT: "objc_class": [
+; CHECK-NEXT: "_A"
+; CHECK-NEXT: ],
+; CHECK-NEXT: "objc_ivar": [
+; CHECK-NEXT: "_A._ivar1"
+; CHECK-NEXT: ],
+; CHECK-NEXT: "thread_local": [
+; CHECK-NEXT: "_tlv1"
+; CHECK-NEXT: ],
+; CHECK-NEXT: "weak": [
+; CHECK-NEXT: "_weak1"
+; CHECK-NEXT: ]
+; CHECK-NEXT: }
+; CHECK-NEXT: }
+; CHECK-NEXT: ],
+; CHECK-NEXT: "install_names": [
+; CHECK-NEXT: {
+; CHECK-NEXT: "name": "/usr/lib/libfat.dylib"
+; CHECK-NEXT: }
+; CHECK-NEXT: ],
+; CHECK-NEXT: "reexported_libraries": [
+; CHECK-NEXT: {
+; CHECK-NEXT: "names": [
+; CHECK-NEXT: "/usr/lib/liball.dylib"
+; CHECK-NEXT: ]
+; CHECK-NEXT: }
+; CHECK-NEXT: ],
+; CHECK-NEXT: "target_info": [
+; CHECK-NEXT: {
+; CHECK-NEXT: "target": "arm64-macos"
+; CHECK-NEXT: }
+; CHECK-NEXT: ]
+; CHECK-NEXT: },
+; CHECK-NEXT: "tapi_tbd_version": 5
+; CHECK-NEXT: }
+
+
+;--- arm64.tbd
+--- !tapi-tbd
+tbd-version: 4
+targets: [ arm64-macos ]
+install-name: '/usr/lib/libfat.dylib'
+allowable-clients:
+ - targets: [ arm64-macos ]
+ clients: [ ClientAll ]
+reexported-libraries:
+ - targets: [ arm64-macos ]
+ libraries: [ '/usr/lib/liball.dylib' ]
+exports:
+ - targets: [ arm64-macos ]
+ symbols: [ _sym1 ]
+ objc-classes: [ _A ]
+ objc-ivars: [ _A._ivar1 ]
+ weak-symbols: [ _weak1 ]
+ thread-local-symbols: [ _tlv1 ]
+...
diff --git a/llvm/tools/llvm-readtapi/TapiOpts.td b/llvm/tools/llvm-readtapi/TapiOpts.td
index 82c8ede2809997e..1efa86ea3ae48d6 100644
--- a/llvm/tools/llvm-readtapi/TapiOpts.td
+++ b/llvm/tools/llvm-readtapi/TapiOpts.td
@@ -7,6 +7,13 @@ multiclass JS<string name, string help, string var = ""> {
def : Separate<["-", "--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
}
+//
+// Top level operations
+//
+def action_group : OptionGroup<"action group">;
+def compare : FF<"compare", "compare tapi files for library differences">, Group<action_group>;
+def merge : FF<"merge", "merge the input files that represent the same library">, Group<action_group>;
+
//
// General Driver options
//
@@ -14,9 +21,3 @@ def help : FF<"help", "display this help">;
defm output: JS<"o", "write output to <file>","<file>">;
def compact: FF<"compact", "write compact tapi output file">;
defm filetype: JS<"filetype", "specify the output file type (tbd-v3, tbd-v4 or tbd-v5)","<value>">;
-def merge: FF<"merge", "merge the input files that represent the same library">;
-
-//
-// Compare options
-//
-def compare : FF<"compare", "compare tapi files for library differences">;
diff --git a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
index 71cf5bf70df63c4..3e0bcc49d19cc4c 100644
--- a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
+++ b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
@@ -57,6 +57,13 @@ class TAPIOptTable : public opt::GenericOptTable {
}
};
+// Handle error reporting in cases where `ExitOnError` is not used.
+void reportError(Twine Message, int ExitCode = EXIT_FAILURE) {
+ WithColor::error(errs()) << Message << "\n";
+ errs().flush();
+ exit(ExitCode);
+}
+
struct Context {
std::vector<std::string> Inputs;
std::unique_ptr<llvm::raw_fd_stream> OutStream;
@@ -84,12 +91,11 @@ std::unique_ptr<InterfaceFile> getInterfaceFile(const StringRef Filename,
const int NON_TAPI_EXIT_CODE = 2;
bool handleCompareAction(const Context &Ctx) {
- ExitOnError ExitOnErr("error: ", /*DefaultErrorExitCode=*/NON_TAPI_EXIT_CODE);
- if (Ctx.Inputs.size() != 2) {
- ExitOnErr(make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
- "compare only supports 2 input files"));
- }
+ if (Ctx.Inputs.size() != 2)
+ reportError("compare only supports two input files",
+ /*ExitCode=*/NON_TAPI_EXIT_CODE);
+ ExitOnError ExitOnErr("error: ", /*DefaultErrorExitCode=*/NON_TAPI_EXIT_CODE);
auto LeftIF = getInterfaceFile(Ctx.Inputs.front(), ExitOnErr);
auto RightIF = getInterfaceFile(Ctx.Inputs.at(1), ExitOnErr);
@@ -97,16 +103,28 @@ bool handleCompareAction(const Context &Ctx) {
return DiffEngine(LeftIF.get(), RightIF.get()).compareFiles(OS);
}
-bool handleMergeAction(const Context &Ctx) {
+bool handleWriteAction(const Context &Ctx,
+ std::unique_ptr<InterfaceFile> Out = nullptr) {
ExitOnError ExitOnErr("error: ");
- if (Ctx.Inputs.size() < 2) {
- ExitOnErr(
- make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
- "merge requires at least two input files"));
+ if (!Out) {
+ if (Ctx.Inputs.size() != 1)
+ reportError("write only supports one input file");
+ Out = getInterfaceFile(Ctx.Inputs.front(), ExitOnErr);
}
+ raw_ostream &OS = Ctx.OutStream ? *Ctx.OutStream : outs();
+ ExitOnErr(TextAPIWriter::writeToStream(OS, *Out, Ctx.WriteFT, Ctx.Compact));
+ return EXIT_SUCCESS;
+}
+
+bool handleMergeAction(const Context &Ctx) {
+ if (Ctx.Inputs.size() < 2)
+ reportError("merge requires at least two input files");
+
+ ExitOnError ExitOnErr("error: ");
std::unique_ptr<InterfaceFile> Out;
for (StringRef FileName : Ctx.Inputs) {
auto IF = getInterfaceFile(FileName, ExitOnErr);
+ // On the first iteration copy the input file and skip merge.
if (!Out) {
Out = std::move(IF);
continue;
@@ -116,10 +134,7 @@ bool handleMergeAction(const Context &Ctx) {
ExitOnErr(ResultIF.takeError());
Out = std::move(ResultIF.get());
}
-
- raw_ostream &OS = Ctx.OutStream ? *Ctx.OutStream : outs();
- ExitOnErr(TextAPIWriter::writeToStream(OS, *Out, Ctx.WriteFT, Ctx.Compact));
- return EXIT_SUCCESS;
+ return handleWriteAction(Ctx, std::move(Out));
}
} // anonymous namespace
@@ -155,22 +170,39 @@ int main(int Argc, char **Argv) {
}
}
- if (Args.hasArg(OPT_compact))
- Ctx.Compact = true;
+ Ctx.Compact = Args.hasArg(OPT_compact);
if (opt::Arg *A = Args.getLastArg(OPT_filetype_EQ)) {
- Ctx.WriteFT = TextAPIWriter::parseFileType(A->getValue());
- if (Ctx.WriteFT < FileType::TBD_V3 || Ctx.WriteFT == FileType::Invalid) {
- llvm::errs() << "error: unsupported filetype '" << A->getValue() << "'\n";
- return EXIT_FAILURE;
- }
+ StringRef FT = A->getValue();
+ Ctx.WriteFT = TextAPIWriter::parseFileType(FT);
+ if (Ctx.WriteFT < FileType::TBD_V3)
+ reportError("deprecated filetype '" + FT + "' is not supported to write");
+ if (Ctx.WriteFT == FileType::Invalid)
+ reportError("unsupported filetype '" + FT + "'");
}
- if (Args.hasArg(OPT_compare))
- return handleCompareAction(Ctx);
+ // Handle top level and exclusive operation.
+ SmallVector<opt::Arg *, 1> ActionArgs(Args.filtered(OPT_action_group));
+
+ if (ActionArgs.empty())
+ // If no action specified, write out tapi file in requested format.
+ return handleWriteAction(Ctx);
+
+ if (ActionArgs.size() > 1) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ OS << "only one of the following actions can be specified:";
+ for (auto *Arg : ActionArgs)
+ OS << " " << Arg->getSpelling();
+ reportError(OS.str());
+ }
- if (Args.hasArg(OPT_merge))
+ switch (ActionArgs.front()->getOption().getID()) {
+ case OPT_compare:
+ return handleCompareAction(Ctx);
+ case OPT_merge:
return handleMergeAction(Ctx);
+ }
return EXIT_SUCCESS;
}
More information about the llvm-commits
mailing list