1 // Copyright (c) 2024 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gn/ohos_components_checker.h"
6
7 #include <filesystem>
8 #include <fstream>
9 #include <functional>
10 #include <iostream>
11 #include <sys/stat.h>
12
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/json/json_reader.h"
16 #include "base/values.h"
17 #include "gn/build_settings.h"
18 #include "gn/config.h"
19 #include "gn/filesystem_utils.h"
20 #include "gn/functions.h"
21 #include "gn/ohos_components.h"
22 #include "gn/parse_tree.h"
23 #include "gn/settings.h"
24 #include "gn/substitution_writer.h"
25 #include "gn/target.h"
26 #include "gn/value.h"
27
28 namespace fs = std::filesystem;
29
30 static const std::string SCAN_RESULT_PATH = "scan_out";
31 static const std::string WHITELIST_PATH = "build/component_compilation_whitelist.json";
32 static std::vector<std::string> all_deps_config_;
33 static std::vector<std::string> includes_over_range_;
34 static std::map<std::string, std::vector<std::string>> innerapi_public_deps_inner_;
35 static std::vector<std::string> innerapi_not_lib_;
36 static std::vector<std::string> innerapi_not_declare_;
37 static std::map<std::string, std::vector<std::string>> includes_absolute_deps_other_;
38 static std::map<std::string, std::vector<std::string>> target_absolute_deps_other_;
39 static std::map<std::string, std::vector<std::string>> import_other_;
40
41 OhosComponentChecker *OhosComponentChecker::instance_ = nullptr;
42
Trim(std::string & s)43 static std::string &Trim(std::string &s)
44 {
45 if (s.empty()) {
46 return s;
47 }
48 s.erase(0, s.find_first_not_of(" \t\r\n"));
49 s.erase(s.find_last_not_of(" \t\r\n") + 1);
50 return s;
51 }
52
StartWith(const std::string & str,const std::string prefix)53 static bool StartWith(const std::string &str, const std::string prefix)
54 {
55 return (str.rfind(prefix, 0) == 0);
56 }
57
CreateScanOutDir(const std::string & dir)58 static void CreateScanOutDir(const std::string &dir)
59 {
60 fs::path path(dir);
61 fs::create_directories(path);
62 return;
63 }
64
RemoveScanOutDir(const std::string & dir)65 static void RemoveScanOutDir(const std::string& dir)
66 {
67 if (access(dir.c_str(), F_OK) == -1) {
68 return;
69 }
70 for (auto& entry : fs::directory_iterator(dir)) {
71 if (entry.is_regular_file()) {
72 fs::remove(entry);
73 } else if (entry.is_directory()) {
74 RemoveScanOutDir(entry.path().string());
75 }
76 }
77 fs::remove(dir);
78 }
79
ReadBuildConfigFile(std::string & content)80 static bool ReadBuildConfigFile(std::string &content)
81 {
82 if (!base::ReadFileToString(base::FilePath(WHITELIST_PATH), &content)) {
83 return false;
84 }
85 return true;
86 }
87
LoadAllDepsConfigWhitelist(const base::Value & list)88 static void LoadAllDepsConfigWhitelist(const base::Value &list)
89 {
90 for (const base::Value &value : list.GetList()) {
91 all_deps_config_.push_back(value.GetString());
92 }
93 }
94
LoadIncludesOverRangeWhitelist(const base::Value & list)95 static void LoadIncludesOverRangeWhitelist(const base::Value &list)
96 {
97 for (const base::Value &value : list.GetList()) {
98 includes_over_range_.push_back(value.GetString());
99 }
100 }
101
LoadInnerApiPublicDepsInnerWhitelist(const base::Value & value)102 static void LoadInnerApiPublicDepsInnerWhitelist(const base::Value &value)
103 {
104 for (auto info : value.DictItems()) {
105 for (const base::Value &value_tmp : info.second.GetList()) {
106 innerapi_public_deps_inner_[info.first].push_back(value_tmp.GetString());
107 }
108 }
109 }
110
LoadInnerApiNotLibWhitelist(const base::Value & list)111 static void LoadInnerApiNotLibWhitelist(const base::Value &list)
112 {
113 for (const base::Value &value : list.GetList()) {
114 innerapi_not_lib_.push_back(value.GetString());
115 }
116 }
117
LoadInnerApiNotDeclareWhitelist(const base::Value & list)118 static void LoadInnerApiNotDeclareWhitelist(const base::Value &list)
119 {
120 for (const base::Value &value : list.GetList()) {
121 innerapi_not_declare_.push_back(value.GetString());
122 }
123 }
124
LoadIncludesAbsoluteDepsOtherWhitelist(const base::Value & value)125 static void LoadIncludesAbsoluteDepsOtherWhitelist(const base::Value &value)
126 {
127 for (auto info : value.DictItems()) {
128 for (const base::Value &value_tmp : info.second.GetList()) {
129 includes_absolute_deps_other_[info.first].push_back(value_tmp.GetString());
130 }
131 }
132 }
133
LoadAbsoluteDepsOtherWhitelist(const base::Value & value)134 static void LoadAbsoluteDepsOtherWhitelist(const base::Value &value)
135 {
136 for (auto info : value.DictItems()) {
137 for (const base::Value &value_tmp : info.second.GetList()) {
138 target_absolute_deps_other_[info.first].push_back(value_tmp.GetString());
139 }
140 }
141 }
142
LoadImportOtherWhitelist(const base::Value & value)143 static void LoadImportOtherWhitelist(const base::Value &value)
144 {
145 for (auto info : value.DictItems()) {
146 for (const base::Value &value_tmp : info.second.GetList()) {
147 import_other_[info.first].push_back(value_tmp.GetString());
148 }
149 }
150 }
151
152 static std::map<std::string, std::function<void(const base::Value &value)>> whitelist_map_ = {
153 { "all_dependent_configs", LoadAllDepsConfigWhitelist },
154 { "includes_over_range", LoadIncludesOverRangeWhitelist },
155 { "innerapi_not_lib", LoadInnerApiNotLibWhitelist },
156 { "innerapi_not_declare", LoadInnerApiNotDeclareWhitelist },
157 { "innerapi_public_deps_inner", LoadInnerApiPublicDepsInnerWhitelist },
158 { "includes_absolute_deps_other", LoadIncludesAbsoluteDepsOtherWhitelist },
159 { "target_absolute_deps_other", LoadAbsoluteDepsOtherWhitelist },
160 { "import_other", LoadImportOtherWhitelist }
161 };
162
LoadWhitelist()163 static void LoadWhitelist()
164 {
165 std::string whitelistContent;
166 if (!ReadBuildConfigFile(whitelistContent)) {
167 return;
168 }
169 const base::DictionaryValue *whitelist_dict;
170 std::unique_ptr<base::Value> whitelist = base::JSONReader::ReadAndReturnError(whitelistContent,
171 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, nullptr, nullptr, nullptr);
172 if (!whitelist) {
173 return;
174 }
175 if (!whitelist->GetAsDictionary(&whitelist_dict)) {
176 return;
177 }
178
179 for (const auto kv : whitelist_dict->DictItems()) {
180 auto iter = whitelist_map_.find(kv.first);
181 if (iter != whitelist_map_.end()) {
182 iter->second(kv.second);
183 }
184 }
185 return;
186 }
187
InterceptAllDepsConfig(const Target * target,const std::string label,Err * err) const188 bool OhosComponentChecker::InterceptAllDepsConfig(const Target *target, const std::string label, Err *err) const
189 {
190 auto result = std::find(all_deps_config_.begin(), all_deps_config_.end(), label);
191 if (result != all_deps_config_.end()) {
192 return true;
193 }
194
195 *err = Err(target->defined_from(), "all_dependent_configs not allowed.",
196 "The item " + label + " does not allow all_dependent_configs.");
197 return false;
198 }
199
InterceptIncludesOverRange(const Target * target,const std::string label,const std::string dir,Err * err) const200 bool OhosComponentChecker::InterceptIncludesOverRange(const Target *target, const std::string label,
201 const std::string dir, Err *err) const
202 {
203 auto result = std::find(includes_over_range_.begin(), includes_over_range_.end(), label);
204 if (result != includes_over_range_.end()) {
205 return true;
206 }
207 *err = Err(target->defined_from(), "Header file range is too large.",
208 "The item " + label + " header : " + dir + " range is too large.");
209 return false;
210 }
211
InterceptInnerApiPublicDepsInner(const Target * target,const std::string label,const std::string deps,Err * err) const212 bool OhosComponentChecker::InterceptInnerApiPublicDepsInner(const Target *target, const std::string label,
213 const std::string deps, Err *err) const
214 {
215 if (auto res = innerapi_public_deps_inner_.find(label); res != innerapi_public_deps_inner_.end()) {
216 std::string deps_str(deps);
217 auto res_second = std::find(res->second.begin(), res->second.end(), Trim(deps_str));
218 if (res_second != res->second.end()) {
219 return true;
220 }
221 }
222 *err = Err(target->defined_from(), "InnerApi not allow the use of public_deps dependent internal modules.",
223 "The item " + label + " not allow the use of public_deps dependent internal modules : " + deps);
224 return false;
225 }
226
InterceptInnerApiNotLib(const Item * item,const std::string label,Err * err) const227 bool OhosComponentChecker::InterceptInnerApiNotLib(const Item *item, const std::string label, Err *err) const
228 {
229 auto result = std::find(innerapi_not_lib_.begin(), innerapi_not_lib_.end(), label);
230 if (result != innerapi_not_lib_.end()) {
231 return true;
232 }
233 *err =
234 Err(item->defined_from(), "InnerApi is not a library type.", "The item " + label + " is not a library type.");
235 return false;
236 }
237
InterceptInnerApiNotDeclare(const Item * item,const std::string label,Err * err) const238 bool OhosComponentChecker::InterceptInnerApiNotDeclare(const Item *item, const std::string label, Err *err) const
239 {
240 auto result = std::find(innerapi_not_declare_.begin(), innerapi_not_declare_.end(), label);
241 if (result != innerapi_not_declare_.end()) {
242 return true;
243 }
244 *err = Err(item->defined_from(), "InnerApi is not defined in bundle.json.",
245 "The item " + label + " is not defined in bundle.json.");
246 return false;
247 }
248
InterceptIncludesAbsoluteDepsOther(const Target * target,const std::string label,const std::string includes,Err * err) const249 bool OhosComponentChecker::InterceptIncludesAbsoluteDepsOther(const Target *target, const std::string label,
250 const std::string includes, Err *err) const
251 {
252 if (auto res = includes_absolute_deps_other_.find(label); res != includes_absolute_deps_other_.end()) {
253 std::string includes_str(includes);
254 auto res_second = std::find(res->second.begin(), res->second.end(), Trim(includes_str));
255 if (res_second != res->second.end()) {
256 return true;
257 }
258 }
259 *err = Err(target->defined_from(), "Do not directly use header files of other components.",
260 "The item " + label + " do not directly use header files : " + includes + " of other components." +
261 "\n"
262 "Please use 'external_deps/public_external_deps' dependent module.");
263 return false;
264 }
265
266
InterceptTargetAbsoluteDepsOther(const Item * item,const std::string label,const std::string deps,Err * err) const267 bool OhosComponentChecker::InterceptTargetAbsoluteDepsOther(const Item *item, const std::string label,
268 const std::string deps, Err *err) const
269 {
270 if (auto res = target_absolute_deps_other_.find(label); res != target_absolute_deps_other_.end()) {
271 std::string deps_str(deps);
272 auto res_second = std::find(res->second.begin(), res->second.end(), Trim(deps_str));
273 if (res_second != res->second.end()) {
274 return true;
275 }
276 }
277 *err = Err(item->defined_from(), "Not allow use absolute dependent other component.",
278 "The item " + label + " not allow use absolute dependent other component : " + deps +
279 "\n"
280 "Please use 'external_deps/public_external_deps'.");
281 return false;
282 }
283
InterceptInnerApiVisibilityDenied(const Item * item,const std::string from_label,const std::string to_label,Err * err) const284 bool OhosComponentChecker::InterceptInnerApiVisibilityDenied(const Item *item, const std::string from_label,
285 const std::string to_label, Err *err) const
286 {
287 *err = Err(item->defined_from(), "InnerApi visibility denied.",
288 "The item " + from_label + " cannot dependent " + to_label +
289 "\n"
290 "Please check 'visibility' field in 'bundle.json' of " +
291 to_label);
292 return false;
293 }
294
InterceptImportOther(const FunctionCallNode * function,const std::string label,const std::string deps,Err * err) const295 bool OhosComponentChecker::InterceptImportOther(const FunctionCallNode* function, const std::string label,
296 const std::string deps, Err *err) const
297 {
298 if (auto res = import_other_.find(label); res != import_other_.end()) {
299 std::string deps_str(deps);
300 auto res_second = std::find(res->second.begin(), res->second.end(), Trim(deps_str));
301 if (res_second != res->second.end()) {
302 return true;
303 }
304 }
305 *err = Err(function->function(), "Not allow import other gni.",
306 label + " not allow import other gni : " + deps);
307 return false;
308 }
309
OhosComponentChecker(const std::string & build_dir,int checkType)310 OhosComponentChecker::OhosComponentChecker(const std::string &build_dir, int checkType)
311 {
312 checkType_ = checkType;
313 build_dir_ = build_dir;
314 if (checkType_ == CheckType::INTERCEPT_IGNORE_TEST || checkType_ == CheckType::INTERCEPT_ALL) {
315 LoadWhitelist();
316 }
317 if (checkType_ == CheckType::SCAN_ALL || checkType_ == CheckType::INTERCEPT_ALL) {
318 ignoreTest_ = false;
319 }
320 RemoveScanOutDir(build_dir_ + "/" + SCAN_RESULT_PATH);
321 }
322
GenerateScanList(const std::string path,const std::string subsystem,const std::string component,const std::string label,const std::string deps) const323 void OhosComponentChecker::GenerateScanList(const std::string path, const std::string subsystem,
324 const std::string component, const std::string label, const std::string deps) const
325 {
326 CreateScanOutDir(build_dir_ + "/" + SCAN_RESULT_PATH);
327 std::ofstream file;
328 file.open(build_dir_ + "/" + SCAN_RESULT_PATH + "/" + path, std::ios::app);
329 file << subsystem << " " << component << " " << label << " " << deps << "\n";
330 file.close();
331 return;
332 }
333
CheckAllDepsConfigs(const Target * target,const std::string label,Err * err) const334 bool OhosComponentChecker::CheckAllDepsConfigs(const Target *target, const std::string label, Err *err) const
335 {
336 if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) {
337 return true;
338 }
339
340 const OhosComponent *component = target->ohos_component();
341 if (component == nullptr) {
342 return true;
343 }
344 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
345 return InterceptAllDepsConfig(target, label, err);
346 }
347 GenerateScanList("all_dependent_configs.list", component->subsystem(), component->name(), label, "");
348 return true;
349 }
350
CheckInnerApiIncludesOverRange(const Target * target,const std::string label,const std::string dir,Err * err) const351 bool OhosComponentChecker::CheckInnerApiIncludesOverRange(const Target *target, const std::string label,
352 const std::string dir, Err *err) const
353 {
354 if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) {
355 return true;
356 }
357
358 const OhosComponent *component = target->ohos_component();
359 if (component == nullptr || (!component->isInnerApi(label) && !StartWith(label, "//third_party"))) {
360 return true;
361 }
362
363 if (dir != "." && dir != "./" && dir != "../" && dir != component->path() && dir != component->path() + "/") {
364 return true;
365 }
366
367 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
368 return InterceptIncludesOverRange(target, label, dir, err);
369 }
370 GenerateScanList("includes_over_range.list", component->subsystem(), component->name(), label, dir);
371 return true;
372 }
373
CheckInnerApiPublicDepsInner(const Target * target,const std::string label,const std::string deps,Err * err) const374 bool OhosComponentChecker::CheckInnerApiPublicDepsInner(const Target *target, const std::string label,
375 const std::string deps, Err *err) const
376 {
377 if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) {
378 return true;
379 }
380
381 const OhosComponent *component = target->ohos_component();
382 if (component == nullptr || !component->isInnerApi(label)) {
383 return true;
384 }
385
386 if (!StartWith(deps, component->path()) && StartWith(deps, "//")) {
387 return true;
388 }
389
390 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
391 return InterceptInnerApiPublicDepsInner(target, label, deps, err);
392 }
393 GenerateScanList("innerapi_public_deps_inner.list", component->subsystem(), component->name(), label, deps);
394 return true;
395 }
396
CheckInnerApiNotLib(const Item * item,const OhosComponent * component,const std::string label,Err * err) const397 bool OhosComponentChecker::CheckInnerApiNotLib(const Item *item, const OhosComponent *component,
398 const std::string label, Err *err) const
399 {
400 if (checkType_ <= CheckType::NONE || item == nullptr || item->AsTarget() == nullptr ||
401 (ignoreTest_ && item->testonly()) || component == nullptr) {
402 return true;
403 }
404
405 Target::OutputType type = item->AsTarget()->output_type();
406 if (type == Target::SHARED_LIBRARY || type == Target::STATIC_LIBRARY || type == Target::RUST_LIBRARY) {
407 return true;
408 }
409
410 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
411 return InterceptInnerApiNotLib(item, label, err);
412 }
413 GenerateScanList("innerapi_not_lib.list", component->subsystem(), component->name(), label, "");
414 return true;
415 }
416
CheckInnerApiNotDeclare(const Item * item,const OhosComponent * component,const std::string label,Err * err) const417 bool OhosComponentChecker::CheckInnerApiNotDeclare(const Item *item, const OhosComponent *component,
418 const std::string label, Err *err) const
419 {
420 if (checkType_ <= CheckType::NONE || component == nullptr || item == nullptr || (ignoreTest_ && item->testonly())) {
421 return true;
422 }
423
424 if (component->isInnerApi(label)) {
425 return true;
426 }
427 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
428 return InterceptInnerApiNotDeclare(item, label, err);
429 }
430 GenerateScanList("innerapi_not_declare.list", component->subsystem(), component->name(), label, "");
431 return true;
432 }
433
CheckIncludesAbsoluteDepsOther(const Target * target,const std::string label,const std::string includes,Err * err) const434 bool OhosComponentChecker::CheckIncludesAbsoluteDepsOther(const Target *target, const std::string label,
435 const std::string includes, Err *err) const
436 {
437 if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) {
438 return true;
439 }
440
441 if (!StartWith(includes, "//") || StartWith(includes, "//out/") || StartWith(includes, "////out/")) {
442 return true;
443 }
444
445 const OhosComponent *component = target->ohos_component();
446 if (component == nullptr) {
447 return true;
448 }
449
450 if (StartWith(includes, component->path())) {
451 return true;
452 }
453
454 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
455 return InterceptIncludesAbsoluteDepsOther(target, label, includes, err);
456 }
457 GenerateScanList("includes_absolute_deps_other.list", component->subsystem(), component->name(), label, includes);
458 return true;
459 }
460
CheckInnerApiVisibilityDenied(const Item * item,const OhosComponent * component,const std::string label,const std::string deps,Err * err) const461 bool OhosComponentChecker::CheckInnerApiVisibilityDenied(const Item *item, const OhosComponent *component,
462 const std::string label, const std::string deps, Err *err) const
463 {
464 if (checkType_ <= CheckType::NONE || component == nullptr || item == nullptr || (ignoreTest_ && item->testonly())) {
465 return true;
466 }
467
468 if (!component->isInnerApi(deps)) {
469 return true;
470 }
471 std::vector<std::string> visibility = component->getInnerApiVisibility(deps);
472 if (visibility.empty()) {
473 return true;
474 }
475
476 const OhosComponent *from_component = item->ohos_component();
477 if (from_component == nullptr) {
478 return true;
479 }
480 auto result = std::find(visibility.begin(), visibility.end(), from_component->name());
481 if (result != visibility.end()) {
482 return true;
483 }
484
485 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
486 return InterceptInnerApiVisibilityDenied(item, label, deps, err);
487 }
488 GenerateScanList("innerkit_visibility_denied.list", from_component->subsystem(), from_component->name(), label,
489 deps);
490 return true;
491 }
492
CheckTargetAbsoluteDepsOther(const Item * item,const OhosComponent * component,const std::string label,const std::string deps,bool is_external_deps,Err * err) const493 bool OhosComponentChecker::CheckTargetAbsoluteDepsOther(const Item *item, const OhosComponent *component,
494 const std::string label, const std::string deps, bool is_external_deps, Err *err) const
495 {
496 if (checkType_ <= CheckType::NONE || component == nullptr || item == nullptr || (ignoreTest_ && item->testonly())) {
497 return true;
498 }
499 if (!component->isInnerApi(deps)) {
500 return true;
501 }
502
503 if (is_external_deps) {
504 return true;
505 }
506
507 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
508 return InterceptTargetAbsoluteDepsOther(item, label, deps, err);
509 }
510
511 const OhosComponent *from_component = item->ohos_component();
512 if (from_component == nullptr) {
513 return true;
514 }
515 GenerateScanList("target_absolute_deps_other.list",
516 from_component->subsystem(), from_component->name(), label, deps);
517 return true;
518 }
519
CheckImportOther(const FunctionCallNode * function,const BuildSettings * build_settings,const std::string label,const std::string deps,Err * err) const520 bool OhosComponentChecker::CheckImportOther(const FunctionCallNode* function, const BuildSettings* build_settings,
521 const std::string label, const std::string deps, Err *err) const
522 {
523 if (checkType_ <= CheckType::NONE || function == nullptr || build_settings == nullptr) {
524 return true;
525 }
526 const OhosComponent *component = build_settings->GetOhosComponent(label);
527 if (component == nullptr) {
528 return true;
529 }
530 if (StartWith(deps, component->path()) || StartWith(deps, "//build/") || StartWith(deps, "//out/")) {
531 return true;
532 }
533
534 if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) {
535 return InterceptImportOther(function, label, deps, err);
536 }
537 GenerateScanList("import_other.list", component->subsystem(), component->name(), label, deps);
538 return true;
539 }