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
21 /**
22 * Ohos Component API
23 *
24 * Each component belongs to one subsystem.
25 * Each component has a source path.
26 * Each component has zero or more innerapis
27 */
28
29 static const std::string EMPTY_INNERAPI;
30
31 static const int PATH_PREFIX_LEN = 2;
32
33 OhosComponent::OhosComponent() = default;
34
OhosComponent(const char * name,const char * subsystem,const char * path)35 OhosComponent::OhosComponent(const char *name, const char *subsystem, const char *path)
36 {
37 name_ = std::string(name);
38 subsystem_ = std::string(subsystem);
39 if (strncmp(path, "//", PATH_PREFIX_LEN) == 0) {
40 path_ = std::string(path);
41 } else {
42 path_ = "//" + std::string(path);
43 }
44 }
45
addInnerApi(const std::string & name,const std::string & label)46 void OhosComponent::addInnerApi(const std::string &name, const std::string &label)
47 {
48 innerapi_names_[name] = label;
49 innerapi_labels_[label] = name;
50 }
51
52
getInnerApi(const std::string & innerapi) const53 const std::string &OhosComponent::getInnerApi(const std::string &innerapi) const
54 {
55 if (auto res = innerapi_names_.find(innerapi); res != innerapi_names_.end()) {
56 return res->second;
57 }
58 return EMPTY_INNERAPI;
59 }
60
isInnerApi(const std::string & label) const61 bool OhosComponent::isInnerApi(const std::string &label) const
62 {
63 if (auto res = innerapi_labels_.find(label); res != innerapi_labels_.end()) {
64 return true;
65 }
66 return false;
67 }
68
addInnerApiVisibility(const std::string & name,const std::vector<base::Value> & list)69 void OhosComponent::addInnerApiVisibility(const std::string &name, const std::vector<base::Value> &list)
70 {
71 for (const base::Value &visibility : list) {
72 innerapi_visibility_[innerapi_names_[name]].push_back(visibility.GetString());
73 }
74 }
75
getInnerApiVisibility(const std::string & label) const76 const std::vector<std::string> OhosComponent::getInnerApiVisibility(const std::string &label) const
77 {
78 if (auto res = innerapi_visibility_.find(label); res != innerapi_visibility_.end()) {
79 return res->second;
80 }
81 return {};
82 }
83
84 /**
85 * Ohos Component Implimentation API
86 */
87 OhosComponentsImpl::OhosComponentsImpl() = default;
88
ReadBuildConfigFile(const std::string & build_dir,const char * subfile,std::string & content)89 bool OhosComponentsImpl::ReadBuildConfigFile(const std::string &build_dir, const char *subfile, std::string &content)
90 {
91 std::string path = build_dir;
92 path += "/build_configs/";
93 path += subfile;
94 if (!base::ReadFileToString(base::FilePath(path), &content)) {
95 return false;
96 }
97 return true;
98 }
99
LoadComponentSubsystemAndPaths(const std::string & paths,const std::string & override_map,const std::string & subsystems,std::string & err_msg_out)100 bool OhosComponentsImpl::LoadComponentSubsystemAndPaths(const std::string &paths, const std::string &override_map,
101 const std::string &subsystems, std::string &err_msg_out)
102 {
103 const base::DictionaryValue *paths_dict;
104
105 std::unique_ptr<base::Value> paths_value = base::JSONReader::ReadAndReturnError(paths,
106 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr);
107 if (!paths_value) {
108 return false;
109 }
110 if (!paths_value->GetAsDictionary(&paths_dict)) {
111 return false;
112 }
113
114 const base::DictionaryValue *subsystems_dict;
115 std::unique_ptr<base::Value> subsystems_value = base::JSONReader::ReadAndReturnError(subsystems,
116 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr);
117 if (!subsystems_value) {
118 return false;
119 }
120 if (!subsystems_value->GetAsDictionary(&subsystems_dict)) {
121 return false;
122 }
123
124 const base::Value *subsystem;
125 for (const auto com : paths_dict->DictItems()) {
126 subsystem = subsystems_dict->FindKey(com.first);
127 if (!subsystem) {
128 continue;
129 }
130 components_[com.first] =
131 new OhosComponent(com.first.c_str(), subsystem->GetString().c_str(), com.second.GetString().c_str());
132 }
133 setupComponentsTree();
134 return true;
135 }
136
findChildByPath(const struct OhosComponentTree * current,const char * path,size_t len)137 const struct OhosComponentTree *OhosComponentsImpl::findChildByPath(const struct OhosComponentTree *current,
138 const char *path, size_t len)
139 {
140 if (current->child == nullptr) {
141 return nullptr;
142 }
143 const struct OhosComponentTree *item = current->child;
144 while (item != nullptr) {
145 if (strncmp(item->dirName, path, len) == 0) {
146 // Exactly matching
147 if (item->dirName[len] == '\0') {
148 return item;
149 }
150 }
151 item = item->next;
152 }
153
154 return nullptr;
155 }
156
matchComponentByLabel(const char * label)157 const OhosComponent *OhosComponentsImpl::matchComponentByLabel(const char *label)
158 {
159 const struct OhosComponentTree *child;
160 const struct OhosComponentTree *current = pathTree;
161
162 if (!label) {
163 return nullptr;
164 }
165 // Skip leading //
166 if (strncmp(label, "//", PATH_PREFIX_LEN) == 0) {
167 label += PATH_PREFIX_LEN;
168 }
169
170 size_t len;
171 const char *sep;
172 while (label[0] != '\0') {
173 // Get next path seperator
174 sep = strchr(label, '/');
175 if (sep) {
176 len = sep - label;
177 } else {
178 // Check if it is a label with target name
179 sep = strchr(label, ':');
180 if (sep) {
181 len = sep - label;
182 } else {
183 len = strlen(label);
184 }
185 }
186
187 // Match with children
188 child = findChildByPath(current, label, len);
189 if (child == nullptr) {
190 break;
191 }
192
193 // No children, return current matched item
194 if (child->child == nullptr) {
195 return child->component;
196 }
197
198 label += len;
199 // Finish matching if target name started
200 if (label[0] == ':') {
201 return child->component;
202 }
203
204 // Match with child again
205 current = child;
206
207 // Skip leading seperator
208 if (label[0] == '/') {
209 label += 1;
210 }
211 }
212
213 return nullptr;
214 }
215
addComponentToTree(struct OhosComponentTree * current,OhosComponent * component)216 void OhosComponentsImpl::addComponentToTree(struct OhosComponentTree *current, OhosComponent *component)
217 {
218 size_t len;
219 const char *path = component->path().c_str() + PATH_PREFIX_LEN;
220 const char *sep;
221
222 while (path[0] != '\0') {
223 sep = strchr(path, '/');
224 if (sep) {
225 len = sep - path;
226 } else {
227 len = strlen(path);
228 }
229
230 // Check if node already exists
231 struct OhosComponentTree *child = (struct OhosComponentTree *)findChildByPath(current, path, len);
232 if (!child) {
233 // Add intermediate node
234 child = new struct OhosComponentTree(path, len, nullptr);
235 child->next = current->child;
236 current->child = child;
237 }
238
239 // End of path detected, setup component pointer
240 path = path + len;
241 if (path[0] == '\0') {
242 child->component = component;
243 break;
244 }
245
246 // Continue to add next part
247 path += 1;
248 current = child;
249 }
250 }
251
setupComponentsTree()252 void OhosComponentsImpl::setupComponentsTree()
253 {
254 pathTree = new struct OhosComponentTree("//", nullptr);
255
256 std::map<std::string, OhosComponent *>::iterator it;
257 for (it = components_.begin(); it != components_.end(); it++) {
258 addComponentToTree(pathTree, it->second);
259 }
260 }
261
LoadInnerApi(const base::DictionaryValue * innerapis)262 void OhosComponentsImpl::LoadInnerApi(const base::DictionaryValue *innerapis)
263 {
264 OhosComponent *component;
265 for (const auto kv : innerapis->DictItems()) {
266 for (const auto inner : kv.second.DictItems()) {
267 component = (OhosComponent *)GetComponentByName(kv.first);
268 if (!component) {
269 break;
270 }
271 for (const auto info : inner.second.DictItems()) {
272 if (info.first == "label") {
273 component->addInnerApi(inner.first, info.second.GetString());
274 break;
275 }
276 }
277 for (const auto info : inner.second.DictItems()) {
278 if (info.first == "visibility") {
279 component->addInnerApiVisibility(inner.first, info.second.GetList());
280 break;
281 }
282 }
283 }
284 }
285 }
286
LoadOhosInnerApis_(const std::string innerapi_content,std::string & err_msg_out)287 bool OhosComponentsImpl::LoadOhosInnerApis_(const std::string innerapi_content, std::string &err_msg_out)
288 {
289 const base::DictionaryValue *innerapis_dict;
290
291 std::unique_ptr<base::Value> innerapis = base::JSONReader::ReadAndReturnError(innerapi_content,
292 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr);
293 if (!innerapis) {
294 return false;
295 }
296 if (!innerapis->GetAsDictionary(&innerapis_dict)) {
297 return false;
298 }
299 LoadInnerApi(innerapis_dict);
300 return true;
301 }
302
LoadOhosComponents(const std::string & build_dir,const Value * enable,Err * err)303 bool OhosComponentsImpl::LoadOhosComponents(const std::string &build_dir, const Value *enable, Err *err)
304 {
305 const char *paths_file = "parts_info/parts_path_info.json";
306 std::string paths_content;
307 if (!ReadBuildConfigFile(build_dir, paths_file, paths_content)) {
308 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
309 "OpenHarmony build config file (" +
310 std::string(paths_file) + ") does not exists.\n");
311 return false;
312 }
313 const char *subsystems_file = "parts_info/part_subsystem.json";
314 std::string subsystems_content;
315 if (!ReadBuildConfigFile(build_dir, subsystems_file, subsystems_content)) {
316 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
317 "OpenHarmony build config file (" +
318 std::string(subsystems_file) + ") does not exists.\n");
319 return false;
320 }
321 const char *innerapis_file = "parts_info/inner_kits_info.json";
322 std::string innerapis_content;
323 if (!ReadBuildConfigFile(build_dir, innerapis_file, innerapis_content)) {
324 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
325 "OpenHarmony build config file (" +
326 std::string(innerapis_file) + ") does not exists.\n");
327 return false;
328 }
329 const char *override_file = "component_override_map.json";
330 std::string override_map;
331 if (!ReadBuildConfigFile(build_dir, override_file, override_map)) {
332 override_map = EMPTY_INNERAPI;
333 }
334 std::string err_msg_out;
335 if (!LoadComponentSubsystemAndPaths(paths_content, override_map, subsystems_content, err_msg_out)) {
336 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
337 "OpenHarmony build config file parsing failed:\n" +
338 err_msg_out + "\n");
339 return false;
340 }
341 if (!LoadOhosInnerApis_(innerapis_content, err_msg_out)) {
342 *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but "
343 "OpenHarmony build config file " +
344 std::string(innerapis_file) + " parsing failed:\n" + err_msg_out + "\n");
345 return false;
346 }
347 return true;
348 }
349
GetComponentByName(const std::string & component_name) const350 const OhosComponent *OhosComponentsImpl::GetComponentByName(const std::string &component_name) const
351 {
352 if (auto res = components_.find(component_name); res != components_.end()) {
353 return res->second;
354 }
355 return nullptr;
356 }
357
GetExternalDepsLabel(const Value & external_dep,std::string & label,Err * err) const358 bool OhosComponentsImpl::GetExternalDepsLabel(const Value &external_dep, std::string &label, Err *err) const
359 {
360 std::string str_val = external_dep.string_value();
361 size_t sep = str_val.find(":");
362 if (sep <= 0) {
363 *err = Err(external_dep, "OHOS component external_deps format error: (" + external_dep.string_value() +
364 "),"
365 "it should be a string like \"component_name:innerapi_name\".");
366 return false;
367 }
368 std::string component_name = str_val.substr(0, sep);
369 const OhosComponent *component = GetComponentByName(component_name);
370 if (component == nullptr) {
371 *err = Err(external_dep, "OHOS component : (" + component_name + ") not found.");
372 return false;
373 }
374 std::string innerapi_name = str_val.substr(sep + 1);
375 label = component->getInnerApi(innerapi_name);
376 if (label == EMPTY_INNERAPI) {
377 *err = Err(external_dep,
378 "OHOS innerapi: (" + innerapi_name + ") not found for component (" + component_name + ").");
379 return false;
380 }
381 return true;
382 }
383
GetSubsystemName(const Value & component_name,std::string & subsystem_name,Err * err) const384 bool OhosComponentsImpl::GetSubsystemName(const Value &component_name, std::string &subsystem_name, Err *err) const
385 {
386 const OhosComponent *component = GetComponentByName(component_name.string_value());
387 if (component == nullptr) {
388 *err = Err(component_name, "OHOS component : (" + component_name.string_value() + ") not found.");
389 return false;
390 }
391
392 subsystem_name = component->subsystem();
393 return true;
394 }
395
396 /**
397 * Ohos Components Public API
398 */
399
400 OhosComponents::OhosComponents() = default;
401
LoadOhosComponents(const std::string & build_dir,const Value * enable,Err * err)402 bool OhosComponents::LoadOhosComponents(const std::string &build_dir, const Value *enable, Err *err)
403 {
404 if (!enable) {
405 // Not enabled
406 return true;
407 }
408 if (!enable->VerifyTypeIs(Value::BOOLEAN, err)) {
409 return false;
410 }
411
412 // Disabled
413 if (!enable->boolean_value()) {
414 return true;
415 }
416
417 mgr = new OhosComponentsImpl();
418
419 if (!mgr->LoadOhosComponents(build_dir, enable, err)) {
420 delete mgr;
421 mgr = nullptr;
422 return false;
423 }
424
425 return true;
426 }
427
isOhosComponentsLoaded() const428 bool OhosComponents::isOhosComponentsLoaded() const
429 {
430 if (mgr == nullptr) {
431 return false;
432 } else {
433 return true;
434 }
435 }
436
GetExternalDepsLabel(const Value & external_dep,std::string & label,Err * err) const437 bool OhosComponents::GetExternalDepsLabel(const Value &external_dep, std::string &label, Err *err) const
438 {
439 if (!mgr) {
440 if (err) {
441 *err = Err(external_dep, "You are compiling OpenHarmony components, but \n"
442 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
443 }
444 return false;
445 }
446 return mgr->GetExternalDepsLabel(external_dep, label, err);
447 }
448
GetSubsystemName(const Value & part_name,std::string & label,Err * err) const449 bool OhosComponents::GetSubsystemName(const Value &part_name, std::string &label, Err *err) const
450 {
451 if (!mgr) {
452 if (err) {
453 *err = Err(part_name, "You are compiling OpenHarmony components, but \n"
454 "\"ohos_components_support\" is not enabled or build_configs files are invalid.");
455 }
456 return false;
457 }
458 return mgr->GetSubsystemName(part_name, label, err);
459 }
460
GetComponentByLabel(const std::string & label) const461 const OhosComponent *OhosComponents::GetComponentByLabel(const std::string &label) const
462 {
463 if (!mgr) {
464 return nullptr;
465 }
466 return mgr->matchComponentByLabel(label.c_str());
467 }
468
LoadOhosComponentsChecker(const std::string & build_dir,const Value * support,int checkType)469 void OhosComponents::LoadOhosComponentsChecker(const std::string &build_dir, const Value *support, int checkType)
470 {
471 if (!support) {
472 return;
473 }
474 if (!support->boolean_value()) {
475 return;
476 }
477 if (checkType > OhosComponentChecker::CheckType::INTERCEPT_ALL ||
478 checkType <= OhosComponentChecker::CheckType::NONE) {
479 InnerApiPublicInfoGenerator::Init(build_dir, 0);
480 return;
481 }
482 OhosComponentChecker::Init(build_dir, checkType);
483 InnerApiPublicInfoGenerator::Init(build_dir, checkType);
484 return;
485 }
486