Skip to content

Commit c9dc52d

Browse files
authored
[InstallAPI] add JSON option to pass X<label> arguments (#91770)
1 parent b4492c9 commit c9dc52d

File tree

9 files changed

+222
-4
lines changed

9 files changed

+222
-4
lines changed

clang/include/clang/Basic/DiagnosticInstallAPIKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def err_no_matching_target : Error<"no matching target found for target variant
2424
def err_unsupported_vendor : Error<"vendor '%0' is not supported: '%1'">;
2525
def err_unsupported_environment : Error<"environment '%0' is not supported: '%1'">;
2626
def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
27-
def err_cannot_read_input_list : Error<"could not read %select{alias list|filelist}0 '%1': %2">;
27+
def err_cannot_read_input_list : Error<"could not read %0 input list '%1': %2">;
2828
def err_invalid_label: Error<"label '%0' is reserved: use a different label name for -X<label>">;
2929
} // end of command line category.
3030

clang/test/InstallAPI/alias_list.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
; RUN: -o %t/AliasList.tbd 2>&1 | FileCheck -allow-empty %s \
2424
; RUN: --check-prefix=INVALID
2525

26-
; INVALID: error: could not read alias list {{.*}} missing alias for: _hidden
26+
; INVALID: error: could not read symbol alias input list {{.*}}invalid.txt': invalid input format: missing alias for: _hidden
2727

2828
;--- Frameworks/AliasList.framework/Headers/AliasList.h
2929
// simple alias from one symbol to another.

clang/test/InstallAPI/exclusive-passes-2.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
; RUN: -DFoo -XApple -DDarwin=1 -XElf -DNONDarwin=1 2>&1 | FileCheck -allow-empty %s
1212
; RUN: llvm-readtapi --compare %t/output.tbd %t/expected.tbd 2>&1 | FileCheck -allow-empty %s
1313

14+
; RUN: clang-installapi -target arm64-apple-macos12 \
15+
; RUN: -install_name @rpath/libfoo.dylib \
16+
; RUN: -current_version 1 -compatibility_version 1 \
17+
; RUN: -I%S/Inputs/LibFoo/usr/include -dynamiclib \
18+
; RUN: -extra-public-header %S/Inputs/LibFoo/usr/include/foo.h \
19+
; RUN: -o %t/output2.tbd \
20+
; RUN: -DFoo -optionlist %t/options.json 2>&1 | FileCheck -allow-empty %s
21+
; RUN: llvm-readtapi --compare %t/output.tbd %t/expected.tbd 2>&1 | FileCheck -allow-empty %s
22+
1423
; CHECK-NOT: error
1524
; CHECK-NOT: warning
1625

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
; RUN: rm -rf %t
2+
; RUN: split-file %s %t
3+
4+
// "Apple" label has split options between the optionlist & command line.
5+
; RUN: clang-installapi -target arm64-apple-macos12 \
6+
; RUN: -install_name @rpath/libfoo.dylib -current_version 1 \
7+
; RUN: -compatibility_version 1 \
8+
; RUN: -extra-public-header %t/usr/include/opts.h \
9+
; RUN: -optionlist %t/options.json -XApple -DCLI_OPT=1 \
10+
; RUN: -I%S/Inputs/LibFoo/usr/include \
11+
; RUN: -I%t/usr/include -dynamiclib -o %t/output.tbd 2>&1 | FileCheck %s -allow-empty
12+
; RUN: llvm-readtapi --compare %t/output.tbd %t/expected.tbd 2>&1 | FileCheck -allow-empty %s
13+
14+
// Validate duplicated options give same result.
15+
; RUN: clang-installapi -target arm64-apple-macos12 \
16+
; RUN: -install_name @rpath/libfoo.dylib -current_version 1 \
17+
; RUN: -compatibility_version 1 \
18+
; RUN: -extra-public-header %t/usr/include/opts.h \
19+
; RUN: -optionlist %t/options.json -XApple -DCLI_OPT=1 \
20+
; RUN: -I%S/Inputs/LibFoo/usr/include \
21+
; RUN: -XApple -DDarwin -XElf -DNONDarwin \
22+
; RUN: -I%t/usr/include -dynamiclib -o %t/output2.tbd 2>&1 | FileCheck %s -allow-empty
23+
; RUN: llvm-readtapi --compare %t/output2.tbd %t/expected.tbd 2>&1 | FileCheck -allow-empty %s
24+
25+
; CHECK-NOT: error
26+
; CHECK-NOT: warning
27+
28+
;--- usr/include/opts.h
29+
#ifndef OPTS_H
30+
#define OPTS_H
31+
#include <macro_defs.h>
32+
33+
#if defined(CLI_OPT) && CLI_OPT
34+
#define SUFFIX "$final"
35+
#else
36+
#define SUFFIX
37+
#endif
38+
39+
40+
#define __STRING(x) #x
41+
#define PLATFORM_ALIAS(sym) __asm("_" __STRING(sym) DARWIN LINUX SUFFIX)
42+
extern int foo() PLATFORM_ALIAS(foo);
43+
44+
#endif
45+
46+
;--- expected.tbd
47+
{
48+
"main_library": {
49+
"exported_symbols": [
50+
{
51+
"text": {
52+
"global": [
53+
"_foo$darwin$final",
54+
"_foo$linux",
55+
"_foo"
56+
]
57+
}
58+
}
59+
],
60+
"flags": [
61+
{
62+
"attributes": [
63+
"not_app_extension_safe"
64+
]
65+
}
66+
],
67+
"install_names": [
68+
{
69+
"name": "@rpath/libfoo.dylib"
70+
}
71+
],
72+
"target_info": [
73+
{
74+
"min_deployment": "12",
75+
"target": "arm64-macos"
76+
}
77+
]
78+
},
79+
"tapi_tbd_version": 5
80+
}
81+
82+
//--- options.json
83+
{
84+
"Apple" : ["-DDarwin=1"],
85+
"Elf" : ["-DNONDarwin=1"]
86+
}

