• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }