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 } else {
460 label = component->getInnerApi(innerapi_name) + tool_chain;
461 }
462
463 if (label == EMPTY_INNERAPI) {
464 *err = Err(external_dep,
465 "OHOS innerapi: (" + innerapi_name + ") not found for component (" + component_name + ").");
466 return false;
467 }
468 return true;
469 }
470
GetSubsystemName(const Value & component_name,std::string & subsystem_name,Err * err) const471 bool OhosComponentsImpl::GetSubsystemName(const Value &component_name, std::string &subsystem_name, Err *err) const
472 {
473 const OhosComponent *component = GetComponentByName(component_name.string_value());
474 if (component == nullptr) {
475 *err = Err(component_name, "OHOS component : (" + component_name.string_value() + ") not found.");
476 return false;
477 }
478
479 subsystem_name = component->subsystem();
480 return true;
481 }
482
483 /**
484 * Ohos Components Public API
485 */
486
487 OhosComponents::OhosComponents() = default;
488
LoadOhosComponents(const std::string & build_dir,const Value * enable,const Value * indep,const Value * product,Err * err)489 bool OhosComponents::LoadOhosComponents(const std::string &build_dir,
490 const Value *enable, const Value *indep, const Value *product, Err *err)
491 {
492 if (!enable) {
493 // Not enabled
494 return true;
495 }
496 if (!enable->VerifyTypeIs(Value::BOOLEAN, err)) {
497 return false;
498 }
499
500 // Disabled
501 if (!enable->boolean_value()) {
502 return true;
503 }
504
505 mgr = new OhosComponentsImpl();
506
507 if (!mgr->LoadOhosComponents(build_dir, enable, indep, product, err)) {
508 delete mgr;
509 mgr = nullptr;
510 return false;
511 }
512
513 return true;
514 }
515
isOhosComponentsLoaded() const516 bool OhosComponents::isOhosComponentsLoaded() const
517 {
518 if (mgr == nullptr) {
519 return false;
520 } else {
521 return true;
522 }
523 }
524
GetExternalDepsLabel(const Value & external_dep,std::string & label,const Label & current_toolchain,int & whole_status,Err * err) const525 bool OhosComponents::GetExternalDepsLabel(const Value &external_dep, std::string &label,
526 const Label& current_toolchain, int &whole_status, Err *err) const
527 {
528 if (!mgr) {
529 if (err) {
530 *err = Err(external_dep, "You are compiling OpenHarmony components, but \n"
531 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
532 }
533 return false;
534 }
535 return mgr->GetExternalDepsLabel(external_dep, label, current_toolchain, whole_status, err);
536 }
537
GetPrivateDepsLabel(const Value & dep,std::string & label,const Label & current_toolchain,int & whole_status,Err * err) const538 bool OhosComponents::GetPrivateDepsLabel(const Value &dep, std::string &label,
539 const Label& current_toolchain, int &whole_status, Err *err) const
540 {
541 if (!mgr) {
542 if (err) {
543 *err = Err(dep, "You are compiling OpenHarmony components, but \n"
544 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
545 }
546 return false;
547 }
548 return mgr->GetPrivateDepsLabel(dep, label, current_toolchain, whole_status, err);
549 }
550
GetSubsystemName(const Value & part_name,std::string & label,Err * err) const551 bool OhosComponents::GetSubsystemName(const Value &part_name, std::string &label, Err *err) const
552 {
553 if (!mgr) {
554 if (err) {
555 *err = Err(part_name, "You are compiling OpenHarmony components, but \n"
556 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
557 }
558 return false;
559 }
560 return mgr->GetSubsystemName(part_name, label, err);
561 }
562
GetComponentByLabel(const std::string & label) const563 const OhosComponent *OhosComponents::GetComponentByLabel(const std::string &label) const
564 {
565 if (!mgr) {
566 return nullptr;
567 }
568 return mgr->matchComponentByLabel(label.c_str());
569 }
570
LoadOhosComponentsChecker(const std::string & build_dir,const Value * support,int checkType,unsigned int ruleSwitch)571 void OhosComponents::LoadOhosComponentsChecker(const std::string &build_dir, const Value *support, int checkType,
572 unsigned int ruleSwitch)
573 {
574 if (!support) {
575 return;
576 }
577 if (!support->boolean_value()) {
578 return;
579 }
580 if (checkType > OhosComponentChecker::CheckType::INTERCEPT_ALL ||
581 checkType <= OhosComponentChecker::CheckType::NONE) {
582 InnerApiPublicInfoGenerator::Init(build_dir, 0);
583 return;
584 }
585 OhosComponentChecker::Init(build_dir, checkType, ruleSwitch);
586 InnerApiPublicInfoGenerator::Init(build_dir, checkType);
587 return;
588 }
589
LoadOhosComponentsMapping(const std::string & build_dir,const Value * support,const Value * independent)590 void OhosComponents::LoadOhosComponentsMapping(const std::string& build_dir,
591 const Value *support, const Value *independent)
592 {
593 if (!support) {
594 return;
595 }
596 if (!support->boolean_value()) {
597 return;
598 }
599
600 if (!independent || !independent->boolean_value()) {
601 return;
602 }
603
604 OhosComponentMapping::Init(build_dir);
605 return;
606 }