clang/test/InstallAPI/exclusive-passes.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,28 @@
1010
; RUN: -o %t/output.tbd -v 2>&1 | FileCheck %s --check-prefix=INSTALLAPI
1111
; RUN: llvm-readtapi --compare %t/output.tbd %t/expected.tbd 2>&1 | FileCheck -allow-empty %s
1212

13+
// Try with -optionlist.
14+
; RUN: clang-installapi \
15+
; RUN: -target arm64-apple-macos12 -install_name @rpath/libfoo.dylib \
16+
; RUN: -current_version 1 -compatibility_version 1 \
17+
; RUN: -I%S/Inputs/LibFoo/usr/include -dynamiclib \
18+
; RUN: -extra-public-header %S/Inputs/LibFoo/usr/include/public.h \
19+
; RUN: -optionlist %t/options.json -o %t/output2.tbd 2>&1 | FileCheck %s -allow-empty
20+
; RUN: llvm-readtapi --compare %t/output2.tbd %t/expected.tbd 2>&1 | FileCheck -allow-empty %s
21+
1322
; CHECK-NOT: error
1423
; CHECK-NOT: warning
1524

1625
; INSTALLAPI: Public Headers:
1726
; INSTALLAPI: Apple Public Headers:
1827
; INSTALLAPI: Elf Public Headers:
1928

