Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit dc9bbed

Browse files
hsinyichen-googleGerrit Code Review
authored and
Gerrit Code Review
committed
Merge "Parse mode tags in version scripts" into main
2 parents f024b46 + ce1e865 commit dc9bbed

File tree

4 files changed

+203
-29
lines changed

4 files changed

+203
-29
lines changed

vndk/tools/header-checker/src/linker/header_abi_linker.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,19 @@ static llvm::cl::list<std::string> excluded_symbol_tags(
8181
"exclude-symbol-tag", llvm::cl::Optional,
8282
llvm::cl::cat(header_linker_category));
8383

84+
static llvm::cl::list<std::string> included_symbol_tags(
85+
"include-symbol-tag",
86+
llvm::cl::desc("Filter the symbols in the version script by mode tag, "
87+
"such as llndk, apex, and systemapi. The format is "
88+
"<tag>=<level> or <tag>. If this option is not specified, "
89+
"all mode tags are included."),
90+
llvm::cl::Optional, llvm::cl::cat(header_linker_category));
91+
8492
static llvm::cl::opt<std::string> api(
85-
"api", llvm::cl::desc("<api>"), llvm::cl::Optional,
86-
llvm::cl::init("current"),
93+
"api",
94+
llvm::cl::desc("Filter the symbols in the version script by comparing "
95+
"\"introduced\" tags and the specified API level."),
96+
llvm::cl::Optional, llvm::cl::init("current"),
8797
llvm::cl::cat(header_linker_category));
8898

