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 #include "gn/ohos_components.h"
5
6 #include <cstring>
7 #include <iostream>
8 #include <map>
9
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/json/json_reader.h"
13 #include "base/values.h"
14
15 #include "gn/err.h"
16 #include "gn/filesystem_utils.h"
17 #include "gn/innerapis_publicinfo_generator.h"
18 #include "gn/ohos_components_checker.h"
19 #include "gn/ohos_components_impl.h"
20 #include "gn/ohos_components_mapping.h"
21
22 /**
23 * Ohos Component API
24 *
25 * Each component belongs to one subsystem.
26 * Each component has a source path.
27 * Each component has zero or more innerapis
28 */
29
30 static const std::string EMPTY_INNERAPI;
31
32 static const int PATH_PREFIX_LEN = 2;
33
34 OhosComponent::OhosComponent() = default;
35
OhosComponent(const char * name,const char * subsystem,const char * path)36 OhosComponent::OhosComponent(const char *name, const char *subsystem, const char *path)
37 {
38 name_ = std::string(name);
39 subsystem_ = std::string(subsystem);
40 if (strncmp(path, "//", PATH_PREFIX_LEN) == 0) {
41 path_ = std::string(path);
42 } else {
43 path_ = "//" + std::string(path);
44 }
45 }
46
addInnerApi(const std::string & name,const std::string & label)47 void OhosComponent::addInnerApi(const std::string &name, const std::string &label)
48 {
49 std::string la = label;
50 size_t pos = label.find(":");
51 if (pos != std::string::npos) {
52 if ((label[pos - 1]) == '/') { // some are like this : "//components/foo/libfoo/:libfoo"
53 unsigned long indexToRemove = pos - 1;
54 if (indexToRemove >= 0 && indexToRemove <= la.length()) {
55 la.erase(la.begin() + indexToRemove);
56 }
57 }
58 }
59 innerapi_names_[name] = la;
60 innerapi_labels_[la] = name;
61 }
62
63
getInnerApi(const std::string & innerapi) const64 const std::string &OhosComponent::getInnerApi(const std::string &innerapi) const
65 {
66 if (auto res = innerapi_names_.find(innerapi); res != innerapi_names_.end()) {
67 return res->second;
68 }
69 return EMPTY_INNERAPI;
70 }
71
isInnerApi(const std::string & label) const72 bool OhosComponent::isInnerApi(const std::string &label) const
73 {
74 if (auto res = innerapi_labels_.find(label); res != innerapi_labels_.end()) {
75 return true;
76 }
77 return false;
78 }
79
addInnerApiVisibility(const std::string & name,const std::vector<base::Value> & list)80 void OhosComponent::addInnerApiVisibility(const std::string &name, const std::vector<base::Value> &list)
81 {
82 for (const base::Value &visibility : list) {
83 innerapi_visibility_[innerapi_names_[name]].push_back(visibility.GetString());
84 }
85 }
86
getInnerApiVisibility(const std::string & label) const87 const std::vector<std::string> OhosComponent::getInnerApiVisibility(const std::string &label) const
88 {
89 if (auto res = innerapi_visibility_.find(label); res != innerapi_visibility_.end()) {
90 return res->second;
91 }
92 return {};
93 }
94
95 /**
96 * Ohos Component Implimentation API
97 */
98 OhosComponentsImpl::OhosComponentsImpl() = default;
99
ReadBuildConfigFile(const std::string & build_dir,const char * subfile,std::string & content)100 bool OhosComponentsImpl::ReadBuildConfigFile(const std::string &build_dir, const char *subfile, std::string &content)
101 {
102 std::string path = build_dir;
103 path += "/build_configs/";
104 path += subfile;
105 if (!base::ReadFileToString(base::FilePath(path), &content)) {
106 return false;
107 }
108 return true;
109 }
110
LoadComponentInfo(const std::string & components_content,std::string & err_msg_out)111 bool OhosComponentsImpl::LoadComponentInfo(const std::string &components_content, std::string &err_msg_out)
112 {
113 const base::DictionaryValue *components_dict;
114 std::unique_ptr<base::Value> components_value = base::JSONReader::ReadAndReturnError(components_content,
115 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr);
116 if (!components_value) {
117 return false;
118 }
119 if (!components_value->GetAsDictionary(&components_dict)) {
120 return false;
121 }
122
123 for (const auto com : components_dict->DictItems()) {
124 const base::Value *subsystem = com.second.FindKey("subsystem");
125 const base::Value *path = com.second.FindKey("path");
126 if (!subsystem || !path) {
127 continue;
128 }
129 components_[com.first] =
130 new OhosComponent(com.first.c_str(), subsystem->GetString().c_str(), path->GetString().c_str());
131 const base::Value *innerapis = com.second.FindKey("innerapis");
132 if (!innerapis) {
133 continue;
134 }
135 LoadInnerApi(com.first, innerapis->GetList());
136 }
137 setupComponentsTree();
138 return true;
139 }
140
findChildByPath(const struct OhosComponentTree * current,const char * path,size_t len)141 const struct OhosComponentTree *OhosComponentsImpl::findChildByPath(const struct OhosComponentTree *current,
142 const char *path, size_t len)
143 {
144 if (current->child == nullptr) {
145 return nullptr;
146 }
147 const struct OhosComponentTree *item = current->child;
148 while (item != nullptr) {
149 if (strncmp(item->dirName, path, len) == 0) {
150 // Exactly matching
151 if (item->dirName[len] == '\0') {
152 return item;
153 }
154 }
155 item = item->next;
156 }
157
158 return nullptr;
159 }
160
matchComponentByLabel(const char * label)161 const OhosComponent *OhosComponentsImpl::matchComponentByLabel(const char *label)
162 {
163 const struct OhosComponentTree *child;
164 const struct OhosComponentTree *current = pathTree;
165
166 if (!label) {
167 return nullptr;
168 }
169 // Skip leading //
170 if (strncmp(label, "//", PATH_PREFIX_LEN) == 0) {
171 label += PATH_PREFIX_LEN;
172 }
173
174 size_t len;
175 const char *sep;
176 while (label[0] != '\0') {
177 // Get next path seperator
178 sep = strchr(label, '/');
179 if (sep) {
180 len = sep - label;
181 } else {
182 // Check if it is a label with target name
183 sep = strchr(label, ':');
184 if (sep) {
185 len = sep - label;
186 } else {
187 len = strlen(label);
188 }
189 }
190
191 // Match with children
192 child = findChildByPath(current, label, len);
193 if (child == nullptr) {
194 break;
195 }
196
197 // No children, return current matched item
198 if (child->child == nullptr) {
199 return child->component;
200 }
201
202 label += len;
203 // Finish matching if target name started
204 if (label[0] == ':') {
205 return child->component;
206 }
207
208 // Match with child again
209 current = child;
210
211 // Skip leading seperator
212 if (label[0] == '/') {
213 label += 1;
214 }
215 }
216
217 return nullptr;
218 }
219
addComponentToTree(struct OhosComponentTree * current,OhosComponent * component)220 void OhosComponentsImpl::addComponentToTree(struct OhosComponentTree *current, OhosComponent *component)
221 {
222 size_t len;
223 const char *path = component->path().c_str() + PATH_PREFIX_LEN;
224 const char *sep;
225
226 while (path[0] != '\0') {
227 sep = strchr(path, '/');
228 if (sep) {
229 len = sep - path;
230 } else {
231 len = strlen(path);
232 }
233
234 // Check if node already exists
235 struct OhosComponentTree *child = (struct OhosComponentTree *)findChildByPath(current, path, len);
236 if (!child) {
237 // Add intermediate node
238 child = new struct OhosComponentTree(path, len, nullptr);
239 child->next = current->child;
240 current->child = child;
241 }
242
243 // End of path detected, setup component pointer
244 path = path + len;
245 if (path[0] == '\0') {
246 child->component = component;
247 break;
248 }
249
250 // Continue to add next part
251 path += 1;
252 current = child;
253 }
254 }
255
setupComponentsTree()256 void OhosComponentsImpl::setupComponentsTree()
257 {
258 pathTree = new struct OhosComponentTree("//", nullptr);
259
260 std::map<std::string, OhosComponent *>::iterator it;
261 for (it = components_.begin(); it != components_.end(); it++) {
262 addComponentToTree(pathTree, it->second);
263 }
264 }
265
LoadInnerApi(const std::string & component_name,const std::vector<base::Value> & innerapis)266 void OhosComponentsImpl::LoadInnerApi(const std::string &component_name, const std::vector<base::Value> &innerapis)
267 {
268 OhosComponent *component = (OhosComponent *)GetComponentByName(component_name);
269 if (!component) {
270 return;
271 }
272 for (const base::Value &kv : innerapis) {
273 const base::Value *label = kv.FindKey("label");
274 const base::Value *name = kv.FindKey("name");
275
276 if (!label || !name) {
277 continue;
278 }
279 component->addInnerApi(name->GetString(), label->GetString());
280 const base::Value *visibility = kv.FindKey("visibility");
281 if (!visibility) {
282 continue;
283 }
284 component->addInnerApiVisibility(name->GetString(), visibility->GetList());
285 }
286 }
287
LoadOverrideMap(const std::string & override_map)288 void OhosComponentsImpl::LoadOverrideMap(const std::string &override_map)
289 {
290 const base::DictionaryValue *override_dict;
291 std::unique_ptr<base::Value> override_value = base::JSONReader::ReadAndReturnError(override_map,
292 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, nullptr, nullptr, nullptr);
293 if (!override_value) {
294 return;
295 }
296 if (!override_value->GetAsDictionary(&override_dict)) {
297 return;
298 }
299
300 for (const auto com : override_dict->DictItems()) {
301 override_map_[com.first] = com.second.GetString();
302 }
303 return;
304 }
305
LoadToolchain(const Value * product)306 void OhosComponentsImpl::LoadToolchain(const Value *product)
307 {
308 if (!product) {
309 return;
310 }
311 std::string path = "out/preloader/" + product->string_value() + "/build_config.json";
312 std::string content;
313 if (!base::ReadFileToString(base::FilePath(path), &content)) {
314 return;
315 }
316
317 const base::DictionaryValue *content_dict;
318 std::unique_ptr<base::Value> content_value = base::JSONReader::ReadAndReturnError(content,
319 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, nullptr, nullptr, nullptr);
320 if (!content_value) {
321 return;
322 }
323 if (!content_value->GetAsDictionary(&content_dict)) {
324 return;
325 }
326
327 for (const auto com : content_dict->DictItems()) {
328 if (com.first == "product_toolchain_label") {
329 toolchain_ = com.second.GetString();
330 break;
331 }
332 }
333 return;
334 }
335
LoadOhosComponents(const std::string & build_dir,const Value * enable,const Value * indep,const Value * product,Err * err)336 bool OhosComponentsImpl::LoadOhosComponents(const std::string &build_dir, const Value *enable,
337 const Value *indep, const Value *product, Err *err)
338 {
339 const char *components_file = "parts_info/components.json";
340 std::string components_content;
341 if (!ReadBuildConfigFile(build_dir, components_file, components_content)) {
342 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
343 "OpenHarmony build config file (" +
344 std::string(components_file) + ") does not exists.\n");
345 return false;
346 }
347
348 std::string override_map;
349 if (ReadBuildConfigFile(build_dir, "component_override_map.json", override_map)) {
350 LoadOverrideMap(override_map);
351 }
352
353 std::string err_msg_out;
354 if (!LoadComponentInfo(components_content, err_msg_out)) {
355 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
356 "OpenHarmony build config file parsing failed:\n" +
357 err_msg_out + "\n");
358 return false;
359 }
360 if (indep && indep->boolean_value()) {
361 is_indep_compiler_enable_ = true;
362 }
363 LoadToolchain(product);
364 return true;
365 }
366
GetComponentByName(const std::string & component_name) const367 const OhosComponent *OhosComponentsImpl::GetComponentByName(const std::string &component_name) const
368 {
369 if (auto res = components_.find(component_name); res != components_.end()) {
370 return res->second;
371 }
372 return nullptr;
373 }
374
GetWholeArchiveFlag(std::string str_val,int & whole_status)375 static size_t GetWholeArchiveFlag(std::string str_val, int &whole_status)
376 {
377 size_t sep_whole = str_val.find("(--whole-archive)");
378 if (sep_whole != std::string::npos) {
379 whole_status = 1;
380 } else {
381 sep_whole = str_val.find("(--no-whole-archive)");
382 if (sep_whole != std::string::npos) {
383 whole_status = 0;
384 } else {
385 whole_status = -1;
386 }
387 }
388 return sep_whole;
389 }
390
GetPrivateDepsLabel(const Value & dep,std::string & label,const Label & current_toolchain,int & whole_status,Err * err) const391 bool OhosComponentsImpl::GetPrivateDepsLabel(const Value &dep, std::string &label,
392 const Label& current_toolchain, int &whole_status, Err *err) const
393 {
394 std::string str_val = dep.string_value();
395 size_t sep_whole = GetWholeArchiveFlag(str_val, whole_status);
396
397 if (sep_whole != std::string::npos) {
398 label = str_val.substr(0, sep_whole);
399 } else {
400 label = str_val;
401 }
402 std::string current_toolchain_str = current_toolchain.GetUserVisibleName(false);
403 size_t tool_sep = label.find("(");
404 if (tool_sep == std::string::npos && GetTargetToolchain() != current_toolchain_str) {
405 label += "(" + current_toolchain_str + ")";
406 }
407 if (label == EMPTY_INNERAPI) {
408 *err = Err(dep,
409 "Deps label: (" + dep.string_value() + ") format error.");
410 return false;
411 }
412 return true;
413 }
414
GetExternalDepsLabel(const Value & external_dep,std::string & label,const Label & current_toolchain,int & whole_status,Err * err) const415 bool OhosComponentsImpl::GetExternalDepsLabel(const Value &external_dep, std::string &label,
416 const Label& current_toolchain, int &whole_status, Err *err) const
417 {
418 std::string str_val = external_dep.string_value();
419 size_t sep = str_val.find(":");
420 if (sep == std::string::npos) {
421 *err = Err(external_dep, "OHOS component external_deps format error: (" + external_dep.string_value() +
422 "),"
423 "it should be a string like \"component_name:innerapi_name\".");
424 return false;
425 }
426 std::string component_name = str_val.substr(0, sep);
427 for (const auto& pair : override_map_) {
428 if (pair.first == component_name) {
429 component_name = pair.second;
430 break;
431 }
432 }
433 const OhosComponent *component = GetComponentByName(component_name);
434 if (component == nullptr) {
435 *err = Err(external_dep, "OHOS component : (" + component_name + ") not found.");
436 return false;
437 }
438
439 std::string innerapi_name;
440 std::string tool_chain = "";
441 size_t sep_whole = GetWholeArchiveFlag(str_val, whole_status);
442 if (sep_whole != std::string::npos) {
443 innerapi_name = str_val.substr(sep + 1, sep_whole - sep - 1);
444 } else {
445 innerapi_name = str_val.substr(sep + 1);
446 size_t tool_sep = innerapi_name.find("(");
447 if (tool_sep != std::string::npos) {
448 tool_chain = innerapi_name.substr(tool_sep);
449 innerapi_name = innerapi_name.substr(0, tool_sep);
450 }
451 }
452
453 std::string current_toolchain_str = current_toolchain.GetUserVisibleName(false);
454 if (tool_chain == "" && GetTargetToolchain() != current_toolchain_str) {
455 tool_chain = "(" + current_toolchain_str + ")";
456 }
457 if (isOhosIndepCompilerEnable()) {
458 label = component->getInnerApi(innerapi_name + tool_chain);
459 if (label == EMPTY_INNERAPI) {
460 label = component->getInnerApi(innerapi_name);
461 }
462 } else {
463 label = component->getInnerApi(innerapi_name) + tool_chain;
464 }
465
466 if (label == EMPTY_INNERAPI) {
467 *err = Err(external_dep,
468 "OHOS innerapi: (" + innerapi_name + ") not found for component (" + component_name + ").");
469 return false;
470 }
471 return true;
472 }
473
GetSubsystemName(const Value & component_name,std::string & subsystem_name,Err * err) const474 bool OhosComponentsImpl::GetSubsystemName(const Value &component_name, std::string &subsystem_name, Err *err) const
475 {
476 const OhosComponent *component = GetComponentByName(component_name.string_value());
477 if (component == nullptr) {
478 *err = Err(component_name, "OHOS component : (" + component_name.string_value() + ") not found.");
479 return false;
480 }
481
482 subsystem_name = component->subsystem();
483 return true;
484 }
485
486 /**
487 * Ohos Components Public API
488 */
489
490 OhosComponents::OhosComponents() = default;
491
LoadOhosComponents(const std::string & build_dir,const Value * enable,const Value * indep,const Value * product,Err * err)492 bool OhosComponents::LoadOhosComponents(const std::string &build_dir,
493 const Value *enable, const Value *indep, const Value *product, Err *err)
494 {
495 if (!enable) {
496 // Not enabled
497 return true;
498 }
499 if (!enable->VerifyTypeIs(Value::BOOLEAN, err)) {
500 return false;
501 }
502
503 // Disabled
504 if (!enable->boolean_value()) {
505 return true;
506 }
507
508 mgr = new OhosComponentsImpl();
509
510 if (!mgr->LoadOhosComponents(build_dir, enable, indep, product, err)) {
511 delete mgr;
512 mgr = nullptr;
513 return false;
514 }
515
516 return true;
517 }
518
isOhosComponentsLoaded() const519 bool OhosComponents::isOhosComponentsLoaded() const
520 {
521 if (mgr == nullptr) {
522 return false;
523 } else {
524 return true;
525 }
526 }
527
GetExternalDepsLabel(const Value & external_dep,std::string & label,const Label & current_toolchain,int & whole_status,Err * err) const528 bool OhosComponents::GetExternalDepsLabel(const Value &external_dep, std::string &label,
529 const Label& current_toolchain, int &whole_status, Err *err) const
530 {
531 if (!mgr) {
532 if (err) {
533 *err = Err(external_dep, "You are compiling OpenHarmony components, but \n"
534 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
535 }
536 return false;
537 }
538 return mgr->GetExternalDepsLabel(external_dep, label, current_toolchain, whole_status, err);
539 }
540
GetPrivateDepsLabel(const Value & dep,std::string & label,const Label & current_toolchain,int & whole_status,Err * err) const541 bool OhosComponents::GetPrivateDepsLabel(const Value &dep, std::string &label,
542 const Label& current_toolchain, int &whole_status, Err *err) const
543 {
544 if (!mgr) {
545 if (err) {
546 *err = Err(dep, "You are compiling OpenHarmony components, but \n"
547 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
548 }
549 return false;
550 }
551 return mgr->GetPrivateDepsLabel(dep, label, current_toolchain, whole_status, err);
552 }
553
GetSubsystemName(const Value & part_name,std::string & label,Err * err) const554 bool OhosComponents::GetSubsystemName(const Value &part_name, std::string &label, Err *err) const
555 {
556 if (!mgr) {
557 if (err) {
558 *err = Err(part_name, "You are compiling OpenHarmony components, but \n"
559 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
560 }
561 return false;
562 }
563 return mgr->GetSubsystemName(part_name, label, err);
564 }
565
GetComponentByLabel(const std::string & label) const566 const OhosComponent *OhosComponents::GetComponentByLabel(const std::string &label) const
567 {
568 if (!mgr) {
569 return nullptr;
570 }
571 return mgr->matchComponentByLabel(label.c_str());
572 }
573
LoadOhosComponentsChecker(const std::string & build_dir,const Value * support,int checkType,unsigned int ruleSwitch)574 void OhosComponents::LoadOhosComponentsChecker(const std::string &build_dir, const Value *support, int checkType,
575 unsigned int ruleSwitch)
576 {
577 if (!support) {
578 return;
579 }
580 if (!support->boolean_value()) {
581 return;
582 }
583 if (checkType > OhosComponentChecker::CheckType::INTERCEPT_ALL ||
584 checkType <= OhosComponentChecker::CheckType::NONE) {
585 InnerApiPublicInfoGenerator::Init(build_dir, 0);
586 return;
587 }
588 OhosComponentChecker::Init(build_dir, checkType, ruleSwitch);
589 InnerApiPublicInfoGenerator::Init(build_dir, checkType);
590 return;
591 }
592
LoadOhosComponentsMapping(const std::string & build_dir,const Value * support,const Value * independent)593 void OhosComponents::LoadOhosComponentsMapping(const std::string& build_dir,
594 const Value *support, const Value *independent)
595 {
596 if (!support) {
597 return;
598 }
599 if (!support->boolean_value()) {
600 return;
601 }
602
603 if (!independent || !independent->boolean_value()) {
604 return;
605 }
606
607 OhosComponentMapping::Init(build_dir);
608 return;
609 }
610
GetComponentByName(const std::string & component_name)611 const OhosComponent *OhosComponents::GetComponentByName(const std::string &component_name) {
612 if (!mgr) {
613 return nullptr;
614 }
615 return mgr->GetComponentByName(component_name);
616 }
617
isOhosIndepCompilerEnable()618 bool OhosComponents::isOhosIndepCompilerEnable() {
619 return mgr && mgr->isOhosIndepCompilerEnable();
620 }
621