29+
;--- options.json
30+
{
31+
"Apple" : ["-DDarwin=1"],
32+
"Elf" : ["-DNONDarwin=1"]
33+
}
34+
2035
;--- expected.tbd
2136
{
2237
"main_library": {

clang/test/InstallAPI/invalid-exclusive-passes.test

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,39 @@
3030
; RUN: -o %t/output.tbd 2>&1 | FileCheck %s --check-prefix=INVALID_PROJECT_OPT
3131
; INVALID_PROJECT_OPT: error: invalid argument '-Xproject' not allowed with '-fprofile-instr-generate'
3232

33+
// Validate arguments not allowed with -X passed via json
34+
; RUN: not clang-installapi -target arm64-apple-macos12 \
35+
; RUN: -install_name @rpath/libfoo.dylib -current_version 1 -compatibility_version 1 \
36+
; RUN: -optionlist %t/options.json -I/fake/path \
37+
; RUN: -I%t -dynamiclib -o %t/output.tbd 2>&1 | FileCheck %s --check-prefix=INVALID_JSON_OPT
38+
; INVALID_JSON_OPT: error: invalid argument '-XApple' not allowed with '-I/fake/path'
39+
40+
// Validate invalid json path
41+
; RUN: not clang-installapi -target arm64-apple-macos12 \
42+
; RUN: -install_name @rpath/libfoo.dylib -current_version 1 \
43+
; RUN: -compatibility_version 1 -optionlist %t/invalid_loc.json \
44+
; RUN: -I/fake/path -I%t -dynamiclib \
45+
; RUN: -o %t/output.tbd %t 2>&1 | FileCheck %s --check-prefix=INVALID_JSON_LOC -DMSG=%errc_ENOENT
46+
; INVALID_JSON_LOC: error: cannot open file {{.*}}invalid_loc.json': [[MSG]]
47+
48+
// Validate invalid json format
49+
; RUN: not clang-installapi -target arm64-apple-macos12 \
50+
; RUN: -install_name @rpath/libfoo.dylib -current_version 1 \
51+
; RUN: -compatibility_version 1 -optionlist %t/invalid_format.json \
52+
; RUN: -I/fake/path -isysroot %sysroot -I%t -dynamiclib \
53+
; RUN: -o %t/output.tbd %t 2>&1 | FileCheck %s --check-prefix=INVALID_JSON_FORMAT
54+
; INVALID_JSON_FORMAT: error: could not read option input list {{.*}}invalid_format.json': invalid input format
55+
56+
;--- options.json
57+
{
58+
"Apple" : ["-I/fake/path"]
59+
}
60+
61+
;--- invalid_format.json
62+
{
63+
"Apple" : {"opt" : "-I/fake/path"}
64+
}
65+
3366
;--- inputs.json
3467
{
3568
"headers": [ ],

clang/tools/clang-installapi/InstallAPIOpts.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ def X__ : Joined<["-"], "X">,
9999
HelpText<"Pass <arg> to run unique clang invocation identified as <label>">,
100100
MetaVarName<"<label> <arg>">;
101101

102+
def option_list : Separate<["-"], "optionlist">, MetaVarName<"<path>">,
103+
HelpText<"Specifies the <path> to a file that contains X<label> arguments to parse.">;
104+
102105
//
103106
/// Overidden clang options for different behavior.
104107
//

clang/tools/clang-installapi/Options.cpp

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clang/InstallAPI/HeaderFile.h"
1414
#include "clang/InstallAPI/InstallAPIDiagnostic.h"
1515
#include "llvm/BinaryFormat/Magic.h"
16+
#include "llvm/Support/JSON.h"
1617
#include "llvm/Support/Program.h"
1718
#include "llvm/TargetParser/Host.h"
1819
#include "llvm/TextAPI/DylibReader.h"
@@ -82,6 +83,47 @@ static llvm::opt::OptTable *createDriverOptTable() {
8283
return new DriverOptTable();
8384
}
8485

86+
/// Parse JSON input into argument list.
87+
///
88+
/* Expected input format.
89+
* { "label" : ["-ClangArg1", "-ClangArg2"] }
90+
*/
91+
///
92+
/// Input is interpreted as "-Xlabel ClangArg1 -XLabel ClangArg2".
93+
static Expected<llvm::opt::InputArgList>
94+
getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table,
95+
std::vector<std::string> &Storage) {
96+
using namespace json;
97+
Expected<Value> ValOrErr = json::parse(Input);
98+
if (!ValOrErr)
99+
return ValOrErr.takeError();
100+
101+
const Object *Root = ValOrErr->getAsObject();
102+
if (!Root)
103+
return llvm::opt::InputArgList();
104+
105+
for (const auto &KV : *Root) {
106+
const Array *ArgList = KV.getSecond().getAsArray();
107+
std::string Label = "-X" + KV.getFirst().str();
108+
if (!ArgList)
109+
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat);
110+
for (auto Arg : *ArgList) {
111+
std::optional<StringRef> ArgStr = Arg.getAsString();
112+
if (!ArgStr)
113+
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat);
114+
Storage.emplace_back(Label);
115+
Storage.emplace_back(*ArgStr);
116+
}
117+
}
118+
119+
std::vector<const char *> CArgs(Storage.size());
120+
llvm::for_each(Storage,
121+
[&CArgs](StringRef Str) { CArgs.emplace_back(Str.data()); });
122+
123+
unsigned MissingArgIndex, MissingArgCount;
124+
return Table->ParseArgs(CArgs, MissingArgIndex, MissingArgCount);
125+
}
126+
85127
bool Options::processDriverOptions(InputArgList &Args) {
86128
// Handle inputs.
87129
llvm::append_range(DriverOpts.FileLists,
@@ -348,6 +390,31 @@ bool Options::processXarchOption(InputArgList &Args, arg_iterator Curr) {
348390
return true;
349391
}
350392

393+
bool Options::processOptionList(InputArgList &Args,
394+
llvm::opt::OptTable *Table) {
395+
Arg *A = Args.getLastArg(OPT_option_list);
396+
if (!A)
397+
return true;
398+
399+
const StringRef Path = A->getValue(0);
400+
auto InputOrErr = FM->getBufferForFile(Path);
401+
if (auto Err = InputOrErr.getError()) {
402+
Diags->Report(diag::err_cannot_open_file) << Path << Err.message();
403+
return false;
404+
}
405+
// Backing storage referenced for argument processing.
406+
std::vector<std::string> Storage;
407+
auto ArgsOrErr =
408+
getArgListFromJSON((*InputOrErr)->getBuffer(), Table, Storage);
409+
410+
if (auto Err = ArgsOrErr.takeError()) {
411+
Diags->Report(diag::err_cannot_read_input_list)
412+
<< "option" << Path << toString(std::move(Err));
413+
return false;
414+
}
415+
return processInstallAPIXOptions(*ArgsOrErr);
416+
}
417+
351418
bool Options::processLinkerOptions(InputArgList &Args) {
352419
// Handle required arguments.
353420
if (const Arg *A = Args.getLastArg(drv::OPT_install__name))
@@ -510,6 +577,9 @@ Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
510577
if (!processInstallAPIXOptions(ParsedArgs))
511578
return {};
512579

580+
if (!processOptionList(ParsedArgs, Table.get()))
581+
return {};
582+
513583
DriverOpts.Demangle = ParsedArgs.hasArg(OPT_demangle);
514584

515585
if (auto *A = ParsedArgs.getLastArg(OPT_filetype)) {
@@ -818,7 +888,7 @@ InstallAPIContext Options::createContext() {
818888
Expected<AliasMap> Result = parseAliasList(Buffer.get());
819889
if (!Result) {
820890
Diags->Report(diag::err_cannot_read_input_list)
821-
<< /*IsFileList=*/false << ListPath << toString(Result.takeError());
891+
<< "symbol alias" << ListPath << toString(Result.takeError());
822892
return Ctx;
823893
}
824894
Aliases.insert(Result.get().begin(), Result.get().end());
@@ -839,7 +909,7 @@ InstallAPIContext Options::createContext() {
839909
if (auto Err = FileListReader::loadHeaders(std::move(Buffer.get()),
840910
Ctx.InputHeaders, FM)) {
841911
Diags->Report(diag::err_cannot_read_input_list)
842-
<< /*IsFileList=*/true << ListPath << std::move(Err);
912+
<< "header file" << ListPath << std::move(Err);
843913
return Ctx;
844914
}
845915
}

clang/tools/clang-installapi/Options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ class Options {
161161
bool processXarchOption(llvm::opt::InputArgList &Args, arg_iterator Curr);
162162
bool processXplatformOption(llvm::opt::InputArgList &Args, arg_iterator Curr);
163163
bool processXprojectOption(llvm::opt::InputArgList &Args, arg_iterator Curr);
164+
bool processOptionList(llvm::opt::InputArgList &Args,
165+
llvm::opt::OptTable *Table);
164166

165167
public:
166168
/// The various options grouped together.

0 commit comments

Comments
 (0)