diff --git a/config/BUILDCONFIG.gn b/config/BUILDCONFIG.gn index dcdca67083b99938602ca832f5482921f6a0fd63..ebde0d7d89a79dabdf0dadf35294a48a8ca2f389 100755 --- a/config/BUILDCONFIG.gn +++ b/config/BUILDCONFIG.gn @@ -101,6 +101,14 @@ declare_args() { rustc_codecheck = false } +declare_args() { + # Whether to use the sandbox + use_sandbox = false + + # enable sandbox debug mode + sandbox_debug = false +} + product_build_config = read_file("${preloader_output_dir}/build_config.json", "json") @@ -1069,13 +1077,13 @@ foreach(_target_type, target_type_list) { } } -if (is_lite_system && current_os == "ohos") { - _target_type_list = [ - "action", - "action_foreach", - ] +action_target_type_list = [ + "action", + "action_foreach", +] - foreach(_target_type, _target_type_list) { +if (is_lite_system && current_os == "ohos") { + foreach(_target_type, action_target_type_list) { template(_target_type) { target(_target_type, target_name) { forward_variables_from(invoker, "*", [ "no_default_deps" ]) @@ -1089,3 +1097,96 @@ if (is_lite_system && current_os == "ohos") { } } } + +import("//build/sandbox/sandbox.gni") + +if (is_standard_system && use_sandbox) { + no_sandbox_script_list = + rebase_path(sandbox_config.no_sandbox_script_list, "//") + + foreach(_target_type, action_target_type_list) { + template(_target_type) { + script_path = rebase_path(invoker.script, "//") + target_label = get_label_info(":${target_name}", "label_no_toolchain") + + if (sandbox_debug) { + print("action label: ${target_label}, script: ${script_path}") + } + + if (filter_include(no_sandbox_script_list, [ script_path ]) != []) { + target(_target_type, target_name) { + forward_variables_from(invoker, "*", [ "no_default_deps" ]) + if (!defined(depfile) || + filter_include(args, [ rebase_path(depfile, root_build_dir) ]) == + []) { + # Trusted script that do not use the sandbox need to configure the depfile field and pass to script + print("WARNING: ${target_label} not set depfile or not pass args") + } else { + not_needed([ "target_label" ]) + } + } + } else { + metadata_target = "${target_name}__sandbox_metadata" + metadata_file = "${target_out_dir}/${metadata_target}.json" + + target(_target_type, target_name) { + forward_variables_from(invoker, + [ + "testonly", + "visibility", + "outputs", + ]) + forward_variables_from(invoker, + "*", + [ + "no_default_deps", + "script", + "args", + ]) + + script = sandbox_config.sandbox_script + args = [ + "--sandbox-metadata", + rebase_path(metadata_file, root_build_dir), + "--script-file", + rebase_path(invoker.script, root_build_dir), + ] + + if (sandbox_debug) { + args += [ "--debug" ] + } + + if (defined(invoker.args) && invoker.args != []) { + args += [ "--" ] + args += invoker.args + } + + if (!defined(deps)) { + deps = [] + } + deps += [ ":${metadata_target}" ] + } + + action_output = + rebase_path(get_target_outputs(":${target_name}"), root_build_dir) + sandbox_metadata("${metadata_target}") { + forward_variables_from(invoker, + [ + "custom_mount_dir", + "inputs", + "sources", + ]) + if (defined(invoker.depfile)) { + depfile = invoker.depfile + } else { + if (_target_type == "action_foreach") { + depfile = "${target_gen_dir}/{{source_name_part}}__sandbox.d" + } else { + depfile = "${target_gen_dir}/${target_name}__sandbox.d" + } + } + } + } + } + } +} diff --git a/ohos/app/app.gni b/ohos/app/app.gni index c567bd7c17c1b6121c7af654957fdfd794dfe3a9..9d3edd8018a6c6f2040d8ae3e07302f1698f16e8 100644 --- a/ohos/app/app.gni +++ b/ohos/app/app.gni @@ -213,9 +213,10 @@ if (app_need_publicity) { action(target_name) { script = "//build/scripts/collect_publicity.py" - module_type = "app" + depfile = "${target_gen_dir}/${target_name}.d" if (defined(module_install_dir)) { not_needed([ "relative_install_dir" ]) + module_type = "app" install_dir = string_replace(module_install_dir, "${module_type}/", "") } else if (defined(relative_install_dir)) { install_dir = relative_install_dir @@ -227,7 +228,10 @@ if (app_need_publicity) { print( "${invoker.module_label} not set install dir, empty install dir is invalid.") } - args = [] + args = [ + "--depfile", + rebase_path(depfile, root_build_dir), + ] if (defined(invoker.publicity_file)) { inputs = [ invoker.publicity_file ] source = inputs[0] @@ -269,8 +273,10 @@ template("ohos_app") { script = "//build/scripts/check_hvigor_hap.py" outputs = [ "${target_out_dir}/${target_name}.txt" ] inputs = [ "${preloader_output_dir}/hvigor_compile_hap_whitelist.json" ] - + depfile = "${target_gen_dir}/${target_name}.d" args = [ + "--depfile", + rebase_path(depfile, root_build_dir), "--target-path", target_path, "--output", @@ -356,7 +362,8 @@ template("ohos_app") { module_label = get_label_info(":${target_name}", "label_with_toolchain") _collect_target = "${target_name}__collect" - collect_module_target(_collect_target) {} + collect_module_target(_collect_target) { + } if (!defined(install_enable)) { install_enable = true @@ -364,7 +371,10 @@ template("ohos_app") { if (!_test_target) { if (defined(install_images)) { - app_allow_images = [ "system", "cloud_rom" ] + app_allow_images = [ + "system", + "cloud_rom", + ] allow_image = filter_exclude(install_images, app_allow_images) assert( allow_image == [], @@ -473,7 +483,7 @@ template("ohos_app") { if (defined(invoker.modules_filter) && invoker.modules_filter) { module_filter = true } - + ohos_test_coverage = false if (defined(invoker.ohos_test_coverage) && invoker.ohos_test_coverage) { ohos_test_coverage = invoker.ohos_test_coverage diff --git a/ohos/generate_part_info.py b/ohos/generate_part_info.py index b6b9403f1f3e3ffeea0ef46a3ec3e856d9aee952..067eeb5a9970e06cada8c006fd3c741ef0e56dd9 100755 --- a/ohos/generate_part_info.py +++ b/ohos/generate_part_info.py @@ -19,6 +19,7 @@ import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from scripts.util.file_utils import read_json_file, write_json_file # noqa: E402 E501 +from scripts.util.build_utils import write_depfile # noqa: E402 E501 # read subsystem module, generate subsystem install list and deps list. @@ -124,6 +125,14 @@ def main(): args.sdk_modules_info_file, args.output_install_file, args.output_deps_file, args.output_host_file, args.current_toolchain, args.host_toolchain) + + if args.depfile: + _dep_files = [args.input_file] + if os.path.exists(args.sdk_modules_info_file): + _dep_files.append(args.sdk_modules_info_file) + + write_depfile(args.depfile, args.output_install_file, _dep_files, add_pydeps=False) + return 0 diff --git a/ohos/notice/collect_module_notice_file.py b/ohos/notice/collect_module_notice_file.py index 104875a442d6bc6bfb71587520a2548c8bf37a51..a6c9a7c3de3ca6f4e091d8f4b3019b8ce23c6b2a 100755 --- a/ohos/notice/collect_module_notice_file.py +++ b/ohos/notice/collect_module_notice_file.py @@ -104,7 +104,7 @@ def get_license_from_readme(readme_path: str): notice_files.append(content.get('License File').strip()) notice_names.append(content.get('Name').strip()) notice_versions.append(content.get('Version Number').strip()) - + if notice_files is None: raise Exception("Error: value of notice file is empty in {}.".format( readme_path)) @@ -137,7 +137,7 @@ def add_path_to_module_notice(module_notice_info, module_notice_info_list, optio module_notice_info["Path"] = module_path.replace("../", "") -def do_collect_notice_files(options, depfiles: str): +def do_collect_notice_files(options, depfiles: list): module_notice_info_list = [] module_notice_info = {} notice_file = options.license_file @@ -178,7 +178,7 @@ def do_collect_notice_files(options, depfiles: str): else: module_notice_info['Software'] = "" module_notice_info['Version'] = "" - + add_path_to_module_notice(module_notice_info, module_notice_info_list, options) if notice_file: @@ -203,10 +203,10 @@ def write_file_content(notice_files, options, output, notice_info_json, module_n build_utils.touch(output) write_notice_to_output(notice_file, output) write_json_file(notice_info_json, module_notice_info_list) + depfiles.append(notice_file) else: build_utils.touch(output) build_utils.touch(notice_info_json) - depfiles.append(notice_file) def write_notice_to_output(notice_file, output): @@ -263,4 +263,3 @@ def main(args): if __name__ == '__main__': sys.exit(main(sys.argv[1:])) - diff --git a/ohos/ohos_kits.gni b/ohos/ohos_kits.gni index 26fa7b4028ad48c8fbe4deb8cf7179a6707a6fad..e4f9b69c80bc030867aaf821fdd8d5704eae0ccf 100755 --- a/ohos/ohos_kits.gni +++ b/ohos/ohos_kits.gni @@ -183,7 +183,10 @@ template("_ohos_subsystem_sdk") { sdk_build_file, sdk_info_file, ] + depfile = "${target_gen_dir}/${target_name}.d" args = [ + "--depfile", + rebase_path(depfile, root_build_dir), "--input-file", rebase_path(subsystem_sdk_desc_file, root_build_dir), "--sdk-out-dir", diff --git a/ohos/ohos_part.gni b/ohos/ohos_part.gni index 57278ce6048994ecd8793615b4b2e3da12df03cd..2170d420ee959daa38c99d9f36ef1ab4addfdcab 100755 --- a/ohos/ohos_part.gni +++ b/ohos/ohos_part.gni @@ -110,11 +110,14 @@ template("ohos_part") { deps = [ ":${part_name}_info" ] script = "//build/ohos/generate_part_info.py" sources = [ part_modules_info_file ] + depfile = "${target_gen_dir}/${target_name}.d" outputs = [ part_install_modules_file, part_dep_modules_file, ] args = [ + "--depfile", + rebase_path(depfile, root_build_dir), "--part-name", part_name, "--origin-part-name", diff --git a/sandbox/sandbox.gni b/sandbox/sandbox.gni new file mode 100644 index 0000000000000000000000000000000000000000..eebee91e433502573b4beecad997fdeafe5196f3 --- /dev/null +++ b/sandbox/sandbox.gni @@ -0,0 +1,26 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if (use_sandbox) { + if (!defined(build_ext_path)) { + import("//build/sandbox/sandbox_metadata.gni") + } else { + import("${build_ext_path}/sandbox/sandbox_metadata.gni") + } + + assert(defined(sandbox_config), "must defined sandbox_config") + assert(defined(sandbox_config.no_sandbox_script_list), + "must defined sandbox_config.no_sandbox_script_list") + assert(sandbox_config.sandbox_script != "", + "must set sandbox_config.sandbox_script") +} diff --git a/sandbox/sandbox.sh b/sandbox/sandbox.sh new file mode 100755 index 0000000000000000000000000000000000000000..7806bbd544d79dae45dd7a41bbe5e48f6f685ba8 --- /dev/null +++ b/sandbox/sandbox.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Copyright (c) 2025 Huawei Device Co., Ltd. +# provide a sandbox script to isolate inputs dir for action task +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +while [[ $# -gt 0 ]]; do + case "$1" in + --script-file) + shift + script_file="$1" + ;; + --sandbox-metadata) + shift + sandbox_metadata="$(realpath "$1")" + ;; + --debug) + # current only print debug mode hint, can remain sandbox directory to debug + echo "execute sandbox with debug mode" + ;; + --) + shift + break + ;; + *) + echo "invalid option param $1" + exit 1 + ;; + esac + shift +done + +check_param() { + if [ ! -f "${script_file}" ]; then + echo "action script: ${script_file} not exists." + exit 1 + fi + + if [ ! -f "${sandbox_metadata}" ]; then + echo "action metadata file: ${sandbox_metadata} not exists." + exit 1 + fi +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + check_param + + exec_param="/usr/bin/env" + + action_cmd="${exec_param} ${script_file} " + action_cmd+=$(printf "%q " "$@") + + # directory isolation based on namespace, mount and chroot, currnt only print origin action command + echo "${action_cmd}" + ${action_cmd} +fi diff --git a/sandbox/sandbox_metadata.gni b/sandbox/sandbox_metadata.gni new file mode 100644 index 0000000000000000000000000000000000000000..4d4020331fb0317c4dbf303c986fad0f3097a4e5 --- /dev/null +++ b/sandbox/sandbox_metadata.gni @@ -0,0 +1,51 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +sandbox_config = { + sandbox_script = "//build/sandbox/sandbox.sh" + no_sandbox_script_list = [] +} + +template("sandbox_metadata") { + forward_variables_from(invoker, + [ + "custom_mount_dir", + "inputs", + "sources", + ]) + + group(target_name) { + # filled metadata required for sandbox_script, you can filled more + depfile_path = rebase_path(invoker.depfile, root_build_dir) + + metadata_info = { + depfile_path = depfile_path + outputs = invoker.action_output + target_label = invoker.target_label + } + + if (defined(inputs) && inputs != []) { + metadata_info.inputs = rebase_path(inputs, root_build_dir) + } + + if (defined(sources) && sources != []) { + metadata_info.sources = rebase_path(sources, root_build_dir) + } + + if (defined(custom_mount_dir) && custom_mount_dir != []) { + metadata_info.custom_mount_dir = + rebase_path(custom_mount_dir, root_build_dir) + } + write_file(invoker.metadata_file, metadata_info, "json") + } +} diff --git a/scripts/check_hvigor_hap.py b/scripts/check_hvigor_hap.py index 883916ca5d20a4e919dbcaba7b0cfcc152fe5723..05c9cc4b9b4af8d6db70dd55568a117158201f13 100755 --- a/scripts/check_hvigor_hap.py +++ b/scripts/check_hvigor_hap.py @@ -18,7 +18,7 @@ import os import sys from util.file_utils import read_json_file -from util.build_utils import touch, check_instance +from util.build_utils import touch, check_instance, write_depfile def read_hap_file(file_path: str): @@ -49,7 +49,7 @@ def check_sdk_version(args, hvigor_compile_hap_allow_info): def check_hvigor_version(args, hvigor_compile_hap_allow_info): if not args.hvigor_home or not hvigor_compile_hap_allow_info: return 0 - + hvigor_allow_version_list = hvigor_compile_hap_allow_info.get("hvigor_version") check_instance(hvigor_allow_version_list, "hvigor_version", dict) @@ -57,10 +57,10 @@ def check_hvigor_version(args, hvigor_compile_hap_allow_info): if args.hvigor_home not in hvigor_allow_version_list: return 0 - + if args.target_path not in hvigor_allow_version_list.get(args.hvigor_home): raise Exception(message) - + return 0 @@ -71,6 +71,7 @@ def main(): parser.add_argument('--hvigor-compile-hap-allow-file', required=True) parser.add_argument('--sdk-home', help='sdk home') parser.add_argument('--hvigor-home', help='hvigor home') + parser.add_argument('--depfile', required=False) args = parser.parse_args() # read hvigor compile hap allow list file @@ -83,6 +84,9 @@ def main(): check_hvigor_version(args, hvigor_compile_hap_allow_info) touch(args.output) + if args.depfile: + _dep_files = [args.hvigor_compile_hap_allow_file] + write_depfile(args.depfile, args.output, _dep_files, add_pydeps=False) return 0 diff --git a/scripts/collect_publicity.py b/scripts/collect_publicity.py index 703bb2eae1eb03dd90770eb550edee81f28689a0..7bac9fe88f237e80716400e1b35bf1d21c1b30b1 100755 --- a/scripts/collect_publicity.py +++ b/scripts/collect_publicity.py @@ -18,7 +18,7 @@ import os import sys import shutil -from util.build_utils import touch +from util.build_utils import touch, write_depfile def parse_args(args): @@ -26,6 +26,7 @@ def parse_args(args): parser.add_argument("--source", help="specify publicity xml") parser.add_argument("--output", required=True, help="specify publicity output") + parser.add_argument("--depfile", required=False, help="") options = parser.parse_args(args) return options @@ -34,6 +35,7 @@ def parse_args(args): def main(args): options = parse_args(args) + _dep_files = [] dest_dir = os.path.dirname(options.output) if not os.path.exists(dest_dir): os.makedirs(dest_dir, exist_ok=True) @@ -41,12 +43,16 @@ def main(args): if os.path.exists(options.output): os.unlink(options.output) - if not options.source: + if options.source: + _dep_files.append(options.source) + shutil.copy2(options.source, options.output) + else: # touch an empty publicity.xml touch(options.output) - return 0 - shutil.copy2(options.source, options.output) + if options.depfile: + write_depfile(options.depfile, options.output, _dep_files, add_pydeps=False) + return 0 diff --git a/scripts/gen_sdk_build_file.py b/scripts/gen_sdk_build_file.py index 8b28f050c982db98034dffb292e72215d8080a10..15022966122bf97073d3b1ae8177a97f0a46054c 100755 --- a/scripts/gen_sdk_build_file.py +++ b/scripts/gen_sdk_build_file.py @@ -233,11 +233,17 @@ def main(): parser.set_defaults(generate_sig=False) parser.add_argument('--signature-file-check-dir', help='', required=False) parser.add_argument('--signature-file-gen-dir', help='', required=False) + parser.add_argument('--depfile', help='', required=False) args = parser.parse_args() generate_sdk(args.input_file, args.sdk_out_dir, args.output_build_file, args.sdk_info_file, args.generate_sig, args.signature_file_check_dir, args.signature_file_gen_dir) + + if args.depfile: + _dep_files = [] + _dep_files.append(args.input_file) + build_utils.write_depfile(args.depfile, args.output_build_file, _dep_files, add_pydeps=False) return 0 diff --git a/templates/common/check_build_target.py b/templates/common/check_build_target.py index 388523db0b6e735cbf399d14339dfe7dc2acf425..1bfc4b13b7eaa69b5e6adf7a1fcd2f50e75318cf 100755 --- a/templates/common/check_build_target.py +++ b/templates/common/check_build_target.py @@ -20,10 +20,10 @@ import argparse import check_deps_handler import check_external_deps import check_part_subsystem_name -sys.path.append( - os.path.dirname(os.path.dirname(os.path.dirname( - os.path.abspath(__file__))))) + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from scripts.util.build_utils import write_depfile, add_depfile_option, touch # noqa: E402 +from scripts.util.file_utils import read_json_file # noqa: E402 def parse_args(): @@ -43,25 +43,46 @@ def parse_args(): return args +def get_depfile_info(part_name: str, source_root_dir: str) -> list: + depfile = [] + parts_path_file = 'build_configs/parts_info/parts_path_info.json' + parts_path_info = read_json_file(parts_path_file) + if not parts_path_info: + raise Exception("read pre_build parts_path_info failed") + + part_path = parts_path_info.get(part_name) + if not part_path: + return depfile + + bundle_json_file = os.path.join(source_root_dir, part_path, "bundle.json") + if os.path.exists(bundle_json_file): + depfile.append(bundle_json_file) + + return depfile + + def main(): args = parse_args() - depfiles = [] + add_depfile = False if not args.skip_check_subsystem: - _depfile = check_part_subsystem_name.check(args) - depfiles.extend(_depfile) + check_part_subsystem_name.check(args) + add_depfile = True if args.deps: - _depfile = check_deps_handler.check(args) - depfiles.extend(_depfile) + check_deps_handler.check(args) + add_depfile = True if args.external_deps: - _depfile = check_external_deps.check(args) - depfiles.extend(_depfile) + check_external_deps.check(args) + add_depfile = True + + depfiles = [] + if add_depfile: + depfiles.extend(get_depfile_info(args.part_name, args.source_root_dir)) - if depfiles: - depfiles = list(set(depfiles)) - write_depfile(args.depfile, args.output, depfiles) + if args.depfile: + write_depfile(args.depfile, args.output, depfiles, add_pydeps=False) touch(args.output) diff --git a/templates/metadata/gen_module_info.py b/templates/metadata/gen_module_info.py index 2866946c20c48f397e1af3b98d26ec051216b63b..4de40e62c543ce9a1bfdc47a3f7a5a38a9699723 100755 --- a/templates/metadata/gen_module_info.py +++ b/templates/metadata/gen_module_info.py @@ -21,6 +21,7 @@ sys.path.append( os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) from scripts.util.file_utils import write_json_file # noqa: E402 +from scripts.util.build_utils import write_depfile # noqa: E402 def get_source_name(module_type, name, prefix_override, suffix, @@ -194,6 +195,7 @@ def main(): dest='prefix_override', action='store_false') parser.set_defaults(prefix_override=False) + parser.add_argument('--depfile', required=False) args = parser.parse_args() module_source = '' @@ -234,6 +236,10 @@ def main(): # write module info file write_json_file(args.output_file, module_info_data) + + if args.depfile: + _dep_files = [] + write_depfile(args.depfile, args.output_file, _dep_files, add_pydeps=False) return 0 diff --git a/templates/metadata/module_info.gni b/templates/metadata/module_info.gni index eeed5828e36c5665367290b6c5ac43ac38b01896..436f9cb4c06b8b67c8c23f1dfbb05309a2601b8d 100755 --- a/templates/metadata/module_info.gni +++ b/templates/metadata/module_info.gni @@ -97,6 +97,7 @@ template("generate_module_info") { } action(target_name) { + depfile = "${target_gen_dir}/${target_name}.d" args = [ "--system-base-dir", system_base_dir, @@ -128,6 +129,8 @@ template("generate_module_info") { module_name, "--output-file", rebase_path(output_file, "$root_build_dir"), + "--depfile", + rebase_path(depfile, root_build_dir), ] if (module_install_name != "") {