diff --git a/build/gen.py b/build/gen.py index 8b5795bf525a2ad004582b1988aefa02ea16c722..60142964616b6412a6449b412499b47d41b8d8f3 100755 --- a/build/gen.py +++ b/build/gen.py @@ -620,6 +620,7 @@ def WriteGNNinja(path, platform, host, options, args_list): 'src/gn/header_checker.cc', 'src/gn/import_manager.cc', 'src/gn/inherited_libraries.cc', + "src/gn/innerapis_publicinfo_generator.cc", 'src/gn/input_conversion.cc', 'src/gn/input_file.cc', 'src/gn/input_file_manager.cc', @@ -648,6 +649,9 @@ def WriteGNNinja(path, platform, host, options, args_list): 'src/gn/ninja_tools.cc', 'src/gn/ninja_utils.cc', 'src/gn/ninja_writer.cc', + 'src/gn/ohos_components.cc', + "src/gn/ohos_components_checker.cc", + 'src/gn/ohos_variables.cc', 'src/gn/operators.cc', 'src/gn/output_conversion.cc', 'src/gn/output_file.cc', @@ -771,6 +775,7 @@ def WriteGNNinja(path, platform, host, options, args_list): 'src/gn/ninja_target_command_util_unittest.cc', 'src/gn/ninja_target_writer_unittest.cc', 'src/gn/ninja_toolchain_writer_unittest.cc', + 'src/gn/ohos_components_unittest.cc', 'src/gn/operators_unittest.cc', 'src/gn/output_conversion_unittest.cc', 'src/gn/parse_tree_unittest.cc', diff --git a/docs/reference.md b/docs/reference.md index 51dcecc38df370760e0e91821b586f2fd7bf434b..eb9d74beacb778749017f9b98eb95dca0c299f95 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -120,6 +120,7 @@ * [defines: [string list] C preprocessor defines.](#var_defines) * [depfile: [string] File name for input dependencies for actions.](#var_depfile) * [deps: [label list] Private linked dependencies.](#var_deps) + * [external_deps: [label list] Declare external dependencies for OpenHarmony component.](#var_external_deps) * [externs: [scope] Set of Rust crate-dependency pairs.](#var_externs) * [framework_dirs: [directory list] Additional framework search directories.](#var_framework_dirs) * [frameworks: [name list] Name of frameworks that must be linked.](#var_frameworks) @@ -147,6 +148,7 @@ * [public: [file list] Declare public header files for a target.](#var_public) * [public_configs: [label list] Configs applied to dependents.](#var_public_configs) * [public_deps: [label list] Declare public dependencies.](#var_public_deps) + * [public_external_deps: [label list] Declare public external dependencies for OpenHarmony component.](#var_public_external_deps) * [rebase: [boolean] Rebase collected metadata as files.](#var_rebase) * [response_file_contents: [string list] Contents of .rsp file for actions.](#var_response_file_contents) * [script: [file name] Script file for actions.](#var_script) @@ -5463,6 +5465,26 @@ See also "public_deps". ``` +### **external_deps**: Declare external dependencies for OpenHarmony component. + +``` + External dependencies are like private dependencies (see "gn help deps") but + expressed in the form of: component_name:innerapi_name. + * component and innerapi are defined by OpenHarmony bundle.json. + With external_deps, deps can be added without absolute path. + + This variable is enabled by setting "ohos_components_support" in .gn file (see "gn help dotfile"). +``` + +#### **Example** + +``` + # This target "a" can include libinitapi from "init" component + executable("a") { + deps = [ ":b" ] + external_deps = [ "init:libinitapi" ] + } +``` ### **externs**: [scope] Set of Rust crate-dependency pairs. ``` @@ -6351,6 +6373,28 @@ public_deps = [ ":c" ] } ``` +### **public_external_deps**: Declare public external dependencies for OpenHarmony component. + +``` + Public external dependencies (public_external_deps) are like external_deps but additionally express that + the current target exposes the listed external_deps as part of its public API. + + This variable is enabled by setting "ohos_components_support" in .gn file (see "gn help dotfile"). +``` + +#### **Example** + +``` + # Target "a" will include libinitapi from "init" component by deps "b": + executable("a") { + deps = [ ":b" ] + } + + shared_library("b") { + deps = [ ":b" ] + public_external_deps = [ "init:libinitapi" ] + } +``` ### **rebase**: Rebase collected metadata as files. ``` @@ -6853,6 +6897,21 @@ When set specifies the minimum required version of Ninja. The default required version is 1.7.2. Specifying a higher version might enable the use of some of newer features that can make the build more efficient. + + ohos_components_support [optional] + This parameter enable support for OpenHarmony components. + When enabled, gn will load components information from "build_configs/" + directory in the root_out_directory. + + The following files will be loaded: + out/build_configs/parts_info/inner_kits_info.json (required): + Required InnerAPI information file for each OHOS component. + out/build_configs/component_override_map.json (optional): + Optional overrided component maps file. + + For OpenHarmony system build, this value must be enabled to support + external_deps (see "gn help external_deps") and public_external_deps + (see "gn help public_external_deps"). ``` #### **Example .gn file contents** diff --git a/src/gn/build_settings.cc b/src/gn/build_settings.cc index 22c11451f5a48323e39fb6c368613813b6e275e7..ae908bb04048189b7f68dec2fdc955f4f272b261 100644 --- a/src/gn/build_settings.cc +++ b/src/gn/build_settings.cc @@ -8,6 +8,7 @@ #include "base/files/file_util.h" #include "gn/filesystem_utils.h" +#include "gn/ohos_components.h" BuildSettings::BuildSettings() = default; @@ -74,3 +75,33 @@ void BuildSettings::ItemDefined(std::unique_ptr item) const { if (item_defined_callback_) item_defined_callback_(std::move(item)); } + +void BuildSettings::SetOhosComponentsInfo(OhosComponents *ohos_components) +{ + ohos_components_ = ohos_components; +} + +bool BuildSettings::GetExternalDepsLabel(const Value& external_dep, std::string& label, Err* err) const +{ + if (ohos_components_ == nullptr) { + *err = Err(external_dep, "You are using OpenHarmony external_deps, but no components information loaded."); + return false; + } + return ohos_components_->GetExternalDepsLabel(external_dep, label, err); +} + +bool BuildSettings::is_ohos_components_enabled() const +{ + if (ohos_components_ != nullptr) { + return true; + } + return false; +} + +const OhosComponent *BuildSettings::GetOhosComponent(const std::string& label) const +{ + if (ohos_components_ == nullptr) { + return nullptr; + } + return ohos_components_->GetComponentByLabel(label); +} diff --git a/src/gn/build_settings.h b/src/gn/build_settings.h index 6c925e985e0f366f49574b16333ce9bd0167f552..d94b1231e785e25d735ac7c0e9311dd6e36565bb 100644 --- a/src/gn/build_settings.h +++ b/src/gn/build_settings.h @@ -20,6 +20,8 @@ #include "gn/version.h" class Item; +class OhosComponent; +class OhosComponents; // Settings for one build, which is one toplevel output directory. There // may be multiple Settings objects that refer to this, one for each toolchain. @@ -55,6 +57,12 @@ class BuildSettings { base::FilePath python_path() const { return python_path_; } void set_python_path(const base::FilePath& p) { python_path_ = p; } + // OpenHarmony components manager. + void SetOhosComponentsInfo(OhosComponents *ohos_components); + bool GetExternalDepsLabel(const Value& external_dep, std::string& label, Err* err) const; + bool is_ohos_components_enabled() const; + const OhosComponent *GetOhosComponent(const std::string& label) const; + // Required Ninja version. const Version& ninja_required_version() const { return ninja_required_version_; @@ -141,6 +149,8 @@ class BuildSettings { base::FilePath secondary_source_path_; base::FilePath python_path_; + OhosComponents *ohos_components_ = nullptr; + // See 40045b9 for the reason behind using 1.7.2 as the default version. Version ninja_required_version_{1, 7, 2}; bool no_stamp_files_ = false; diff --git a/src/gn/copy_target_generator.cc b/src/gn/copy_target_generator.cc index a0dc9242c964780715f54e14e7efb13d58aea8c0..8f25479f7200dd55faa5f30404ed0db889581175 100644 --- a/src/gn/copy_target_generator.cc +++ b/src/gn/copy_target_generator.cc @@ -6,6 +6,7 @@ #include "gn/build_settings.h" #include "gn/filesystem_utils.h" +#include "gn/ohos_variables.h" #include "gn/parse_tree.h" #include "gn/scope.h" #include "gn/value.h" @@ -18,6 +19,17 @@ CopyTargetGenerator::CopyTargetGenerator(Target* target, CopyTargetGenerator::~CopyTargetGenerator() = default; +bool CopyTargetGenerator::FillCopyLinkableFile() +{ + const Value* value = scope_->GetValue(variables::kCopyLinkableFile, true); + if (!value) + return true; + if (!value->VerifyTypeIs(Value::BOOLEAN, err_)) + return false; + target_->set_copy_linkable_file(value->boolean_value()); + return true; +} + void CopyTargetGenerator::DoRun() { target_->set_output_type(Target::COPY_FILES); @@ -41,4 +53,7 @@ void CopyTargetGenerator::DoRun() { "source_expansion\")."); return; } + + if (!FillCopyLinkableFile()) + return; } diff --git a/src/gn/copy_target_generator.h b/src/gn/copy_target_generator.h index c3098f52fbe39957637a9cc8a7e30ed3f5746b46..7b70b7415590a3bab2e70b5209cb363e224859c9 100644 --- a/src/gn/copy_target_generator.h +++ b/src/gn/copy_target_generator.h @@ -6,6 +6,7 @@ #define TOOLS_GN_COPY_TARGET_GENERATOR_H_ #include "gn/target_generator.h" +#include "gn/ohos_variables.h" // Populates a Target with the values from a copy rule. class CopyTargetGenerator : public TargetGenerator { @@ -22,6 +23,8 @@ class CopyTargetGenerator : public TargetGenerator { private: CopyTargetGenerator(const CopyTargetGenerator&) = delete; CopyTargetGenerator& operator=(const CopyTargetGenerator&) = delete; + + bool FillCopyLinkableFile(); }; #endif // TOOLS_GN_COPY_TARGET_GENERATOR_H_ diff --git a/src/gn/functions.cc b/src/gn/functions.cc index 6294f79b712a6b985ad3e4ca98cf25ac41f92b04..79aad15fe1ed4946c64aef382975d6e081cc7920 100644 --- a/src/gn/functions.cc +++ b/src/gn/functions.cc @@ -16,6 +16,7 @@ #include "gn/config_values_generator.h" #include "gn/err.h" #include "gn/input_file.h" +#include "gn/ohos_components_checker.h" #include "gn/parse_node_value_adapter.h" #include "gn/parse_tree.h" #include "gn/pool.h" @@ -667,6 +668,11 @@ Value RunImport(Scope* scope, scope->settings()->import_manager().DoImport(import_file, function, scope, err); } + const OhosComponentChecker *checker = OhosComponentChecker::getInstance(); + if (checker != nullptr) { + checker->CheckImportOther(function, scope->settings()->build_settings(), + input_dir.value(), import_file.value(), err); + } return Value(); } diff --git a/src/gn/functions_target.cc b/src/gn/functions_target.cc index 74f53e915f62bfd5e5789917c9abc5e900f9cfd3..8bf3cd5e2b78f1fec9a2f88a554f349c575469a0 100644 --- a/src/gn/functions_target.cc +++ b/src/gn/functions_target.cc @@ -543,6 +543,10 @@ Examples # names in the gen dir. This will just copy each file. outputs = [ "$target_gen_dir/{{source_file_part}}" ] } + +Variables + + copy_linkable_file )"; Value RunCopy(const FunctionCallNode* function, diff --git a/src/gn/innerapis_publicinfo_generator.cc b/src/gn/innerapis_publicinfo_generator.cc new file mode 100644 index 0000000000000000000000000000000000000000..3f56c87863ac4bec2b572b6e382655b99c532ee0 --- /dev/null +++ b/src/gn/innerapis_publicinfo_generator.cc @@ -0,0 +1,359 @@ +// Copyright 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/innerapis_publicinfo_generator.h" + +#include +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/values.h" +#include "gn/build_settings.h" +#include "gn/config.h" +#include "gn/filesystem_utils.h" +#include "gn/functions.h" +#include "gn/ohos_components.h" +#include "gn/ohos_components_checker.h" +#include "gn/parse_tree.h" +#include "gn/settings.h" +#include "gn/substitution_writer.h" +#include "gn/target.h" +#include "gn/value.h" + +namespace fs = std::filesystem; + +InnerApiPublicInfoGenerator *InnerApiPublicInfoGenerator::instance_ = nullptr; + +static bool StartWith(const std::string &str, const std::string prefix) +{ + return (str.rfind(prefix, 0) == 0); +} + +static bool IsFileExists(const std::string &path) +{ + if (access(path.c_str(), F_OK) == 0) { + return true; + } + return false; +} + +static void CreateDirectory(const std::string &path) +{ + int status = mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH); + if (status == -1 && errno != EEXIST) { + return; + } + size_t found = path.find('/'); + while (found != std::string::npos) { + std::string subPath = path.substr(0, found + 1); + struct stat info; + if (!stat(subPath.c_str(), &info)) { + if ((info.st_mode & S_IFDIR) == 0) { + break; + } + } else { + CreateDirectory(subPath); + } + found = path.find('/', found + 1); + } +} + +static std::string GetOutName(const Scope *scope, std::string targetName, const std::string type) +{ + std::string outputName = ""; + std::string extension = ""; + const Value *outputNameValue = scope->GetValue("output_name"); + const Value *extensionValue = scope->GetValue("output_extension"); + if (outputNameValue != nullptr) { + outputName = outputNameValue->string_value(); + } + if (extensionValue != nullptr) { + extension = extensionValue->string_value(); + } + + if (outputName == "") { + outputName = targetName; + } + if (type == "shared_library") { + if (extension == "") { + extension = ".z.so"; + } else { + extension = "." + extension; + } + if (!StartWith(outputName, "lib")) { + outputName = "lib" + outputName; + } + } else if (type == "static_library") { + extension = ".a"; + if (!StartWith(outputName, "lib")) { + outputName = "lib" + outputName; + } + } else if (type == "rust_library") { + if (extension == "") { + extension = ".dylib.so"; + } else { + extension = "." + extension; + } + if (!StartWith(outputName, "lib")) { + outputName = "lib" + outputName; + } + } + return outputName + extension; +} + +static bool TraverIncludeDirs(const OhosComponentChecker *checker, const Target *target, const Scope *scope, + const std::string label, Err *err) +{ + const Value *includes = scope->GetValue("include_dirs"); + if (includes != nullptr) { + const std::vector &includes_list = includes->list_value(); + for (size_t i = 0; i < includes_list.size(); i++) { + SourceDir real_dir = scope->GetSourceDir().ResolveRelativeDir(includes_list[i], err, + scope->settings()->build_settings()->root_path_utf8()); + if (!checker->CheckIncludesAbsoluteDepsOther(target, label, real_dir.value(), err)) { + return false; + } + } + } + return true; +} + +static bool CheckIncludes(const OhosComponentChecker *checker, const std::string dir, bool isPublic, + const PublicConfigInfoParams ¶ms) +{ + const Target *target = params.target; + const std::string label = params.label; + Err *err = params.err; + if (isPublic) { + if (checker != nullptr) { + if (!checker->CheckInnerApiIncludesOverRange(target, label, dir, err)) { + return false; + } + } + } + if (checker != nullptr) { + if (!checker->CheckIncludesAbsoluteDepsOther(target, label, dir, err)) { + return false; + } + } + return true; +} + +static std::string GetIncludeDirsInfo(const Config *config, const OhosComponentChecker *checker, bool isPublic, + const PublicConfigInfoParams ¶ms) +{ + std::string info = ",\n \"include_dirs\": [\n "; + const std::vector dirs = config->own_values().include_dirs(); + bool first = true; + for (const SourceDir &dir : dirs) { + if (!first) { + info += ",\n "; + } + first = false; + info += "\"" + dir.value() + "\""; + if (!CheckIncludes(checker, dir.value(), isPublic, params)) { + return ""; + } + } + info += "\n ]\n"; + return info; +} + +static std::string GetPublicConfigInfo(const PublicConfigInfoParams ¶ms, Scope *scope, + const UniqueVector &configs, const OhosComponentChecker *checker, bool isPublic) +{ + const Target *target = params.target; + const std::string label = params.label; + Err *err = params.err; + Scope::ItemVector *collector = scope->GetItemCollector(); + std::string info = "[{"; + bool firstConfig = true; + for (const auto &config : configs) { + if (!firstConfig) { + info += ", {"; + } + firstConfig = false; + info += "\n \"label\": \"" + config.label.GetUserVisibleName(false) + "\""; + for (auto &item : *collector) { + if (item->label() != config.label) + continue; + Config *as_config = item->AsConfig(); + if (!as_config) { + continue; + } + PublicConfigInfoParams params = { target, label, err }; + info += GetIncludeDirsInfo(as_config, checker, isPublic, params); + } + info += " }"; + } + info += "]"; + return info; +} + +std::string GetPublicConfigsInfo(const Target *target, const std::string &labelString, Scope *scope, + const OhosComponentChecker *checker, Err *err) +{ + std::string info = ""; + const UniqueVector configs = target->public_configs(); + if (configs.size() > 0) { + info += ",\n \"public_configs\": "; + PublicConfigInfoParams params = { target, labelString, err }; + std::string tmp = GetPublicConfigInfo(params, scope, configs, checker, true); + if (tmp == "") { + return ""; + } + info += tmp; + } + return info; +} + +std::string GetAllDependentConfigsInfo(const Target *target, const std::string &labelString, Scope *scope, + const OhosComponentChecker *checker, Err *err) +{ + std::string info = ""; + const UniqueVector all_configs = target->all_dependent_configs(); + if (all_configs.size() > 0) { + info += ",\n \"all_dependent_configs\": "; + PublicConfigInfoParams params = { target, labelString, err }; + std::string tmp = GetPublicConfigInfo(params, scope, all_configs, checker, true); + if (tmp == "") { + return ""; + } + info += tmp; + if (checker != nullptr) { + if (!checker->CheckAllDepsConfigs(target, labelString, err)) { + return ""; + } + } + } + return info; +} + +std::string GetPrivateConfigsInfo(const Target *target, const std::string &labelString, Scope *scope, + const OhosComponentChecker *checker, Err *err) +{ + std::string info = ""; + const UniqueVector private_configs = target->configs(); + if (private_configs.size() > 0) { + PublicConfigInfoParams params = { target, labelString, err }; + std::string tmp = GetPublicConfigInfo(params, scope, private_configs, checker, false); + if (tmp == "") { + return ""; + } + } + return info; +} + + +std::string GetPublicHeadersInfo(const Target *target) +{ + std::string info = ""; + const std::vector &headers = target->public_headers(); + if (headers.size() > 0) { + info += ",\n \"public\": [\n "; + bool first = true; + for (const auto &header : headers) { + if (!first) { + info += ",\n "; + } + first = false; + info += "\"" + header.value() + "\""; + } + info += " ]"; + } + return info; +} + +std::string GetPublicDepsInfo(const Target *target, const std::string &labelString, + const OhosComponentChecker *checker, Err *err) +{ + std::string info = ""; + const LabelTargetVector deps = target->public_deps(); + if (deps.size() > 0) { + info += ",\n \"public_deps\": [\n "; + bool first = true; + for (const auto &dep : deps) { + if (!first) { + info += ",\n "; + } + first = false; + std::string dep_str = dep.label.GetUserVisibleName(false); + info += "\"" + dep_str + "\""; + if (checker == nullptr) { + continue; + } + if (!checker->CheckInnerApiPublicDepsInner(target, labelString, dep_str, err)) { + return ""; + } + } + info += "\n ]"; + } + return info; +} + +std::string GetOutNameAndTypeInfo(const Scope *scope, const std::string &targetName, const std::string &type) +{ + std::string info = ""; + const std::string name = GetOutName(scope, targetName, type); + info += ",\n \"out_name\":\"" + name + "\""; + info += ",\n \"type\":\"" + type + "\""; + info += "\n}\n"; + return info; +} + +void InnerApiPublicInfoGenerator::GeneratedInnerapiPublicInfo(Target *target, Label label, Scope *scope, + const std::string type, Err *err) +{ + if (target == nullptr || (ignoreTest_ && target->testonly())) { + return; + } + const OhosComponentChecker *checker = OhosComponentChecker::getInstance(); + + std::string labelString = label.GetUserVisibleName(false); + std::string info = "{\n"; + + info += " \"label\": \"" + labelString + "\""; + info += GetPublicConfigsInfo(target, labelString, scope, checker, err); + info += GetAllDependentConfigsInfo(target, labelString, scope, checker, err); + info += GetPrivateConfigsInfo(target, labelString, scope, checker, err); + + if (checker != nullptr) { + if (!TraverIncludeDirs(checker, target, scope, labelString, err)) { + return; + } + } + + if (target->all_headers_public()) { + info += ",\n \"public\": [ \"*\" ]"; + } else { + info += GetPublicHeadersInfo(target); + } + + info += GetPublicDepsInfo(target, labelString, checker, err); + + const OhosComponent *component = target->ohos_component(); + if (target->testonly() || component == nullptr || !component->isInnerApi(labelString)) { + return; + } + int pos = labelString.find(":"); + std::string targetName = labelString.substr(pos + 1, labelString.length() - 1); + info += GetOutNameAndTypeInfo(scope, targetName, type); + + const std::string dir = build_dir_ + "/" + component->subsystem() + "/" + component->name() + "/publicinfo"; + fs::path path(dir); + fs::create_directories(path); + std::ofstream publicfile; + const std::string json_path = dir + "/" + targetName + ".json"; + if (IsFileExists(json_path)) { + return; + } + publicfile.open(json_path, std::ios::out); + publicfile << info; + publicfile.close(); + return; +} diff --git a/src/gn/innerapis_publicinfo_generator.h b/src/gn/innerapis_publicinfo_generator.h new file mode 100644 index 0000000000000000000000000000000000000000..37b7dd7de811e9e48d3af22b1d93dfdd2602398c --- /dev/null +++ b/src/gn/innerapis_publicinfo_generator.h @@ -0,0 +1,61 @@ +// Copyright 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INNERAPIS_PUBLICINFO_GENERATOR_H_ +#define INNERAPIS_PUBLICINFO_GENERATOR_H_ + +#include + +#include "gn/build_settings.h" +#include "gn/config.h" +#include "gn/functions.h" +#include "gn/ohos_components_checker.h" +#include "gn/parse_tree.h" +#include "gn/settings.h" +#include "gn/substitution_writer.h" +#include "gn/target.h" +#include "gn/value.h" + +struct PublicConfigInfoParams { + const Target *target; + std::string label; + Err *err; +}; + +class InnerApiPublicInfoGenerator { +public: + void GeneratedInnerapiPublicInfo(Target *target, Label label, Scope *scope, const std::string type, Err *err); + + static InnerApiPublicInfoGenerator *getInstance() + { + return instance_; + } + + static void Init(const std::string &build_dir, int checkType) + { + if (instance_ != nullptr) { + return; + } + instance_ = new InnerApiPublicInfoGenerator(build_dir, checkType); + } + +private: + bool ignoreTest_ = true; + std::string build_dir_; + int checkType_ = OhosComponentChecker::CheckType::NONE; + static InnerApiPublicInfoGenerator *instance_; + InnerApiPublicInfoGenerator(const std::string &build_dir, int checkType) + { + checkType_ = checkType; + build_dir_ = build_dir; + if (checkType == OhosComponentChecker::CheckType::SCAN_ALL || + checkType == OhosComponentChecker::CheckType::INTERCEPT_ALL) { + ignoreTest_ = false; + } + } + InnerApiPublicInfoGenerator() {} + InnerApiPublicInfoGenerator &operator = (const InnerApiPublicInfoGenerator &) = delete; +}; + +#endif // INNERAPIS_PUBLICINFO_GENERATOR_H_ diff --git a/src/gn/item.cc b/src/gn/item.cc index 3a3a166b36c606665cd7f6bf48facd2a0aeef1cd..bedb1da2bb93feb03f3c2d8823b20adb7b6aba96 100644 --- a/src/gn/item.cc +++ b/src/gn/item.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "gn/settings.h" +#include "gn/build_settings.h" Item::Item(const Settings* settings, const Label& label, @@ -13,7 +14,9 @@ Item::Item(const Settings* settings, : settings_(settings), label_(label), build_dependency_files_(build_dependency_files), - defined_from_(nullptr) {} + defined_from_(nullptr) { + component = settings->build_settings()->GetOhosComponent(label.GetUserVisibleName(false)); + } Item::~Item() = default; diff --git a/src/gn/item.h b/src/gn/item.h index dd1a60662fbf1376025c50946693155313c81541..efa034676c92c95fdb26b8fd575a33b29a7e2eb6 100644 --- a/src/gn/item.h +++ b/src/gn/item.h @@ -13,6 +13,7 @@ #include "gn/visibility.h" class Config; +class OhosComponent; class ParseNode; class Pool; class Settings; @@ -71,6 +72,8 @@ class Item { // returns false on failure. virtual bool OnResolved(Err* err); + const OhosComponent *ohos_component() const { return component; }; + private: bool CheckTestonly(Err* err) const; @@ -81,6 +84,8 @@ class Item { bool testonly_ = false; Visibility visibility_; + + const OhosComponent *component; }; #endif // TOOLS_GN_ITEM_H_ diff --git a/src/gn/label_ptr.h b/src/gn/label_ptr.h index cf30772c042ca0f58de6a323729d97004dbf7715..451acade4c927187ccad75cf25973be65bc40387 100644 --- a/src/gn/label_ptr.h +++ b/src/gn/label_ptr.h @@ -35,6 +35,7 @@ struct LabelPtrPair { ~LabelPtrPair() = default; Label label; + bool is_external_deps; const T* ptr = nullptr; // The origin of this dependency. This will be null for internally generated diff --git a/src/gn/ninja_binary_target_writer.cc b/src/gn/ninja_binary_target_writer.cc index e3e36e56e0a1de5fe7de116724aa5ec8ab406fb2..4f9385966bb4d3de412653f1dc86f024de2ce7fa 100644 --- a/src/gn/ninja_binary_target_writer.cc +++ b/src/gn/ninja_binary_target_writer.cc @@ -159,7 +159,10 @@ void NinjaBinaryTargetWriter::ClassifyDependency( if (can_link_libs && dep->builds_swift_module()) classified_deps->swiftmodule_deps.push_back(dep); - if (target_->source_types_used().RustSourceUsed() && + if (dep->output_type() == Target::COPY_FILES && + dep->IsLinkable()) { + classified_deps->linkable_deps.push_back(dep); + } else if (target_->source_types_used().RustSourceUsed() && (target_->output_type() == Target::RUST_LIBRARY || target_->output_type() == Target::STATIC_LIBRARY) && dep->IsLinkable()) { diff --git a/src/gn/ohos_components.cc b/src/gn/ohos_components.cc new file mode 100644 index 0000000000000000000000000000000000000000..ace5ad38acb48957dfc1783a15eac8e9566c9ec4 --- /dev/null +++ b/src/gn/ohos_components.cc @@ -0,0 +1,485 @@ +// Copyright (c) 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "gn/ohos_components.h" + +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/values.h" + +#include "gn/err.h" +#include "gn/filesystem_utils.h" +#include "gn/innerapis_publicinfo_generator.h" +#include "gn/ohos_components_checker.h" +#include "gn/ohos_components_impl.h" + +/** + * Ohos Component API + * + * Each component belongs to one subsystem. + * Each component has a source path. + * Each component has zero or more innerapis + */ + +static const std::string EMPTY_INNERAPI; + +static const int PATH_PREFIX_LEN = 2; + +OhosComponent::OhosComponent() = default; + +OhosComponent::OhosComponent(const char *name, const char *subsystem, const char *path) +{ + name_ = std::string(name); + subsystem_ = std::string(subsystem); + if (strncmp(path, "//", PATH_PREFIX_LEN) == 0) { + path_ = std::string(path); + } else { + path_ = "//" + std::string(path); + } +} + +void OhosComponent::addInnerApi(const std::string &name, const std::string &label) +{ + innerapi_names_[name] = label; + innerapi_labels_[label] = name; +} + + +const std::string &OhosComponent::getInnerApi(const std::string &innerapi) const +{ + if (auto res = innerapi_names_.find(innerapi); res != innerapi_names_.end()) { + return res->second; + } + return EMPTY_INNERAPI; +} + +bool OhosComponent::isInnerApi(const std::string &label) const +{ + if (auto res = innerapi_labels_.find(label); res != innerapi_labels_.end()) { + return true; + } + return false; +} + +void OhosComponent::addInnerApiVisibility(const std::string &name, const std::vector &list) +{ + for (const base::Value &visibility : list) { + innerapi_visibility_[innerapi_names_[name]].push_back(visibility.GetString()); + } +} + +const std::vector OhosComponent::getInnerApiVisibility(const std::string &label) const +{ + if (auto res = innerapi_visibility_.find(label); res != innerapi_visibility_.end()) { + return res->second; + } + return {}; +} + +/** + * Ohos Component Implimentation API + */ +OhosComponentsImpl::OhosComponentsImpl() = default; + +bool OhosComponentsImpl::ReadBuildConfigFile(const std::string &build_dir, const char *subfile, std::string &content) +{ + std::string path = build_dir; + path += "/build_configs/"; + path += subfile; + if (!base::ReadFileToString(base::FilePath(path), &content)) { + return false; + } + return true; +} + +bool OhosComponentsImpl::LoadComponentSubsystemAndPaths(const std::string &paths, const std::string &override_map, + const std::string &subsystems, std::string &err_msg_out) +{ + const base::DictionaryValue *paths_dict; + + std::unique_ptr paths_value = base::JSONReader::ReadAndReturnError(paths, + base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr); + if (!paths_value) { + return false; + } + if (!paths_value->GetAsDictionary(&paths_dict)) { + return false; + } + + const base::DictionaryValue *subsystems_dict; + std::unique_ptr subsystems_value = base::JSONReader::ReadAndReturnError(subsystems, + base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr); + if (!subsystems_value) { + return false; + } + if (!subsystems_value->GetAsDictionary(&subsystems_dict)) { + return false; + } + + const base::Value *subsystem; + for (const auto com : paths_dict->DictItems()) { + subsystem = subsystems_dict->FindKey(com.first); + if (!subsystem) { + continue; + } + components_[com.first] = + new OhosComponent(com.first.c_str(), subsystem->GetString().c_str(), com.second.GetString().c_str()); + } + setupComponentsTree(); + return true; +} + +const struct OhosComponentTree *OhosComponentsImpl::findChildByPath(const struct OhosComponentTree *current, + const char *path, size_t len) +{ + if (current->child == nullptr) { + return nullptr; + } + const struct OhosComponentTree *item = current->child; + while (item != nullptr) { + if (strncmp(item->dirName, path, len) == 0) { + // Exactly matching + if (item->dirName[len] == '\0') { + return item; + } + } + item = item->next; + } + + return nullptr; +} + +const OhosComponent *OhosComponentsImpl::matchComponentByLabel(const char *label) +{ + const struct OhosComponentTree *child; + const struct OhosComponentTree *current = pathTree; + + if (!label) { + return nullptr; + } + // Skip leading // + if (strncmp(label, "//", PATH_PREFIX_LEN) == 0) { + label += PATH_PREFIX_LEN; + } + + size_t len; + const char *sep; + while (label[0] != '\0') { + // Get next path seperator + sep = strchr(label, '/'); + if (sep) { + len = sep - label; + } else { + // Check if it is a label with target name + sep = strchr(label, ':'); + if (sep) { + len = sep - label; + } else { + len = strlen(label); + } + } + + // Match with children + child = findChildByPath(current, label, len); + if (child == nullptr) { + break; + } + + // No children, return current matched item + if (child->child == nullptr) { + return child->component; + } + + label += len; + // Finish matching if target name started + if (label[0] == ':') { + return child->component; + } + + // Match with child again + current = child; + + // Skip leading seperator + if (label[0] == '/') { + label += 1; + } + } + + return nullptr; +} + +void OhosComponentsImpl::addComponentToTree(struct OhosComponentTree *current, OhosComponent *component) +{ + size_t len; + const char *path = component->path().c_str() + PATH_PREFIX_LEN; + const char *sep; + + while (path[0] != '\0') { + sep = strchr(path, '/'); + if (sep) { + len = sep - path; + } else { + len = strlen(path); + } + + // Check if node already exists + struct OhosComponentTree *child = (struct OhosComponentTree *)findChildByPath(current, path, len); + if (!child) { + // Add intermediate node + child = new struct OhosComponentTree(path, len, nullptr); + child->next = current->child; + current->child = child; + } + + // End of path detected, setup component pointer + path = path + len; + if (path[0] == '\0') { + child->component = component; + break; + } + + // Continue to add next part + path += 1; + current = child; + } +} + +void OhosComponentsImpl::setupComponentsTree() +{ + pathTree = new struct OhosComponentTree("//", nullptr); + + std::map::iterator it; + for (it = components_.begin(); it != components_.end(); it++) { + addComponentToTree(pathTree, it->second); + } +} + +void OhosComponentsImpl::LoadInnerApi(const base::DictionaryValue *innerapis) +{ + OhosComponent *component; + for (const auto kv : innerapis->DictItems()) { + for (const auto inner : kv.second.DictItems()) { + component = (OhosComponent *)GetComponentByName(kv.first); + if (!component) { + break; + } + for (const auto info : inner.second.DictItems()) { + if (info.first == "label") { + component->addInnerApi(inner.first, info.second.GetString()); + break; + } + } + for (const auto info : inner.second.DictItems()) { + if (info.first == "visibility") { + component->addInnerApiVisibility(inner.first, info.second.GetList()); + break; + } + } + } + } +} + +bool OhosComponentsImpl::LoadOhosInnerApis_(const std::string innerapi_content, std::string &err_msg_out) +{ + const base::DictionaryValue *innerapis_dict; + + std::unique_ptr innerapis = base::JSONReader::ReadAndReturnError(innerapi_content, + base::JSONParserOptions::JSON_PARSE_RFC, nullptr, &err_msg_out, nullptr, nullptr); + if (!innerapis) { + return false; + } + if (!innerapis->GetAsDictionary(&innerapis_dict)) { + return false; + } + LoadInnerApi(innerapis_dict); + return true; +} + +bool OhosComponentsImpl::LoadOhosComponents(const std::string &build_dir, const Value *enable, Err *err) +{ + const char *paths_file = "parts_info/parts_path_info.json"; + std::string paths_content; + if (!ReadBuildConfigFile(build_dir, paths_file, paths_content)) { + *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but " + "OpenHarmony build config file (" + + std::string(paths_file) + ") does not exists.\n"); + return false; + } + const char *subsystems_file = "parts_info/part_subsystem.json"; + std::string subsystems_content; + if (!ReadBuildConfigFile(build_dir, subsystems_file, subsystems_content)) { + *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but " + "OpenHarmony build config file (" + + std::string(subsystems_file) + ") does not exists.\n"); + return false; + } + const char *innerapis_file = "parts_info/inner_kits_info.json"; + std::string innerapis_content; + if (!ReadBuildConfigFile(build_dir, innerapis_file, innerapis_content)) { + *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but " + "OpenHarmony build config file (" + + std::string(innerapis_file) + ") does not exists.\n"); + return false; + } + const char *override_file = "component_override_map.json"; + std::string override_map; + if (!ReadBuildConfigFile(build_dir, override_file, override_map)) { + override_map = EMPTY_INNERAPI; + } + std::string err_msg_out; + if (!LoadComponentSubsystemAndPaths(paths_content, override_map, subsystems_content, err_msg_out)) { + *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but " + "OpenHarmony build config file parsing failed:\n" + + err_msg_out + "\n"); + return false; + } + if (!LoadOhosInnerApis_(innerapis_content, err_msg_out)) { + *err = Err(*enable, "Your .gn file has enabled \"ohos_components_support\", but " + "OpenHarmony build config file " + + std::string(innerapis_file) + " parsing failed:\n" + err_msg_out + "\n"); + return false; + } + return true; +} + +const OhosComponent *OhosComponentsImpl::GetComponentByName(const std::string &component_name) const +{ + if (auto res = components_.find(component_name); res != components_.end()) { + return res->second; + } + return nullptr; +} + +bool OhosComponentsImpl::GetExternalDepsLabel(const Value &external_dep, std::string &label, Err *err) const +{ + std::string str_val = external_dep.string_value(); + size_t sep = str_val.find(":"); + if (sep <= 0) { + *err = Err(external_dep, "OHOS component external_deps format error: (" + external_dep.string_value() + + ")," + "it should be a string like \"component_name:innerapi_name\"."); + return false; + } + std::string component_name = str_val.substr(0, sep); + const OhosComponent *component = GetComponentByName(component_name); + if (component == nullptr) { + *err = Err(external_dep, "OHOS component : (" + component_name + ") not found."); + return false; + } + std::string innerapi_name = str_val.substr(sep + 1); + label = component->getInnerApi(innerapi_name); + if (label == EMPTY_INNERAPI) { + *err = Err(external_dep, + "OHOS innerapi: (" + innerapi_name + ") not found for component (" + component_name + ")."); + return false; + } + return true; +} + +bool OhosComponentsImpl::GetSubsystemName(const Value &component_name, std::string &subsystem_name, Err *err) const +{ + const OhosComponent *component = GetComponentByName(component_name.string_value()); + if (component == nullptr) { + *err = Err(component_name, "OHOS component : (" + component_name.string_value() + ") not found."); + return false; + } + + subsystem_name = component->subsystem(); + return true; +} + +/** + * Ohos Components Public API + */ + +OhosComponents::OhosComponents() = default; + +bool OhosComponents::LoadOhosComponents(const std::string &build_dir, const Value *enable, Err *err) +{ + if (!enable) { + // Not enabled + return true; + } + if (!enable->VerifyTypeIs(Value::BOOLEAN, err)) { + return false; + } + + // Disabled + if (!enable->boolean_value()) { + return true; + } + + mgr = new OhosComponentsImpl(); + + if (!mgr->LoadOhosComponents(build_dir, enable, err)) { + delete mgr; + mgr = nullptr; + return false; + } + + return true; +} + +bool OhosComponents::isOhosComponentsLoaded() const +{ + if (mgr == nullptr) { + return false; + } else { + return true; + } +} + +bool OhosComponents::GetExternalDepsLabel(const Value &external_dep, std::string &label, Err *err) const +{ + if (!mgr) { + if (err) { + *err = Err(external_dep, "You are compiling OpenHarmony components, but \n" + "\"ohos_components_support\" is not enabled or build_configs files are invalid."); + } + return false; + } + return mgr->GetExternalDepsLabel(external_dep, label, err); +} + +bool OhosComponents::GetSubsystemName(const Value &part_name, std::string &label, Err *err) const +{ + if (!mgr) { + if (err) { + *err = Err(part_name, "You are compiling OpenHarmony components, but \n" + "\"ohos_components_support\" is not enabled or build_configs files are invalid."); + } + return false; + } + return mgr->GetSubsystemName(part_name, label, err); +} + +const OhosComponent *OhosComponents::GetComponentByLabel(const std::string &label) const +{ + if (!mgr) { + return nullptr; + } + return mgr->matchComponentByLabel(label.c_str()); +} + +void OhosComponents::LoadOhosComponentsChecker(const std::string &build_dir, const Value *support, int checkType) +{ + if (!support) { + return; + } + if (!support->boolean_value()) { + return; + } + if (checkType > OhosComponentChecker::CheckType::INTERCEPT_ALL || + checkType <= OhosComponentChecker::CheckType::NONE) { + InnerApiPublicInfoGenerator::Init(build_dir, 0); + return; + } + OhosComponentChecker::Init(build_dir, checkType); + InnerApiPublicInfoGenerator::Init(build_dir, checkType); + return; +} diff --git a/src/gn/ohos_components.h b/src/gn/ohos_components.h new file mode 100644 index 0000000000000000000000000000000000000000..29e7287ec733e63886cd40190b94f2ccbccfdc46 --- /dev/null +++ b/src/gn/ohos_components.h @@ -0,0 +1,84 @@ +// Copyright (c) 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_OHOS_COMPONENTS_H_ +#define TOOLS_GN_OHOS_COMPONENTS_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "base/values.h" +#include "gn/err.h" +#include "gn/value.h" + + +class OhosComponent { +public: + OhosComponent(); + OhosComponent(const char *name, const char *subsystem, const char *path); + + const std::string &name() const + { + return name_; + } + const std::string &subsystem() const + { + return subsystem_; + } + const std::string &path() const + { + return path_; + } + + void addInnerApi(const std::string &name, const std::string &label); + + const std::string &getInnerApi(const std::string &innerapi) const; + + void addInnerApiVisibility(const std::string &name, const std::vector &list); + + const std::vector getInnerApiVisibility(const std::string &label) const; + + bool isInnerApi(const std::string &label) const; + +private: + std::string name_; + std::string subsystem_; + std::string path_; + + // InnerApi name to label map + std::map innerapi_names_; + + // InnerApi label to name map + std::map innerapi_labels_; + + // InnerApi lable to visibility map + std::map> innerapi_visibility_; + + OhosComponent &operator = (const OhosComponent &) = delete; +}; + +class OhosComponentsImpl; + +class OhosComponents { +public: + OhosComponents(); + + bool LoadOhosComponents(const std::string &build_dir, const Value *enable, Err *err); + bool isOhosComponentsLoaded() const; + + bool GetExternalDepsLabel(const Value &external_dep, std::string &label, Err *err) const; + bool GetSubsystemName(const Value &part_name, std::string &label, Err *err) const; + + const OhosComponent *GetComponentByLabel(const std::string &label) const; + + void LoadOhosComponentsChecker(const std::string &build_dir, const Value *support, int checkType); + +private: + OhosComponentsImpl *mgr = nullptr; + + OhosComponents &operator = (const OhosComponents &) = delete; +}; + +#endif // TOOLS_GN_OHOS_COMPONENTS_H_ diff --git a/src/gn/ohos_components_checker.cc b/src/gn/ohos_components_checker.cc new file mode 100644 index 0000000000000000000000000000000000000000..f07b2852cc0962a8a675bb96b6bbb4c735ef7c81 --- /dev/null +++ b/src/gn/ohos_components_checker.cc @@ -0,0 +1,539 @@ +// Copyright (c) 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/ohos_components_checker.h" + +#include +#include +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/values.h" +#include "gn/build_settings.h" +#include "gn/config.h" +#include "gn/filesystem_utils.h" +#include "gn/functions.h" +#include "gn/ohos_components.h" +#include "gn/parse_tree.h" +#include "gn/settings.h" +#include "gn/substitution_writer.h" +#include "gn/target.h" +#include "gn/value.h" + +namespace fs = std::filesystem; + +static const std::string SCAN_RESULT_PATH = "scan_out"; +static const std::string WHITELIST_PATH = "build/component_compilation_whitelist.json"; +static std::vector all_deps_config_; +static std::vector includes_over_range_; +static std::map> innerapi_public_deps_inner_; +static std::vector innerapi_not_lib_; +static std::vector innerapi_not_declare_; +static std::map> includes_absolute_deps_other_; +static std::map> target_absolute_deps_other_; +static std::map> import_other_; + +OhosComponentChecker *OhosComponentChecker::instance_ = nullptr; + +static std::string &Trim(std::string &s) +{ + if (s.empty()) { + return s; + } + s.erase(0, s.find_first_not_of(" \t\r\n")); + s.erase(s.find_last_not_of(" \t\r\n") + 1); + return s; +} + +static bool StartWith(const std::string &str, const std::string prefix) +{ + return (str.rfind(prefix, 0) == 0); +} + +static void CreateScanOutDir(const std::string &dir) +{ + fs::path path(dir); + fs::create_directories(path); + return; +} + +static void RemoveScanOutDir(const std::string& dir) +{ + if (access(dir.c_str(), F_OK) == -1) { + return; + } + for (auto& entry : fs::directory_iterator(dir)) { + if (entry.is_regular_file()) { + fs::remove(entry); + } else if (entry.is_directory()) { + RemoveScanOutDir(entry.path().string()); + } + } + fs::remove(dir); +} + +static bool ReadBuildConfigFile(std::string &content) +{ + if (!base::ReadFileToString(base::FilePath(WHITELIST_PATH), &content)) { + return false; + } + return true; +} + +static void LoadAllDepsConfigWhitelist(const base::Value &list) +{ + for (const base::Value &value : list.GetList()) { + all_deps_config_.push_back(value.GetString()); + } +} + +static void LoadIncludesOverRangeWhitelist(const base::Value &list) +{ + for (const base::Value &value : list.GetList()) { + includes_over_range_.push_back(value.GetString()); + } +} + +static void LoadInnerApiPublicDepsInnerWhitelist(const base::Value &value) +{ + for (auto info : value.DictItems()) { + for (const base::Value &value_tmp : info.second.GetList()) { + innerapi_public_deps_inner_[info.first].push_back(value_tmp.GetString()); + } + } +} + +static void LoadInnerApiNotLibWhitelist(const base::Value &list) +{ + for (const base::Value &value : list.GetList()) { + innerapi_not_lib_.push_back(value.GetString()); + } +} + +static void LoadInnerApiNotDeclareWhitelist(const base::Value &list) +{ + for (const base::Value &value : list.GetList()) { + innerapi_not_declare_.push_back(value.GetString()); + } +} + +static void LoadIncludesAbsoluteDepsOtherWhitelist(const base::Value &value) +{ + for (auto info : value.DictItems()) { + for (const base::Value &value_tmp : info.second.GetList()) { + includes_absolute_deps_other_[info.first].push_back(value_tmp.GetString()); + } + } +} + +static void LoadAbsoluteDepsOtherWhitelist(const base::Value &value) +{ + for (auto info : value.DictItems()) { + for (const base::Value &value_tmp : info.second.GetList()) { + target_absolute_deps_other_[info.first].push_back(value_tmp.GetString()); + } + } +} + +static void LoadImportOtherWhitelist(const base::Value &value) +{ + for (auto info : value.DictItems()) { + for (const base::Value &value_tmp : info.second.GetList()) { + import_other_[info.first].push_back(value_tmp.GetString()); + } + } +} + +static std::map> whitelist_map_ = { + { "all_dependent_configs", LoadAllDepsConfigWhitelist }, + { "includes_over_range", LoadIncludesOverRangeWhitelist }, + { "innerapi_not_lib", LoadInnerApiNotLibWhitelist }, + { "innerapi_not_declare", LoadInnerApiNotDeclareWhitelist }, + { "innerapi_public_deps_inner", LoadInnerApiPublicDepsInnerWhitelist }, + { "includes_absolute_deps_other", LoadIncludesAbsoluteDepsOtherWhitelist }, + { "target_absolute_deps_other", LoadAbsoluteDepsOtherWhitelist }, + { "import_other", LoadImportOtherWhitelist } +}; + +static void LoadWhitelist() +{ + std::string whitelistContent; + if (!ReadBuildConfigFile(whitelistContent)) { + return; + } + const base::DictionaryValue *whitelist_dict; + std::unique_ptr whitelist = base::JSONReader::ReadAndReturnError(whitelistContent, + base::JSONParserOptions::JSON_PARSE_RFC, nullptr, nullptr, nullptr, nullptr); + if (!whitelist) { + return; + } + if (!whitelist->GetAsDictionary(&whitelist_dict)) { + return; + } + + for (const auto kv : whitelist_dict->DictItems()) { + auto iter = whitelist_map_.find(kv.first); + if (iter != whitelist_map_.end()) { + iter->second(kv.second); + } + } + return; +} + +bool OhosComponentChecker::InterceptAllDepsConfig(const Target *target, const std::string label, Err *err) const +{ + auto result = std::find(all_deps_config_.begin(), all_deps_config_.end(), label); + if (result != all_deps_config_.end()) { + return true; + } + + *err = Err(target->defined_from(), "all_dependent_configs not allowed.", + "The item " + label + " does not allow all_dependent_configs."); + return false; +} + +bool OhosComponentChecker::InterceptIncludesOverRange(const Target *target, const std::string label, + const std::string dir, Err *err) const +{ + auto result = std::find(includes_over_range_.begin(), includes_over_range_.end(), label); + if (result != includes_over_range_.end()) { + return true; + } + *err = Err(target->defined_from(), "Header file range is too large.", + "The item " + label + " header : " + dir + " range is too large."); + return false; +} + +bool OhosComponentChecker::InterceptInnerApiPublicDepsInner(const Target *target, const std::string label, + const std::string deps, Err *err) const +{ + if (auto res = innerapi_public_deps_inner_.find(label); res != innerapi_public_deps_inner_.end()) { + std::string deps_str(deps); + auto res_second = std::find(res->second.begin(), res->second.end(), Trim(deps_str)); + if (res_second != res->second.end()) { + return true; + } + } + *err = Err(target->defined_from(), "InnerApi not allow the use of public_deps dependent internal modules.", + "The item " + label + " not allow the use of public_deps dependent internal modules : " + deps); + return false; +} + +bool OhosComponentChecker::InterceptInnerApiNotLib(const Item *item, const std::string label, Err *err) const +{ + auto result = std::find(innerapi_not_lib_.begin(), innerapi_not_lib_.end(), label); + if (result != innerapi_not_lib_.end()) { + return true; + } + *err = + Err(item->defined_from(), "InnerApi is not a library type.", "The item " + label + " is not a library type."); + return false; +} + +bool OhosComponentChecker::InterceptInnerApiNotDeclare(const Item *item, const std::string label, Err *err) const +{ + auto result = std::find(innerapi_not_declare_.begin(), innerapi_not_declare_.end(), label); + if (result != innerapi_not_declare_.end()) { + return true; + } + *err = Err(item->defined_from(), "InnerApi is not defined in bundle.json.", + "The item " + label + " is not defined in bundle.json."); + return false; +} + +bool OhosComponentChecker::InterceptIncludesAbsoluteDepsOther(const Target *target, const std::string label, + const std::string includes, Err *err) const +{ + if (auto res = includes_absolute_deps_other_.find(label); res != includes_absolute_deps_other_.end()) { + std::string includes_str(includes); + auto res_second = std::find(res->second.begin(), res->second.end(), Trim(includes_str)); + if (res_second != res->second.end()) { + return true; + } + } + *err = Err(target->defined_from(), "Do not directly use header files of other components.", + "The item " + label + " do not directly use header files : " + includes + " of other components." + + "\n" + "Please use 'external_deps/public_external_deps' dependent module."); + return false; +} + + +bool OhosComponentChecker::InterceptTargetAbsoluteDepsOther(const Item *item, const std::string label, + const std::string deps, Err *err) const +{ + if (auto res = target_absolute_deps_other_.find(label); res != target_absolute_deps_other_.end()) { + std::string deps_str(deps); + auto res_second = std::find(res->second.begin(), res->second.end(), Trim(deps_str)); + if (res_second != res->second.end()) { + return true; + } + } + *err = Err(item->defined_from(), "Not allow use absolute dependent other component.", + "The item " + label + " not allow use absolute dependent other component : " + deps + + "\n" + "Please use 'external_deps/public_external_deps'."); + return false; +} + +bool OhosComponentChecker::InterceptInnerApiVisibilityDenied(const Item *item, const std::string from_label, + const std::string to_label, Err *err) const +{ + *err = Err(item->defined_from(), "InnerApi visibility denied.", + "The item " + from_label + " cannot dependent " + to_label + + "\n" + "Please check 'visibility' field in 'bundle.json' of " + + to_label); + return false; +} + +bool OhosComponentChecker::InterceptImportOther(const FunctionCallNode* function, const std::string label, + const std::string deps, Err *err) const +{ + if (auto res = import_other_.find(label); res != import_other_.end()) { + std::string deps_str(deps); + auto res_second = std::find(res->second.begin(), res->second.end(), Trim(deps_str)); + if (res_second != res->second.end()) { + return true; + } + } + *err = Err(function->function(), "Not allow import other gni.", + label + " not allow import other gni : " + deps); + return false; +} + +OhosComponentChecker::OhosComponentChecker(const std::string &build_dir, int checkType) +{ + checkType_ = checkType; + build_dir_ = build_dir; + if (checkType_ == CheckType::INTERCEPT_IGNORE_TEST || checkType_ == CheckType::INTERCEPT_ALL) { + LoadWhitelist(); + } + if (checkType_ == CheckType::SCAN_ALL || checkType_ == CheckType::INTERCEPT_ALL) { + ignoreTest_ = false; + } + RemoveScanOutDir(build_dir_ + "/" + SCAN_RESULT_PATH); +} + +void OhosComponentChecker::GenerateScanList(const std::string path, const std::string subsystem, + const std::string component, const std::string label, const std::string deps) const +{ + CreateScanOutDir(build_dir_ + "/" + SCAN_RESULT_PATH); + std::ofstream file; + file.open(build_dir_ + "/" + SCAN_RESULT_PATH + "/" + path, std::ios::app); + file << subsystem << " " << component << " " << label << " " << deps << "\n"; + file.close(); + return; +} + +bool OhosComponentChecker::CheckAllDepsConfigs(const Target *target, const std::string label, Err *err) const +{ + if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) { + return true; + } + + const OhosComponent *component = target->ohos_component(); + if (component == nullptr) { + return true; + } + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptAllDepsConfig(target, label, err); + } + GenerateScanList("all_dependent_configs.list", component->subsystem(), component->name(), label, ""); + return true; +} + +bool OhosComponentChecker::CheckInnerApiIncludesOverRange(const Target *target, const std::string label, + const std::string dir, Err *err) const +{ + if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) { + return true; + } + + const OhosComponent *component = target->ohos_component(); + if (component == nullptr || (!component->isInnerApi(label) && !StartWith(label, "//third_party"))) { + return true; + } + + if (dir != "." && dir != "./" && dir != "../" && dir != component->path() && dir != component->path() + "/") { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptIncludesOverRange(target, label, dir, err); + } + GenerateScanList("includes_over_range.list", component->subsystem(), component->name(), label, dir); + return true; +} + +bool OhosComponentChecker::CheckInnerApiPublicDepsInner(const Target *target, const std::string label, + const std::string deps, Err *err) const +{ + if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) { + return true; + } + + const OhosComponent *component = target->ohos_component(); + if (component == nullptr || !component->isInnerApi(label)) { + return true; + } + + if (!StartWith(deps, component->path()) && StartWith(deps, "//")) { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptInnerApiPublicDepsInner(target, label, deps, err); + } + GenerateScanList("innerapi_public_deps_inner.list", component->subsystem(), component->name(), label, deps); + return true; +} + +bool OhosComponentChecker::CheckInnerApiNotLib(const Item *item, const OhosComponent *component, + const std::string label, Err *err) const +{ + if (checkType_ <= CheckType::NONE || item == nullptr || item->AsTarget() == nullptr || + (ignoreTest_ && item->testonly()) || component == nullptr) { + return true; + } + + Target::OutputType type = item->AsTarget()->output_type(); + if (type == Target::SHARED_LIBRARY || type == Target::STATIC_LIBRARY || type == Target::RUST_LIBRARY) { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptInnerApiNotLib(item, label, err); + } + GenerateScanList("innerapi_not_lib.list", component->subsystem(), component->name(), label, ""); + return true; +} + +bool OhosComponentChecker::CheckInnerApiNotDeclare(const Item *item, const OhosComponent *component, + const std::string label, Err *err) const +{ + if (checkType_ <= CheckType::NONE || component == nullptr || item == nullptr || (ignoreTest_ && item->testonly())) { + return true; + } + + if (component->isInnerApi(label)) { + return true; + } + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptInnerApiNotDeclare(item, label, err); + } + GenerateScanList("innerapi_not_declare.list", component->subsystem(), component->name(), label, ""); + return true; +} + +bool OhosComponentChecker::CheckIncludesAbsoluteDepsOther(const Target *target, const std::string label, + const std::string includes, Err *err) const +{ + if (checkType_ <= CheckType::NONE || target == nullptr || (ignoreTest_ && target->testonly())) { + return true; + } + + if (!StartWith(includes, "//") || StartWith(includes, "//out/") || StartWith(includes, "////out/")) { + return true; + } + + const OhosComponent *component = target->ohos_component(); + if (component == nullptr) { + return true; + } + + if (StartWith(includes, component->path())) { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptIncludesAbsoluteDepsOther(target, label, includes, err); + } + GenerateScanList("includes_absolute_deps_other.list", component->subsystem(), component->name(), label, includes); + return true; +} + +bool OhosComponentChecker::CheckInnerApiVisibilityDenied(const Item *item, const OhosComponent *component, + const std::string label, const std::string deps, Err *err) const +{ + if (checkType_ <= CheckType::NONE || component == nullptr || item == nullptr || (ignoreTest_ && item->testonly())) { + return true; + } + + if (!component->isInnerApi(deps)) { + return true; + } + std::vector visibility = component->getInnerApiVisibility(deps); + if (visibility.empty()) { + return true; + } + + const OhosComponent *from_component = item->ohos_component(); + if (from_component == nullptr) { + return true; + } + auto result = std::find(visibility.begin(), visibility.end(), from_component->name()); + if (result != visibility.end()) { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptInnerApiVisibilityDenied(item, label, deps, err); + } + GenerateScanList("innerkit_visibility_denied.list", from_component->subsystem(), from_component->name(), label, + deps); + return true; +} + +bool OhosComponentChecker::CheckTargetAbsoluteDepsOther(const Item *item, const OhosComponent *component, + const std::string label, const std::string deps, bool is_external_deps, Err *err) const +{ + if (checkType_ <= CheckType::NONE || component == nullptr || item == nullptr || (ignoreTest_ && item->testonly())) { + return true; + } + if (!component->isInnerApi(deps)) { + return true; + } + + if (is_external_deps) { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptTargetAbsoluteDepsOther(item, label, deps, err); + } + + const OhosComponent *from_component = item->ohos_component(); + if (from_component == nullptr) { + return true; + } + GenerateScanList("target_absolute_deps_other.list", + from_component->subsystem(), from_component->name(), label, deps); + return true; +} + +bool OhosComponentChecker::CheckImportOther(const FunctionCallNode* function, const BuildSettings* build_settings, + const std::string label, const std::string deps, Err *err) const +{ + if (checkType_ <= CheckType::NONE || function == nullptr || build_settings == nullptr) { + return true; + } + const OhosComponent *component = build_settings->GetOhosComponent(label); + if (component == nullptr) { + return true; + } + if (StartWith(deps, component->path()) || StartWith(deps, "//build/") || StartWith(deps, "//out/")) { + return true; + } + + if (checkType_ >= CheckType::INTERCEPT_IGNORE_TEST) { + return InterceptImportOther(function, label, deps, err); + } + GenerateScanList("import_other.list", component->subsystem(), component->name(), label, deps); + return true; +} \ No newline at end of file diff --git a/src/gn/ohos_components_checker.h b/src/gn/ohos_components_checker.h new file mode 100644 index 0000000000000000000000000000000000000000..423ba4d119950505fe1737c3677df7dd29f96ab7 --- /dev/null +++ b/src/gn/ohos_components_checker.h @@ -0,0 +1,84 @@ +// Copyright (c) 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OHOS_COMPONENTS_CHECKER_H_ +#define OHOS_COMPONENTS_CHECKER_H_ + +#include "gn/build_settings.h" +#include "gn/config.h" +#include "gn/functions.h" +#include "gn/parse_tree.h" +#include "gn/settings.h" +#include "gn/substitution_writer.h" +#include "gn/target.h" +#include "gn/value.h" + +class OhosComponentChecker { +public: + enum CheckType { + NONE = 0, + SCAN_IGNORE_TEST, + SCAN_ALL, + INTERCEPT_IGNORE_TEST, + INTERCEPT_ALL + }; + + static void Init(const std::string &build_dir, int checkType) + { + if (instance_ != nullptr) { + return; + } + instance_ = new OhosComponentChecker(build_dir, checkType); + } + + bool CheckAllDepsConfigs(const Target *target, const std::string label, Err *err) const; + bool CheckInnerApiIncludesOverRange(const Target *target, const std::string label, const std::string dir, + Err *err) const; + bool CheckInnerApiPublicDepsInner(const Target *target, const std::string label, const std::string deps, + Err *err) const; + bool CheckInnerApiNotLib(const Item *item, const OhosComponent *component, const std::string label, Err *err) const; + bool CheckInnerApiNotDeclare(const Item *item, const OhosComponent *component, const std::string label, + Err *err) const; + bool CheckIncludesAbsoluteDepsOther(const Target *target, const std::string label, const std::string includes, + Err *err) const; + bool CheckInnerApiVisibilityDenied(const Item *item, const OhosComponent *component, const std::string label, + const std::string deps, Err *err) const; + bool CheckTargetAbsoluteDepsOther(const Item *item, const OhosComponent *component, const std::string label, + const std::string deps, bool is_external_deps, Err *err) const; + bool CheckImportOther(const FunctionCallNode* function, const BuildSettings* build_settings, + const std::string label, const std::string deps, Err *err) const; + + static OhosComponentChecker *getInstance() + { + return instance_; + } + +private: + int checkType_ = NONE; + bool ignoreTest_ = true; + std::string build_dir_; + static OhosComponentChecker *instance_; + bool InterceptAllDepsConfig(const Target *target, const std::string label, Err *err) const; + bool InterceptIncludesOverRange(const Target *target, const std::string label, const std::string dir, + Err *err) const; + bool InterceptInnerApiPublicDepsInner(const Target *target, const std::string label, const std::string deps, + Err *err) const; + bool InterceptInnerApiNotLib(const Item *item, const std::string label, Err *err) const; + bool InterceptInnerApiNotDeclare(const Item *item, const std::string label, Err *err) const; + bool InterceptIncludesAbsoluteDepsOther(const Target *target, const std::string label, const std::string includes, + Err *err) const; + bool InterceptInnerApiVisibilityDenied(const Item *item, const std::string from_label, const std::string to_label, + Err *err) const; + bool InterceptTargetAbsoluteDepsOther(const Item *item, const std::string label, const std::string deps, + Err *err) const; + bool InterceptImportOther(const FunctionCallNode* function, const std::string label, const std::string deps, + Err *err) const; + void GenerateScanList(const std::string path, const std::string subsystem, const std::string component, + const std::string label, const std::string deps) const; + OhosComponentChecker() {} + OhosComponentChecker(const std::string &build_dir, int checkType); + OhosComponentChecker &operator = (const OhosComponentChecker &) = delete; +}; + +#endif // OHOS_COMPONENTS_CHECKER_H_ diff --git a/src/gn/ohos_components_impl.h b/src/gn/ohos_components_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..852866e5089123a8a020350c1b3ec59a4b8646f1 --- /dev/null +++ b/src/gn/ohos_components_impl.h @@ -0,0 +1,91 @@ +// Copyright (c) 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_OHOS_COMPONENTS_MGR_H_ +#define TOOLS_GN_OHOS_COMPONENTS_MGR_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "base/values.h" +#include "gn/err.h" +#include "gn/value.h" + +class OhosComponent; + +struct OhosComponentTree { + const char *dirName; + struct OhosComponentTree *next; + struct OhosComponentTree *child; + const OhosComponent *component; + +public: + OhosComponentTree(const char *dirName, const OhosComponent *component = nullptr) + { + this->dirName = strdup(dirName); + this->component = component; + this->next = nullptr; + this->child = nullptr; + } + + OhosComponentTree(const char *dirName, size_t len, const OhosComponent *component = nullptr) + { + this->dirName = (char *)malloc(len + 1); + if (this->dirName) { + strncpy((char *)this->dirName, dirName, len); + ((char *)this->dirName)[len] = '\0'; + } + this->component = component; + this->next = nullptr; + this->child = nullptr; + } + + ~OhosComponentTree() + { + if (!this->dirName) { + free((void *)this->dirName); + } + } +}; + +class OhosComponentsImpl { +public: + OhosComponentsImpl(); + + bool LoadOhosComponents(const std::string &build_dir, + const Value *enable, Err *err); + + bool GetExternalDepsLabel(const Value &external_dep, std::string &label, Err *err) const; + bool GetSubsystemName(const Value &component_name, std::string &subsystem_name, Err *err) const; + + const OhosComponent *GetComponentByName(const std::string &component_name) const; + +private: + bool ReadBuildConfigFile(const std::string &build_dir, + const char *subfile, std::string &content); + + std::map components_; + + struct OhosComponentTree *pathTree = nullptr; + void setupComponentsTree(); + const struct OhosComponentTree *findChildByPath(const struct OhosComponentTree *current, + const char *path, size_t len); + void addComponentToTree(struct OhosComponentTree *current, OhosComponent *component); + + void LoadInnerApi(const base::DictionaryValue *innerapis); + + // For unittest +public: + const OhosComponent *matchComponentByLabel(const char *label); + + bool LoadComponentSubsystemAndPaths(const std::string &paths, + const std::string &override_map, + const std::string &subsystems, + std::string &err_msg_out); + + bool LoadOhosInnerApis_(const std::string innerapi_content, std::string &err_msg_out); +}; + +#endif // TOOLS_GN_OHOS_COMPONENTS_MGR_H_ \ No newline at end of file diff --git a/src/gn/ohos_components_unittest.cc b/src/gn/ohos_components_unittest.cc new file mode 100644 index 0000000000000000000000000000000000000000..1e41772c16fcb3a033443719d1a6091dcdc98b63 --- /dev/null +++ b/src/gn/ohos_components_unittest.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/ohos_components.h" +#include "gn/ohos_components_impl.h" + +#include +#include +#include +#include + +#include "gn/test_with_scope.h" +#include "util/test/test.h" + +static const std::string COMPONENT_PATHS = "{" + "\"foo\": \"components/foo\"," + "\"bar\": \"components/bar\"," + "\"baz\": \"components/baz\"" + "}"; + +static const std::string SUBSYSTEM_PATHS = "{" + "\"foo\": \"samples\"," + "\"bar\": \"samples\"," + "\"baz\": \"samples\"" + "}"; + +static const std::string STR_INNERAPIS = "{" + "\"foo\": {" + "\"libfoo\": {" + "\"label\": \"//components/foo/interfaces/innerapis/libfoo:libfoo\"" + "}" + "}," + "\"bar\": {" + "\"libbar\": {" + "\"label\": \"//components/bar/interfaces/innerapis/libbar:libbar\"" + "}" + "}," + "\"baz\": {" + "\"libbaz\": {" + "\"label\": \"//components/baz/interfaces/innerapis/libbaz:libbaz\"" + "}" + "}" + "}"; + +TEST(OhosComponent, ComponentInnerApi) { + OhosComponent com("foo", "samples", "components/foo"); + EXPECT_EQ("foo", com.name()); + EXPECT_EQ("samples", com.subsystem()); + EXPECT_EQ("//components/foo", com.path()); + + // Add two innerapis + const std::string fooLabel = "//components/foo/interfaces/innerapis/libfoo:libfoo"; + com.addInnerApi("libfoo", fooLabel.c_str()); + const std::string barLabel = "//components/bar/interfaces/innerapis/libbar:libbar"; + com.addInnerApi("libbar", barLabel.c_str()); + + // Get first innerapi + std::string result = com.getInnerApi("libfoo"); + EXPECT_EQ(fooLabel, result); + + // Get second innerapi api + result = com.getInnerApi("libbar"); + EXPECT_EQ(barLabel, result); + + // Get non exist innerapi api + result = com.getInnerApi("libnone"); + EXPECT_EQ(0, result.size()); + + // Check valid innerapi label + bool ret = com.isInnerApi("//components/bar/interfaces/innerapis/libbar:libbar"); + ASSERT_TRUE(ret); + + // Check invalid innerapi label + ret = com.isInnerApi("//components/bar/interfaces/innerapis/libbar:libbar2"); + ASSERT_FALSE(ret); +} + +TEST(OhosComponentsImpl, LoadComponentSubsystemAndPaths) { + OhosComponentsImpl *mgr = new OhosComponentsImpl(); + std::string errStr; + bool ret = mgr->LoadComponentSubsystemAndPaths(COMPONENT_PATHS, + "", + SUBSYSTEM_PATHS, + errStr); + ASSERT_TRUE(ret); + + ret = mgr->LoadOhosInnerApis_(STR_INNERAPIS, errStr); + ASSERT_TRUE(ret); + + const OhosComponent *component; + + component = mgr->matchComponentByLabel("components/foo"); + EXPECT_EQ("foo", component->name()); + component = mgr->matchComponentByLabel("//components/foo"); + EXPECT_EQ("foo", component->name()); + component = mgr->matchComponentByLabel("//components/foo:libfoo"); + EXPECT_EQ("foo", component->name()); + component = mgr->matchComponentByLabel("//components/foo/test"); + EXPECT_EQ("foo", component->name()); + component = mgr->matchComponentByLabel("//components/foo/test:libfoo"); + EXPECT_EQ("foo", component->name()); + + component = mgr->matchComponentByLabel("components/fo"); + EXPECT_EQ(nullptr, component); + component = mgr->matchComponentByLabel("components/"); + EXPECT_EQ(nullptr, component); + component = mgr->matchComponentByLabel("components"); + EXPECT_EQ(nullptr, component); + + Err err; + Value external_dep(nullptr, "foo:libfoo"); + std::string label; + ret = mgr->GetExternalDepsLabel(external_dep, label, &err); + ASSERT_TRUE(ret); + + delete mgr; +} \ No newline at end of file diff --git a/src/gn/ohos_variables.cc b/src/gn/ohos_variables.cc new file mode 100644 index 0000000000000000000000000000000000000000..6ed0ec0e731447595976d4f5a4dedc6f13f85561 --- /dev/null +++ b/src/gn/ohos_variables.cc @@ -0,0 +1,82 @@ +// Copyright 2024 The Open Harmony Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/ohos_variables.h" + +namespace variables { + +// OpenHarmony target vars ----------------------------------------------------- + +const char kCopyLinkableFile[] = "copy_linkable_file"; +const char kCopyLinkableFile_HelpShort[] = + "copy_linkable_file: [boolean] Copy a linkable static or shared library."; +const char kCopyLinkableFile_Help[] = + R"(copy_linkable_file: [boolean] Copy a linkable static or shared library. + + A copy target normally doesn't export configs for targets that depend on it. + In some cases, a copy target is a linkable target, which means it is a static + or shared library with some public_configs to be exported. + + In this case, we can set copy_linkable_file to export configs. + +Example + + copy("foo") { + copy_linkable_file = true + public_configs = [ ":exported_header_files" ] + } + + executable("bar") { + # Target bar will get public_configs of foo target + deps = [ "foo" ] + } +)"; + +const char kExternalDeps[] = "external_deps"; +const char kExternalDeps_HelpShort[] = + "external_deps: [label list] Declare external dependencies for OpenHarmony component."; +const char kExternalDeps_Help[] = + R"(external_deps: Declare external dependencies for OpenHarmony component. + + External dependencies are like private dependencies (see "gn help deps") but + expressed in the form of: component_name:innerapi_name. + * component and innerapi are defined by OpenHarmony bundle.json. + With external_deps, deps can be added without absolute path. + + This variable is enabled by setting "ohos_components_support" in .gn file (see "gn help dotfile"). + +Example + + # This target "a" can include libinitapi from "init" component + executable("a") { + deps = [ ":b" ] + external_deps = [ "init:libinitapi" ] + } +)"; + +const char kPublicExternalDeps[] = "public_external_deps"; +const char kPublicExternalDeps_HelpShort[] = + "public_external_deps: [label list] Declare public external dependencies for OpenHarmony component."; +const char kPublicExternalDeps_Help[] = + R"(public_external_deps: Declare public external dependencies for OpenHarmony component. + + Public external dependencies (public_external_deps) are like external_deps but additionally express that + the current target exposes the listed external_deps as part of its public API. + + This variable is enabled by setting "ohos_components_support" in .gn file (see "gn help dotfile"). + +Example + + # Target "a" will include libinitapi from "init" component by deps "b": + executable("a") { + deps = [ ":b" ] + } + + shared_library("b") { + deps = [ ":b" ] + public_external_deps = [ "init:libinitapi" ] + } +)"; + +} // namespace variables diff --git a/src/gn/ohos_variables.h b/src/gn/ohos_variables.h new file mode 100644 index 0000000000000000000000000000000000000000..c900d92f306cbda7342e5f2d0f8ba6e5e5a76194 --- /dev/null +++ b/src/gn/ohos_variables.h @@ -0,0 +1,27 @@ +// Copyright 2024 The Open Harmony Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_OHOS_TARGET_VARIABLES_H_ +#define TOOLS_GN_OHOS_TARGET_VARIABLES_H_ + +#include "gn/variables.h" + +namespace variables { + +// OpenHarmony target vars ----------------------------------------------------- + +extern const char kCopyLinkableFile[]; +extern const char kCopyLinkableFile_HelpShort[]; +extern const char kCopyLinkableFile_Help[]; + +extern const char kExternalDeps[]; +extern const char kExternalDeps_HelpShort[]; +extern const char kExternalDeps_Help[]; + +extern const char kPublicExternalDeps[]; +extern const char kPublicExternalDeps_HelpShort[]; +extern const char kPublicExternalDeps_Help[]; +} // namespace variables + +#endif // TOOLS_GN_SWIFT_TARGET_VARIABLES_H_ diff --git a/src/gn/setup.cc b/src/gn/setup.cc index 68d42b750e0f69b1b367b8f9ecfc02953a701d68..c0c99a30d22783b8bc302d727651fb3e4369bea9 100644 --- a/src/gn/setup.cc +++ b/src/gn/setup.cc @@ -168,6 +168,21 @@ Variables required version is 1.7.2. Specifying a higher version might enable the use of some of newer features that can make the build more efficient. + ohos_components_support [optional] + This parameter enable support for OpenHarmony components. + When enabled, gn will load components information from "build_configs/" + directory in the root_out_directory. + + The following files will be loaded: + out/build_configs/parts_info/inner_kits_info.json (required): + Required InnerAPI information file for each OHOS component. + out/build_configs/component_override_map.json (optional): + Optional overrided component maps file. + + For OpenHarmony system build, this value must be enabled to support + external_deps (see "gn help external_deps") and public_external_deps + (see "gn help public_external_deps"). + Example .gn file contents buildconfig = "//build/config/BUILDCONFIG.gn" @@ -386,6 +401,28 @@ bool Setup::DoSetup(const std::string& build_dir, return true; } +bool Setup::FillOhosComponentsInfo(const std::string& build_dir, Err* err) +{ + // Load OpenHarmony system components definition file. + const Value *support = + dotfile_scope_.GetValue("ohos_components_support", true); + if (!ohos_components_.LoadOhosComponents(build_dir, support, err)) { + return false; + } + + if (ohos_components_.isOhosComponentsLoaded()) { + build_settings_.SetOhosComponentsInfo(&ohos_components_); + } + + const Value* checkType = build_settings_.build_args().GetArgOverride("ohos_components_checktype"); + if (checkType && checkType->type() == Value::INTEGER) { + ohos_components_.LoadOhosComponentsChecker(build_dir, support, checkType->int_value()); + } else { + ohos_components_.LoadOhosComponentsChecker(build_dir, support, 0); + } + return true; +} + bool Setup::DoSetupWithErr(const std::string& build_dir, bool force_create, const base::CommandLine& cmdline, @@ -420,6 +457,11 @@ bool Setup::DoSetupWithErr(const std::string& build_dir, if (!FillArguments(cmdline, err)) return false; } + + if (!FillOhosComponentsInfo(build_dir, err)) { + return false; + } + if (!FillPythonPath(cmdline, err)) return false; diff --git a/src/gn/setup.h b/src/gn/setup.h index 2809a7d3db7ddf1ff593c888fcfa0b0d020c07f9..532e485127336cc318f30566772fa78df899c969 100644 --- a/src/gn/setup.h +++ b/src/gn/setup.h @@ -13,6 +13,7 @@ #include "gn/builder.h" #include "gn/label_pattern.h" #include "gn/loader.h" +#include "gn/ohos_components.h" #include "gn/scheduler.h" #include "gn/scope.h" #include "gn/settings.h" @@ -132,6 +133,8 @@ class Setup { // Fills build arguments. Returns true on success. bool FillArguments(const base::CommandLine& cmdline, Err* err); + bool FillOhosComponentsInfo(const std::string& build_dir, Err* err); + // Fills the build arguments from the command line or from the build arg file. bool FillArgsFromCommandLine(const std::string& args, Err* err); bool FillArgsFromFile(Err* err); @@ -164,6 +167,8 @@ class Setup { bool FillOtherConfig(const base::CommandLine& cmdline, Err* err); BuildSettings build_settings_; + OhosComponents ohos_components_; + scoped_refptr loader_; Builder builder_; diff --git a/src/gn/target.cc b/src/gn/target.cc index 7b545b522fcd07ce571bd6a9bec7d482284ba2ba..d4440ea6fec12b579903aae05f63f10f1c494cf2 100644 --- a/src/gn/target.cc +++ b/src/gn/target.cc @@ -541,6 +541,9 @@ bool Target::IsBinary() const { } bool Target::IsLinkable() const { + if (output_type_ == COPY_FILES) { + return copy_linkable_file(); + } return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY || output_type_ == RUST_LIBRARY || output_type_ == RUST_PROC_MACRO; } @@ -762,7 +765,9 @@ void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) { dep->output_type() == RUST_LIBRARY || dep->output_type() == SOURCE_SET || (dep->output_type() == CREATE_BUNDLE && - dep->bundle_data().is_framework())) { + dep->bundle_data().is_framework()) || + (dep->output_type() == COPY_FILES && + dep->copy_linkable_file())) { inherited_libraries_.Append(dep, is_public); } @@ -771,7 +776,9 @@ void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) { if (dep->output_type() == STATIC_LIBRARY || dep->output_type() == SHARED_LIBRARY || dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY || - dep->output_type() == GROUP) { + dep->output_type() == GROUP || + (dep->output_type() == COPY_FILES && + dep->copy_linkable_file())) { // Here we have: `this` --[depends-on]--> `dep` // // The `this` target has direct access to `dep` since its a direct @@ -798,7 +805,9 @@ void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) { rust_transitive_inheritable_libs_.Append(dep, is_public); } - if (dep->output_type() == SHARED_LIBRARY) { + if ((dep->output_type() == SHARED_LIBRARY) || + (dep->output_type() == COPY_FILES && + dep->copy_linkable_file())) { // Shared library dependendencies are inherited across public shared // library boundaries. // @@ -1048,6 +1057,11 @@ bool Target::FillOutputFiles(Err* err) { OutputFile(settings()->build_settings(), out)); } + if ((output_type_ == COPY_FILES) && copy_linkable_file()) { + std::vector tool_outputs; + link_output_file_ = computed_outputs()[0]; + } + return true; } @@ -1112,7 +1126,7 @@ bool Target::ResolvePrecompiledHeaders(Err* err) { bool Target::CheckVisibility(Err* err) const { for (const auto& pair : GetDeps(DEPS_ALL)) { - if (!Visibility::CheckItemVisibility(this, pair.ptr, err)) + if (!Visibility::CheckItemVisibility(this, pair.ptr, pair.is_external_deps, err)) return false; } return true; @@ -1121,7 +1135,7 @@ bool Target::CheckVisibility(Err* err) const { bool Target::CheckConfigVisibility(Err* err) const { for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) { if (const Config* config = iter.GetCurrentConfig()) - if (!Visibility::CheckItemVisibility(this, config, err)) + if (!Visibility::CheckItemVisibility(this, config, false, err)) return false; } return true; diff --git a/src/gn/target.h b/src/gn/target.h index 66e5c6cf5087353aca557fb3101dd183897f02b1..5af1cdf2f00a056879762f00228e3308d59a8b6a 100644 --- a/src/gn/target.h +++ b/src/gn/target.h @@ -164,6 +164,14 @@ class Target : public Item { complete_static_lib_ = complete; } + // Whether this copy target is linkable. + bool copy_linkable_file() const { return copy_linkable_file_; } + void set_copy_linkable_file(bool linkable) + { + DCHECK_EQ(COPY_FILES, output_type_); + copy_linkable_file_ = linkable; + } + // Metadata. Target takes ownership of the resulting scope. const Metadata& metadata() const; Metadata& metadata(); @@ -479,6 +487,7 @@ class Target : public Item { FileList public_headers_; bool check_includes_ = true; bool complete_static_lib_ = false; + bool copy_linkable_file_ = false; std::vector data_; std::unique_ptr bundle_data_; OutputFile write_runtime_deps_output_; diff --git a/src/gn/target_generator.cc b/src/gn/target_generator.cc index 4138889a92b8a47d72b9b57cbbf2892048f10f66..2d3af051f2a2fd1d9d608242f3c5568431f5ac41 100644 --- a/src/gn/target_generator.cc +++ b/src/gn/target_generator.cc @@ -21,7 +21,10 @@ #include "gn/functions.h" #include "gn/generated_file_target_generator.h" #include "gn/group_target_generator.h" +#include "gn/innerapis_publicinfo_generator.h" #include "gn/metadata.h" +#include "gn/ohos_components.h" +#include "gn/ohos_variables.h" #include "gn/parse_tree.h" #include "gn/scheduler.h" #include "gn/scope.h" @@ -166,6 +169,12 @@ void TargetGenerator::GenerateTarget(Scope* scope, *err = Err(function_call, "Can't define a target in this context."); return; } + + InnerApiPublicInfoGenerator* instance = InnerApiPublicInfoGenerator::getInstance(); + if (instance != nullptr) { + instance->GeneratedInnerapiPublicInfo(target.get(), label, scope, output_type, err); + } + collector->push_back(std::move(target)); } @@ -256,6 +265,12 @@ bool TargetGenerator::FillData() { bool TargetGenerator::FillDependencies() { if (!FillGenericDeps(variables::kDeps, &target_->private_deps())) return false; + if (scope_->settings()->build_settings()->is_ohos_components_enabled()) { + if (!FillOhosComponentDeps(variables::kExternalDeps, &target_->private_deps())) + return false; + if (!FillOhosComponentDeps(variables::kPublicExternalDeps, &target_->public_deps())) + return false; + } if (!FillGenericDeps(variables::kPublicDeps, &target_->public_deps())) return false; if (!FillGenericDeps(variables::kDataDeps, &target_->data_deps())) @@ -433,6 +448,17 @@ bool TargetGenerator::FillGenericDeps(const char* var_name, return !err_->has_error(); } +bool TargetGenerator::FillOhosComponentDeps(const char* var_name, LabelTargetVector* dest) +{ + const Value* value = scope_->GetValue(var_name, true); + if (value) { + // Append to private deps + ExtractListOfExternalDeps(scope_->settings()->build_settings(), *value, + scope_->GetSourceDir(), ToolchainLabelForScope(scope_), + dest, err_); + } + return !err_->has_error(); +} bool TargetGenerator::FillWriteRuntimeDeps() { const Value* value = scope_->GetValue(variables::kWriteRuntimeDeps, true); if (!value) diff --git a/src/gn/target_generator.h b/src/gn/target_generator.h index b34e43a926c47100e69909e246ad7cd328c981b4..9e2122b0b4442a4faf7da2b782d3339d406117b8 100644 --- a/src/gn/target_generator.h +++ b/src/gn/target_generator.h @@ -79,6 +79,8 @@ class TargetGenerator { UniqueVector* dest); bool FillGenericDeps(const char* var_name, LabelTargetVector* dest); + bool FillOhosComponentDeps(const char* var_name, LabelTargetVector* dest); + TargetGenerator(const TargetGenerator&) = delete; TargetGenerator& operator=(const TargetGenerator&) = delete; }; diff --git a/src/gn/value_extractors.cc b/src/gn/value_extractors.cc index 9eb759318d190fdaa2411de4a645758ec6b92d1b..4f268d483b1f5496538f96962d1791cf982e979d 100644 --- a/src/gn/value_extractors.cc +++ b/src/gn/value_extractors.cc @@ -34,6 +34,25 @@ bool ListValueExtractor(const Value& value, return true; } +// Sets the error and returns false on failure. +template +bool ListValueAppender(const Value& value, + std::vector* dest, + Err* err, + const Converter& converter) +{ + if (!value.VerifyTypeIs(Value::LIST, err)) + return false; + const std::vector& input_list = value.list_value(); + for (const auto& item : input_list) { + T new_one; + if (!converter(item, &new_one, err)) + return false; + dest->push_back(new_one); + } + return true; +} + // Like the above version but extracts to a UniqueVector and sets the error if // there are duplicates. template @@ -193,6 +212,36 @@ struct LabelPtrResolver { const Label& current_toolchain; }; +// Fills the label part of a LabelPtrPair, leaving the pointer null. +template +struct ExternalDepPtrResolver { + ExternalDepPtrResolver(const BuildSettings* build_settings_in, + const SourceDir& current_dir_in, + const Label& current_toolchain_in) + : build_settings(build_settings_in), + current_dir(current_dir_in), + current_toolchain(current_toolchain_in) {} + bool operator()(const Value& v, LabelPtrPair* out, Err* err) const + { + if (!v.VerifyTypeIs(Value::STRING, err)) { + return false; + } + std::string label; + if (!build_settings->GetExternalDepsLabel(v, label, err)) { + return false; + } + Value external_dep(v.origin(), label); + out->label = Label::Resolve(current_dir, build_settings->root_path_utf8(), + current_toolchain, external_dep, err); + out->origin = v.origin(); + out->is_external_deps = true; + return !err->has_error(); + } + const BuildSettings* build_settings; + const SourceDir& current_dir; + const Label& current_toolchain; +}; + struct LabelPatternResolver { LabelPatternResolver(const BuildSettings* build_settings_in, const SourceDir& current_dir_in) @@ -262,6 +311,17 @@ bool ExtractListOfLabels(const BuildSettings* build_settings, LabelPtrResolver(build_settings, current_dir, current_toolchain)); } +bool ExtractListOfExternalDeps(const BuildSettings* build_settings, + const Value& value, + const SourceDir& current_dir, + const Label& current_toolchain, + LabelTargetVector* dest, + Err* err) { + return ListValueAppender( + value, dest, err, + ExternalDepPtrResolver(build_settings, current_dir, current_toolchain)); +} + bool ExtractListOfUniqueLabels(const BuildSettings* build_settings, const Value& value, const SourceDir& current_dir, diff --git a/src/gn/value_extractors.h b/src/gn/value_extractors.h index 9cc9e0086d3316d5f87a07bf0fd749706430c863..057fa7eb734bf773d28fee989293d991031896f1 100644 --- a/src/gn/value_extractors.h +++ b/src/gn/value_extractors.h @@ -56,6 +56,13 @@ bool ExtractListOfLabels(const BuildSettings* build_settings, LabelTargetVector* dest, Err* err); +bool ExtractListOfExternalDeps(const BuildSettings* build_settings, + const Value& value, + const SourceDir& current_dir, + const Label& current_toolchain, + LabelTargetVector* dest, + Err* err); + // Extracts the list of labels and their origins to the given vector. For the // version taking Label*Pair, only the labels are filled in, the ptr for each // pair in the vector will be null. Sets an error and returns false if a label diff --git a/src/gn/visibility.cc b/src/gn/visibility.cc index 08789906ca5c8226742b78d497295544136c4e63..480c9937a7719a8f7a38968ddfbd4020e3120c7a 100644 --- a/src/gn/visibility.cc +++ b/src/gn/visibility.cc @@ -13,6 +13,8 @@ #include "gn/filesystem_utils.h" #include "gn/item.h" #include "gn/label.h" +#include "gn/ohos_components.h" +#include "gn/ohos_components_checker.h" #include "gn/scope.h" #include "gn/value.h" #include "gn/variables.h" @@ -88,23 +90,46 @@ std::unique_ptr Visibility::AsValue() const { } // static -bool Visibility::CheckItemVisibility(const Item* from, - const Item* to, - Err* err) { - if (!to->visibility().CanSeeMe(from->label())) { +bool Visibility::CheckItemVisibility(const Item *from, const Item *to, bool is_external_deps, Err *err) +{ std::string to_label = to->label().GetUserVisibleName(false); - *err = Err(from->defined_from(), "Dependency not allowed.", - "The item " + from->label().GetUserVisibleName(false) + - "\n" - "can not depend on " + - to_label + - "\n" - "because it is not in " + - to_label + - "'s visibility list: " + to->visibility().Describe(0, true)); - return false; - } - return true; + std::string from_label = from->label().GetUserVisibleName(false); + if (!to->visibility().CanSeeMe(from->label())) { + *err = Err(from->defined_from(), "Dependency not allowed.", + "The item " + from->label().GetUserVisibleName(false) + + "\n" + "can not depend on " + + to_label + + "\n" + "because it is not in " + + to_label + "'s visibility list: " + to->visibility().Describe(0, true)); + return false; + } + const OhosComponent *from_component = from->ohos_component(); + const OhosComponent *to_component = to->ohos_component(); + if ((from_component == nullptr) || (to_component == nullptr)) { + return true; + } + if (to_component->name() == "build_framework") { + return true; + } + if (from_component->name() == "build_framework") { + return true; + } + if (from_component == to_component) { + return true; + } + + OhosComponentChecker *checker = OhosComponentChecker::getInstance(); + if (checker != nullptr) { + if (!checker->CheckInnerApiNotLib(to, to_component, to_label, err) || + !checker->CheckInnerApiNotDeclare(to, to_component, to_label, err) || + !checker->CheckTargetAbsoluteDepsOther(from, to_component, from_label, to_label, is_external_deps, err) || + !checker->CheckInnerApiVisibilityDenied(from, to_component, from_label, to_label, err)) { + return false; + } + } + return true; } // static diff --git a/src/gn/visibility.h b/src/gn/visibility.h index a000d8267a8c695fd1330974108321d92a3df173..3c7d5b6c82f978d8c54a7878a9a59d1c931f88de 100644 --- a/src/gn/visibility.h +++ b/src/gn/visibility.h @@ -56,7 +56,7 @@ class Visibility { // Helper function to check visibility between the given two items. If // to is invisible to from, returns false and sets the error. - static bool CheckItemVisibility(const Item* from, const Item* to, Err* err); + static bool CheckItemVisibility(const Item* from, const Item* to, bool is_external_deps, Err* err); // Helper function to fill an item's visibility from the "visibility" value // in the current scope.