8999
static llvm::cl::opt<std::string> api_map(
@@ -480,6 +490,12 @@ static bool InitializeVersionScriptParser(repr::VersionScriptParser &parser) {
480490
for (auto &&tag : excluded_symbol_tags) {
481491
parser.AddExcludedSymbolTag(tag);
482492
}
493+
for (auto &&tag : included_symbol_tags) {
494+
if (!parser.AddModeTag(tag)) {
495+
llvm::errs() << "Failed to parse -include-symbol-tag " << tag << "\n";
496+
return false;
497+
}
498+
}
483499

484500
return true;
485501
}

vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,14 @@ namespace header_checker {
3030
namespace repr {
3131

3232

33-
static constexpr char DEFAULT_ARCH[] = "arm64";
33+
using ModeTagLevel = std::pair<std::string_view, utils::ApiLevel>;
34+
3435

36+
static constexpr char DEFAULT_ARCH[] = "arm64";
37+
static constexpr utils::ApiLevel MIN_MODE_TAG_LEVEL = 0;
38+
static constexpr utils::ApiLevel MAX_MODE_TAG_LEVEL = 1000000;
39+
static const std::set<std::string_view> KNOWN_MODE_TAGS{"apex", "llndk",
40+
"systemapi"};
3541

3642
inline std::string GetIntroducedArchTag(const std::string &arch) {
3743
return "introduced-" + arch + "=";
@@ -55,12 +61,39 @@ void VersionScriptParser::SetApiLevelMap(utils::ApiLevelMap api_level_map) {
5561
}
5662

5763

64+
static std::optional<ModeTagLevel> ParseModeTag(std::string_view tag,
65+
utils::ApiLevel default_level) {
66+
std::vector<std::string_view> split_tag = utils::Split(tag, "=");
67+
utils::ApiLevel level = default_level;
68+
if (split_tag.size() == 2) {
69+
auto level = utils::ParseInt(std::string(split_tag[1]));
70+
if (level.has_value()) {
71+
return {{split_tag[0], level.value()}};
72+
}
73+
} else if (split_tag.size() == 1) {
74+
return {{split_tag[0], default_level}};
75+
}
76+
return {};
77+
}
78+
79+
80+
bool VersionScriptParser::AddModeTag(std::string_view mode_tag) {
81+
auto parsed_mode_tag = ParseModeTag(mode_tag, MAX_MODE_TAG_LEVEL);
82+
if (parsed_mode_tag.has_value()) {
83+
included_mode_tags_[std::string(parsed_mode_tag->first)] =
84+
parsed_mode_tag->second;
85+
return true;
86+
}
87+
return false;
88+
}
89+
90+
5891
VersionScriptParser::ParsedTags VersionScriptParser::ParseSymbolTags(
59-
const std::string &line) {
92+
const std::string &line, const ParsedTags &initial_value) {
6093
static const char *const POSSIBLE_ARCHES[] = {
6194
"arm", "arm64", "x86", "x86_64", "mips", "mips64"};
6295

63-
ParsedTags result;
96+
ParsedTags result = initial_value;
6497

6598
std::string_view line_view(line);
6699
std::string::size_type comment_pos = line_view.find('#');
@@ -138,36 +171,62 @@ VersionScriptParser::ParsedTags VersionScriptParser::ParseSymbolTags(
138171
result.has_weak_tag_ = true;
139172
continue;
140173
}
174+
175+
auto mode_tag = ParseModeTag(tag, MIN_MODE_TAG_LEVEL);
176+
if (mode_tag.has_value() &&
177+
(KNOWN_MODE_TAGS.count(mode_tag->first) > 0 ||
178+
included_mode_tags_.count(mode_tag->first) > 0)) {
179+
result.mode_tags_[std::string(mode_tag->first)] = mode_tag->second;
180+
}
141181
}
142182

143183
return result;
144184
}
145185

146186

147-
bool VersionScriptParser::IsSymbolExported(
148-
const VersionScriptParser::ParsedTags &tags) {
149-
if (tags.has_excluded_tags_) {
187+
bool VersionScriptParser::MatchModeTags(const ParsedTags &tags) {
188+
for (const auto &mode_tag : tags.mode_tags_) {
189+
auto included_mode_tag = included_mode_tags_.find(mode_tag.first);
190+
if (included_mode_tag != included_mode_tags_.end() &&
191+
included_mode_tag->second >= mode_tag.second) {
192+
return true;
193+
}
194+
}
195+
return false;
196+
}
197+
198+
199+
bool VersionScriptParser::MatchIntroducedTags(const ParsedTags &tags) {
200+
if (tags.has_future_tag_ && api_level_ < utils::FUTURE_API_LEVEL) {
201+
return false;
202+
}
203+
if (tags.has_introduced_tags_ && api_level_ < tags.introduced_) {
150204
return false;
151205
}
206+
return true;
207+
}
152208

153-
if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
209+
210+
bool VersionScriptParser::IsSymbolExported(const ParsedTags &tags) {
211+
if (tags.has_excluded_tags_) {
154212
return false;
155213
}
156214

157-
if (tags.has_future_tag_) {
158-
return api_level_ == utils::FUTURE_API_LEVEL;
215+
if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
216+
return false;
159217
}
160218

161-
if (tags.has_introduced_tags_) {
162-
return api_level_ >= tags.introduced_;
219+
if (!included_mode_tags_.empty() && !tags.mode_tags_.empty()) {
220+
return MatchModeTags(tags);
163221
}
164222

165-
return true;
223+
return MatchIntroducedTags(tags);
166224
}
167225

168226

169-
bool VersionScriptParser::ParseSymbolLine(const std::string &line,
170-
bool is_in_extern_cpp) {
227+
bool VersionScriptParser::ParseSymbolLine(
228+
const std::string &line, bool is_in_extern_cpp,
229+
const ParsedTags &version_block_tags) {
171230
// The symbol name comes before the ';'.
172231
std::string::size_type pos = line.find(";");
173232
if (pos == std::string::npos) {
@@ -177,7 +236,7 @@ bool VersionScriptParser::ParseSymbolLine(const std::string &line,
177236

178237
std::string symbol(utils::Trim(line.substr(0, pos)));
179238

180-
ParsedTags tags = ParseSymbolTags(line);
239+
ParsedTags tags = ParseSymbolTags(line, version_block_tags);
181240
if (!IsSymbolExported(tags)) {
182241
return true;
183242
}
@@ -209,7 +268,8 @@ bool VersionScriptParser::ParseSymbolLine(const std::string &line,
209268
}
210269

211270

212-
bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols) {
271+
bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols,
272+
const ParsedTags &tags) {
213273
static const std::regex EXTERN_CPP_PATTERN(R"(extern\s*"[Cc]\+\+"\s*\{)");
214274

215275
LineScope scope = LineScope::GLOBAL;
@@ -250,7 +310,7 @@ bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols) {
250310

251311
// Parse symbol line
252312
if (!ignore_symbols) {
253-
if (!ParseSymbolLine(line, is_in_extern_cpp)) {
313+
if (!ParseSymbolLine(line, is_in_extern_cpp, tags)) {
254314
return false;
255315
}
256316
}
@@ -285,8 +345,8 @@ std::unique_ptr<ExportedSymbolSet> VersionScriptParser::Parse(
285345
bool exclude_symbol_version = utils::HasMatchingGlobPattern(
286346
excluded_symbol_versions_, version.c_str());
287347

288-
ParsedTags tags = ParseSymbolTags(line);
289-
if (!ParseVersionBlock(exclude_symbol_version || !IsSymbolExported(tags))) {
348+
ParsedTags tags = ParseSymbolTags(line, ParsedTags());
349+
if (!ParseVersionBlock(exclude_symbol_version, tags)) {
290350
return nullptr;
291351
}
292352
}

vndk/tools/header-checker/src/repr/symbol/version_script_parser.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "utils/api_level.h"
2121

2222
#include <functional>
23-
#include <set>
23+
#include <map>
2424
#include <string>
2525

2626

@@ -30,12 +30,14 @@ namespace repr {
3030

3131
class VersionScriptParser {
3232
private:
33+
// This comparison function allows finding elements by string_view.
34+
using ModeTagLevelMap = std::map<std::string, utils::ApiLevel, std::less<>>;
35+
3336
enum class LineScope {
3437
GLOBAL,
3538
LOCAL,
3639
};
3740

38-
3941
struct ParsedTags {
4042
public:
4143
unsigned has_arch_tags_ : 1;
@@ -46,7 +48,7 @@ class VersionScriptParser {
4648
unsigned has_var_tag_ : 1;
4749
unsigned has_weak_tag_ : 1;
4850
utils::ApiLevel introduced_;
49-
51+
ModeTagLevelMap mode_tags_;
5052

5153
public:
5254
ParsedTags()
@@ -84,6 +86,9 @@ class VersionScriptParser {
8486
excluded_symbol_tags_.insert(tag);
8587
}
8688

89+
// Returns whether the argument is valid.
90+
bool AddModeTag(std::string_view mode_tag);
91+
8792
void SetErrorHandler(std::unique_ptr<ErrorHandler> error_handler) {
8893
error_handler_ = std::move(error_handler);
8994
}
@@ -94,11 +99,17 @@ class VersionScriptParser {
9499
private:
95100
bool ReadLine(std::string &line);
96101

97-
bool ParseVersionBlock(bool ignore_symbols);
102+
bool ParseVersionBlock(bool ignore_symbols, const ParsedTags &tags);
103+
104+
bool ParseSymbolLine(const std::string &line, bool is_cpp_symbol,
105+
const ParsedTags &version_block_tags);
106+
107+
ParsedTags ParseSymbolTags(const std::string &line,
108+
const ParsedTags &initial_value);
98109

99-
bool ParseSymbolLine(const std::string &line, bool is_cpp_symbol);
110+
bool MatchModeTags(const ParsedTags &tags);
100111

101-
ParsedTags ParseSymbolTags(const std::string &line);
112+
bool MatchIntroducedTags(const ParsedTags &tags);
102113

103114
bool IsSymbolExported(const ParsedTags &tags);
104115

@@ -121,6 +132,7 @@ class VersionScriptParser {
121132

122133
utils::StringSet excluded_symbol_versions_;
123134
utils::StringSet excluded_symbol_tags_;
135+
ModeTagLevelMap included_mode_tags_;
124136

125137
std::istream *stream_;
126138
int line_no_;

vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,9 @@ TEST(VersionScriptParserTest, ParseSymbolTagsIntroduced) {
174174
ASSERT_TRUE(result);
175175

176176
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
177-
178-
EXPECT_TRUE(funcs.empty());
177+
// This may be an undefined behavior. ndkstubgen includes it in llndk mode,
178+
// but excludes it in ndk mode.
179+
EXPECT_THAT(funcs, ElementsAre(Key("test5")));
179180
}
180181

181182
{
@@ -355,6 +356,91 @@ TEST(VersionScriptParserTest, ExcludeSymbolTags) {
355356
}
356357

357358

359+
TEST(VersionScriptParserTest, IncludeSymbolTags) {
360+
static const char testdata[] = R"TESTDATA(
361+
LIBEX_1.0 {
362+
global:
363+
always; # unknown
364+
api34; # introduced=34
365+
api35; # introduced=35
366+
api35_llndk202404; # introduced=35 llndk=202404
367+
systemapi; # systemapi
368+
systemapi_llndk; # systemapi llndk
369+
};
370+
LIBEX_2.0 { # introduced=36
371+
api36_llndk202504;
372+
api36_llndk202504; # llndk=202504
373+
};
374+
)TESTDATA";
375+
376+
{
377+
VersionScriptParser parser;
378+
parser.SetApiLevel(34);
379+
parser.AddModeTag("llndk=202404");
380+
381+
std::istringstream stream(testdata);
382+
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
383+
ASSERT_TRUE(result);
384+
385+
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
386+
387+
EXPECT_THAT(funcs,
388+
ElementsAre(Key("always"), Key("api34"),
389+
Key("api35_llndk202404"), Key("systemapi_llndk")));
390+
}
391+
392+
{
393+
VersionScriptParser parser;
394+
parser.SetApiLevel(34);
395+
parser.AddModeTag("llndk");
396+
397+
std::istringstream stream(testdata);
398+
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
399+
ASSERT_TRUE(result);
400+
401+
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
402+
403+
EXPECT_THAT(
404+
funcs,
405+
ElementsAre(Key("always"), Key("api34"), Key("api35_llndk202404"),
406+
Key("api36_llndk202504"), Key("systemapi_llndk")));
407+
}
408+
409+
// Include all mode tags
410+
{
411+
VersionScriptParser parser;
412+
parser.SetApiLevel(utils::FUTURE_API_LEVEL);
413+
414+
std::istringstream stream(testdata);
415+
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
416+
ASSERT_TRUE(result);
417+
418+
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
419+
420+
EXPECT_THAT(funcs,
421+
ElementsAre(Key("always"), Key("api34"), Key("api35"),
422+
Key("api35_llndk202404"), Key("api36_llndk202504"),
423+
Key("systemapi"), Key("systemapi_llndk")));
424+
}
425+
426+
// Exclude all mode tags
427+
{
428+
VersionScriptParser parser;
429+
parser.SetApiLevel(utils::FUTURE_API_LEVEL);
430+
parser.AddModeTag("none");
431+
432+
std::istringstream stream(testdata);
433+
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
434+
ASSERT_TRUE(result);
435+
436+
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
437+
438+
EXPECT_THAT(funcs, ElementsAre(Key("always"), Key("api34"), Key("api35"),
439+
Key("api36_llndk202504")));
440+
}
441+
}
442+
443+
358444
TEST(VersionScriptParserTest, ParseExternCpp) {
359445
static const char testdata[] = R"TESTDATA(
360446
LIBEX_1.0 {

0 commit comments

Comments
 (0)