• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2024 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import requests
19import json
20import os
21import subprocess
22import shutil
23import glob
24import sys
25import argparse
26import stat
27import shlex
28from itertools import chain
29
30CURRENT_DIRECTORY = os.path.abspath(os.getcwd())
31
32
33def replace_part_sofile(part_name, old_folder, new_folder):
34    components_json = _get_components_json(os.path.join(old_folder, 'out', 'rk3568'))
35    parts_path_info = _get_parts_path_info(components_json)
36    part_path = _get_parts_path(parts_path_info, part_name)
37    _source_list = _get_system_module_info(old_folder, part_name)
38    copy_component_sofile(_source_list, part_name, old_folder, new_folder)
39
40
41def copy_component_sofile(_source_list, part_name, old_folder, new_folder):
42    for source_info in _source_list:
43        so_path = source_info.get("source")
44        indep_so_path = os.path.join(new_folder, 'out', 'default', 'src', so_path)
45        so_dir = os.path.dirname(so_path)
46        old_so_folder = os.path.join(old_folder, 'out', 'rk3568', so_dir)
47        if os.path.exists(indep_so_path):
48            try:
49                shutil.copy(indep_so_path, old_so_folder)
50            except shutil.SameFileError:
51                print(f"Cannot copy '{indep_so_path}' to '{old_so_folder}' because they are the same file.")
52            print(f'{part_name}:{so_path} done')
53
54
55def _get_system_module_info(folder, part_name):
56    _part_system_module_path = os.path.join(folder, 'out', 'rk3568', 'packages', 'phone',
57                                            'system_module_info.json')
58    _source_list = []
59    with os.fdopen(os.open(_part_system_module_path, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR),
60                   'r', encoding='utf-8') as f:
61        jsondata = json.load(f)
62    for _dict in jsondata:
63        for k, v in _dict.items():
64            if v == part_name:
65                _source_list.append(_dict)
66
67    return _source_list
68
69
70def _get_subsystem_name(folder, part_name):
71    _part_subsystem_json_path = os.path.join(folder, 'out', 'rk3568', 'build_configs', 'parts_info',
72                                             'part_subsystem.json')
73    with os.fdopen(os.open(_part_subsystem_json_path, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR),
74                   'r', encoding='utf-8') as f:
75        jsondata = json.load(f)
76        subsystem_name = jsondata.get(part_name)
77    return subsystem_name
78
79
80def _get_components_json(out_path):
81    jsondata = ""
82    json_path = os.path.join(out_path + "/build_configs/parts_info/components.json")
83    with os.fdopen(os.open(json_path, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR),
84                   'r', encoding='utf-8') as f:
85        try:
86            jsondata = json.load(f)
87        except Exception as e:
88            print(f"--_get_components_json parse json error--: {e}")
89    return jsondata
90
91
92def _get_parts_path_info(components_json):
93    jsondata = dict()
94    try:
95        for component, v in components_json.items():
96            jsondata[component] = v.get('path')
97    except Exception as e:
98        print(f"--_get_part_subsystem parse json error--: {e}")
99    return jsondata
100
101
102def _get_parts_path(json_data, part_name):
103    parts_path = None
104    if json_data.get(part_name) is not None:
105        parts_path = json_data[part_name]
106    return parts_path
107
108
109def _get_export_project(export):
110    if export != 'project_list':
111        print(f"Unsupported export key: {export}. Expected 'project_list'.")
112        return []
113    export_button = os.getenv(export)
114    if export_button is None or export_button.strip() == '':
115        print(f"The environment variable {export} is not set or is empty.")
116        return []
117    list_export = export_button.split(',')
118    return list_export
119
120
121def _get_export_files(export):
122    if export == 'PR_FILE_PATHS':
123        export_button = os.getenv(export)
124        if export_button is None:
125            print('The environment variable PR_FILE_PATHS is not set')
126            return None
127        return export_button
128    else:
129        print(f"Unsupported export type: {export}")
130        return None
131
132
133def _handle_successful_response(ret):
134    try:
135        mkdirs_text = json.loads(ret.text)
136        print("The request was successful:", mkdirs_text)
137        return mkdirs_text
138    except json.JSONDecodeError:
139        print("The response is not in valid JSON format.")
140        return None
141
142
143def _get_api_mkdir(params):
144    post_url = "http://ci.openharmony.cn/api/sple/external/artifact/repo?projectName=openharmony"
145    fails = 0
146    while True:
147        try:
148            if fails >= 5:
149                print("The number of failed requests is excessive")
150                break
151            headers = {'Content-Type': 'application/json'}
152            ret = requests.post(post_url, data=json.dumps(params), headers=headers, timeout=10)
153            if ret.status_code == 200:
154                return _handle_successful_response(ret)
155            else:
156                print(f"The request failed, and the status code is displayed: {ret.status_code}, message: {ret.text}")
157            fails += 1
158        except requests.RequestException as e:
159            print(f"Request Exception: {e}")
160            fails += 1
161
162
163def _get_part_list():
164    list_url = "https://ci.openharmony.cn/api/daily_build/component/check/list"
165    fails = 0
166    while True:
167        try:
168            if fails >= 5:
169                print("Failed to retrieve the component list after multiple attempts.")
170                return None
171            ret = requests.get(url=list_url, timeout=10)
172            if ret.status_code == 200:
173                list_text = json.loads(ret.text)
174                return list_text
175            else:
176                print(f"Received status code {ret.status_code}, retrying...")
177                fails += 1
178        except requests.RequestException as e:
179            print(f"Request error: {e}, attempting to request again...")
180            fails += 1
181        except json.JSONDecodeError:
182            print("Failed to decode JSON response, attempting to request again...")
183            fails += 1
184        except Exception as e:
185            print(f"An unexpected error occurred: {e}, attempting to request again...")
186            fails += 1
187
188
189def _handle_single_component_case(path, component):
190    if path not in {'drivers_interface', 'drivers_peripheral'}:
191        return list(component.keys())
192    return []
193
194
195def _handle_multiple_component_case(component, files_json):
196    all_parts = []
197    for driver, driver_path in component.items():
198        if driver_path:
199            matched_parts = _match_files_with_driver(driver_path, files_json)
200            all_parts.extend(matched_parts)
201    return all_parts
202
203
204def _get_dep_parts(mkdirs, files):
205    all_parts = []
206    files_json = json.loads(files)
207    for path, component in mkdirs.items():
208        if not component:
209            continue
210        elif len(component) == 1:
211            all_parts.extend(_handle_single_component_case(path, component))
212        else:
213            all_parts.extend(_handle_multiple_component_case(component, files_json))
214    return all_parts
215
216
217def _match_files_with_driver(driver_path, files_json):
218    matched_parts = []
219    parts_s = driver_path.split('/')
220    remaining_parts = '/'.join(parts_s[2:]) if len(parts_s) > 2 else ''
221    for driver_name, files_list in files_json.items():
222        for file in files_list:
223            if file.startswith(remaining_parts):
224                matched_parts.append(driver_name)
225                break
226    return matched_parts
227
228
229def symlink_src2dest(src_dir, dest_dir):
230    if os.path.exists(dest_dir):
231        if os.path.islink(dest_dir):
232            os.unlink(dest_dir)
233        elif os.path.isdir(dest_dir):
234            shutil.rmtree(dest_dir)
235        else:
236            os.remove(dest_dir)
237    else:
238        os.makedirs(os.path.dirname(dest_dir), exist_ok=True)
239
240    print("symlink {} ---> {}".format(src_dir, dest_dir))
241    os.symlink(src_dir, dest_dir)
242
243
244def install_hpm(code_path, download_dir, symlink_dir, home_path):
245    content = """\
246package-lock=true
247registry=http://repo.huaweicloud.com/repository/npm
248strict-ssl=false
249lockfile=false
250"""
251    with os.fdopen(os.open(os.path.join(home_path, '.npmrc'), os.O_WRONLY | os.O_CREAT, mode=0o640), 'w') as f:
252        os.truncate(f.fileno(), 0)
253        f.write(content)
254    if not os.path.exists(download_dir):
255        os.makedirs(download_dir)
256    with os.fdopen(os.open(os.path.join(download_dir, 'package.json'), os.O_WRONLY | os.O_CREAT, mode=0o640), 'w') as f:
257        os.truncate(f.fileno(), 0)
258        f.write('{}\n')
259    npm_path = os.path.join(code_path, "prebuilts/build-tools/common/nodejs/current/bin/npm")
260    node_bin_path = os.path.join(code_path, "prebuilts/build-tools/common/nodejs/current/bin")
261    os.environ['PATH'] = f"{node_bin_path}:{os.environ['PATH']}"
262    subprocess.run(
263        [npm_path, 'install', '@ohos/hpm-cli', '--registry', 'https://repo.huaweicloud.com/repository/npm/', '--prefix',
264         download_dir])
265    symlink_src2dest(os.path.join(download_dir, 'node_modules'), symlink_dir)
266
267
268def _prebuild_build():
269    script_path = './build/prebuilts_download.sh'
270    try:
271        result = subprocess.run([script_path], check=True, text=True)
272        print("Script completed successfully with return code:", result.returncode)
273    except subprocess.CalledProcessError as e:
274        print("Script failed with return code:", e.returncode)
275        print(e.stderr)
276    commands = [
277        ["pip3", "install", "--upgrade", "pip", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple"],
278        ["pip3", "install", "--upgrade", "jinja2", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple"],
279        ["pip3", "install", "--upgrade", "markupsafe", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple"]
280    ]
281    for cmd in commands:
282        try:
283            result = subprocess.run(cmd, check=True)
284            print(f"Command '{cmd}' completed successfully with return code {result.returncode}")
285        except subprocess.CalledProcessError as e:
286            print(f"Command '{cmd}' failed with return code {e.returncode}")
287    paths_to_remove = [
288        "/usr/local/lib/python3.8/dist-packages/hb",
289        "/usr/local/bin/hb"
290    ]
291    install_command = ["python3", "-m", "pip", "install", "--user", "./build/hb"]
292    for path in paths_to_remove:
293        try:
294            subprocess.run(["rm", "-rf", path], check=True)
295            print(f"Successfully removed {path}")
296        except subprocess.CalledProcessError as e:
297            print(f"Failed to remove {path} with return code {e.returncode}")
298    try:
299        subprocess.run(install_command, check=True)
300        print("hb installation completed successfully")
301    except subprocess.CalledProcessError as e:
302        print(f"hb installation failed with return code {e.returncode}")
303    install_hpm('./', '/root/.prebuilts_cache/hpm/', './prebuilts/hpm/node_modules', '/root')
304
305
306def _hb_build(part):
307    new_path = os.path.expanduser('/root/.prebuilts_cache/hpm/node_modules/.bin')
308    if new_path not in os.environ['PATH'].split(os.pathsep):
309        os.environ['PATH'] = f"{new_path}{os.pathsep}{os.environ['PATH']}"
310    try:
311        subprocess.run(['hb', 'build', part, '-i'], check=True, text=True)
312        print("hb build successfully")
313    except subprocess.CalledProcessError as e:
314        print(f"Error: The command 'hb build {part} -i' failed with return code {e.returncode}.")
315    command = [
316        'python',
317        './build/templates/common/generate_component_package.py',
318        '-rp', './',
319        '-op', './out/default/src',
320        '-lt', '1',
321        '-cl', part
322    ]
323    try:
324        result = subprocess.run(command, check=True, capture_output=True, text=True)
325        print(result.stdout)
326    except subprocess.CalledProcessError as e:
327        print(f"Command '{e.cmd}' returned non-zero exit status {e.returncode}.")
328        print(f"Error output: {e.stderr}")
329
330
331def _get_subsystem_names(part, parts_info_path):
332    with open(parts_info_path, 'r', encoding='utf-8') as info_json:
333        parts_info_json = json.load(info_json)
334    for part_list in parts_info_json.values():
335        for part_info in part_list:
336            if part_info['part_name'] == part:
337                return part_info['subsystem_name']
338    return None
339
340
341def _get_publicinfo_paths(subsystem_name, current_directory):
342    publicinfo_path = os.path.join(current_directory, 'out', 'default', 'src', subsystem_name, '*', 'publicinfo')
343    publicinfo_files = glob.glob(os.path.join(publicinfo_path, '*.json'), recursive=True)
344    all_publicinfo_paths = set()
345    for publicinfo_file in publicinfo_files:
346        with open(publicinfo_file, 'r', encoding='utf-8') as f:
347            publicinfo_json = json.load(f)
348            all_publicinfo_paths.update(_process_public_configs(publicinfo_json))
349    return all_publicinfo_paths
350
351
352def _process_public_configs(publicinfo_json):
353    publicinfo_paths = set()
354    if 'public_configs' in publicinfo_json and isinstance(publicinfo_json['public_configs'], list):
355        base_path = publicinfo_json.get('path', '').rstrip('/') + '/'
356        for public_config in publicinfo_json['public_configs']:
357            if 'include_dirs' in public_config and isinstance(public_config['include_dirs'], list):
358                publicinfo_paths.update(_process_include_dirs(base_path, public_config['include_dirs']))
359    return publicinfo_paths
360
361
362def _process_include_dirs(base_path, include_dirs):
363    processed_paths = set()
364    for include_dir in include_dirs:
365        if include_dir.startswith(base_path):
366            processed_paths.add(include_dir[len(base_path):])
367    return processed_paths
368
369
370def _check_if_file_modifies_inner_api(change_file, publicinfo_paths):
371    for publicinfo_path in publicinfo_paths:
372        if change_file.startswith(publicinfo_path):
373            return True
374    return False
375
376
377def _check_inner_api(part, files, current_directory):
378    parts_info_path = os.path.join(current_directory, 'out', 'rk3568', 'build_configs', 'parts_info', 'parts_info.json')
379    subsystem_name = _get_subsystem_names(part, parts_info_path)
380    if not subsystem_name:
381        print(f"The subsystem name for '{part}' is not found.")
382        return False
383    print(f"The subsystem name for '{part}' is: {subsystem_name}")
384
385    publicinfo_paths = _get_publicinfo_paths(subsystem_name, current_directory)
386    if not publicinfo_paths:
387        print("No publicinfo paths found.")
388        return False
389    print(f"The publicinfo paths are: {publicinfo_paths}")
390
391    files_json = json.loads(files)
392    for parts_p, change_files in files_json.items():
393        if any(_check_if_file_modifies_inner_api(change_file, publicinfo_paths) for change_file in change_files):
394            print('The modification involves the inner API.')
395            return True
396    return False
397
398
399def _create_datapart_json(alternative, changed):
400    data = {
401        'build.type': alternative,
402        'innerAPI.changed': changed
403    }
404    json_str = json.dumps(data, indent=4)
405    output_dir = os.path.join(CURRENT_DIRECTORY, 'out')
406    output_file = 'dataPart.json'
407    output_path = os.path.join(output_dir, output_file)
408    if not os.path.exists(output_dir):
409        os.makedirs(output_dir)
410    with open(output_path, 'w') as f:
411        f.write(json_str)
412
413
414def _remove_directories(directory, exclude_pattern):
415    try:
416        result = subprocess.run(['ls', directory], check=True, capture_output=True, text=True)
417        contents = result.stdout.strip().split('\n')
418        for content in contents:
419            full_path = os.path.join(directory, content)
420            if os.path.isdir(full_path) and exclude_pattern not in content:
421                subprocess.run(['rm', '-rf', full_path], check=True)
422    except subprocess.CalledProcessError as e:
423        print(f"Command '{e.cmd}' returned non-zero exit status {e.returncode}.")
424
425
426def _build_pre_compile():
427    pr_list = os.getenv('pr_list')
428    if not pr_list:
429        _remove_prebuilts()
430    _install_dependencies()
431    if os.path.exists('out'):
432        _clean_out_directory(exclude_pattern='kernel')
433    else:
434        print("'out' directory does not exist, skipping cleanup.")
435    _remove_tar_gz_files_in_prebuilts()
436    print("Pre-compile step completed successfully.")
437
438
439def _remove_prebuilts():
440    subprocess.run(['rm', '-rf', 'prebuilts/ohos-sdk'], check=True)
441    subprocess.run(['rm', '-rf', 'prebuilts/build-tools/common/oh-command-line-tools'], check=True)
442
443
444def _install_dependencies():
445    subprocess.run(
446        ['sudo', 'apt-get', 'install', '-y', 'libxinerama-dev', 'libxcursor-dev', 'libxrandr-dev', 'libxi-dev'],
447        check=True)
448    subprocess.run(['sudo', 'apt-get', 'install', '-y', 'gcc-multilib'], check=True)
449
450
451def _clean_out_directory(exclude_pattern):
452    _remove_directories('out', exclude_pattern)
453
454
455def _remove_tar_gz_files_in_prebuilts():
456    subprocess.run(['rm', '-rf', './prebuilts/*.tar.gz'], check=True)
457
458
459def _build_dayu200():
460    _build_pre_compile()
461    current_env = os.environ.copy()
462    current_env['CCACHE_BASE'] = os.getcwd()
463    current_env['NO_DEVTOOL'] = '1'
464    current_env['CCACHE_LOG_SUFFIX'] = 'dayu200-arm32'
465    current_env['CCACHE_NOHASHDIR'] = 'true'
466    current_env['CCACHE_SLOPPINESS'] = 'include_file_ctime'
467    gn_args_dict = {
468        'ohos_components_checktype': '3',
469        'use_thin_lto': 'false',
470        'load_test_config': 'false',
471        'enable_lto_O0': 'true',
472        'enable_notice_collection': 'false',
473        'skip_generate_module_list_file': 'true'
474    }
475    gn_args_list = [f'{key}={value}' if value else key for key, value in gn_args_dict.items()]
476    gn_args_correct = f'--gn-args {" ".join(gn_args_list)}'
477    cmd = [
478        './build.sh',
479        '--product-name', 'rk3568',
480        '--ccache',
481        '--build-target', 'make_all',
482        gn_args_correct,
483        '--disable-package-image',
484        '--disable-part-of-post-build', 'output_part_rom_status',
485        '--disable-part-of-post-build', 'get_warning_list',
486        '--disable-part-of-post-build', 'compute_overlap_rate'
487    ]
488    result = subprocess.run(cmd, env=current_env, check=True, text=True)
489    print("Return compile cmd:", result.returncode)
490    _build_after_compile()
491
492
493def _build_after_compile():
494    work_dir = os.getcwd()
495    command1 = [
496        "python",
497        os.path.join(work_dir, "developtools/integration_verification/tools/deps_guard/deps_guard.py"),
498        "-i",
499        os.path.join(work_dir, "./out/rk3568")
500    ]
501    subprocess.run(command1, check=True)
502    command2 = ["rm", "-rf", os.path.join(work_dir, "out/rk3568/exe.unstripped/tests")]
503    subprocess.run(command2, check=True)
504    command3 = ["rm", "-rf", os.path.join(work_dir, "screenshot")]
505    subprocess.run(command3, check=True)
506    command4 = ["cp", "-r",
507                os.path.join(work_dir, "developtools/integration_verification/cases/smoke/basic/screenshot32"),
508                os.path.join(work_dir, "screenshot")]
509    subprocess.run(command4, check=True)
510    command5 = ["rm", "-rf", os.path.join(work_dir, "DeployDevice")]
511    subprocess.run(command5, check=True)
512    command6 = ["cp", "-r", os.path.join(work_dir, "developtools/integration_verification/DeployDevice"),
513                os.path.join(work_dir, "DeployDevice")]
514    subprocess.run(command6, check=True)
515    print("Return after compile: success")
516
517
518def images_cmmands(depfile, image_name, input_path, image_config_file, deviceimage_config_file, output_image):
519    images_command = ["../../build/ohos/images/build_image.py"]
520    images_command.append("--depfile")
521    images_command.append(depfile)
522    images_command.append("--image-name")
523    images_command.append(image_name)
524    images_command.append("--input-path")
525    images_command.append(input_path)
526    images_command.append("--image-config-file")
527    images_command.append(image_config_file)
528    images_command.append("--device-image-config-file")
529    images_command.append(deviceimage_config_file)
530    images_command.append("--output-image")
531    images_command.append(output_image)
532    images_command.append("--target-cpu")
533    images_command.append("arm")
534    images_command.append("--build-variant")
535    images_command.append("root")
536    images_command.append("--build-image-tools-path")
537    images_command.append("clang_x64/thirdparty/e2fsprogs")
538    images_command.append("clang_x64/thirdparty/f2fs-tools")
539    images_command.append("../../third_party/e2fsprogs/prebuilt/host/bin")
540    images_command.append("../../build/ohos/images/mkimage")
541    return images_command
542
543
544def get_images_vendor_commands():
545    images_commands = [
546        {
547            "echo": "update chip_ckm.img in packages/phone/images",
548            "cmd": [
549                "../../build/ohos/images/mkimage/mkchip_ckm.py",
550                "--src-dir", "packages/phone/chip_ckm",
551                "--device-name", "packages/phone/images/chip_ckm.img",
552                "--config-file-path", "../../build/ohos/images/mkimage/chip_ckm.txt",
553                "--mkextimage-tools-path", "../../build/ohos/images/mkimage/mkextimage.py",
554                "--build-image-tools-path", "clang_x64/thirdparty/f2fs-tools",
555                "clang_x64/thirdparty/e2fsprogs",
556                "../../third_party/e2fsprogs/prebuilt/host/bin"
557            ]
558        },
559        {
560            "echo": "update chip_prod.img in packages/phone/images",
561            "cmd": images_cmmands("gen/build/ohos/images/phone_chip_prod_image.d", "chip_prod",
562                                  "packages/phone/chip_prod",
563                                  "../../build/ohos/images/mkimage/chip_prod_image_conf.txt",
564                                  "packages/imagesconf/chip_prod_image_conf.txt", "packages/phone/images/chip_prod.img")
565        },
566        {
567            "echo": "update ramdisk.img in packages/phone/images",
568            "cmd": images_cmmands("gen/build/ohos/images/phone_ramdisk_image.d", "ramdisk", "packages/phone/ramdisk",
569                                  "../../build/ohos/images/mkimage/ramdisk_image_conf.txt",
570                                  "packages/imagesconf/ramdisk_image_conf.txt", "ramdisk.img")
571        },
572        {
573            "echo": "update eng_chipset.img in packages/phone/images",
574            "cmd": images_cmmands("gen/build/ohos/images/phone_eng_chipset_image.d", "eng_chipset",
575                                  "packages/phone/eng_chipset",
576                                  "../../build/ohos/images/mkimage/eng_chipset_image_conf.txt",
577                                  "packages/imagesconf/eng_chipset_image_conf.txt",
578                                  "packages/phone/images/eng_chipset.img")
579        },
580        {
581            "echo": "update vendor.img in packages/phone/images",
582            "cmd": images_cmmands("gen/build/ohos/images/phone_vendor_image.d", "vendor", "packages/phone/vendor",
583                                  "../../build/ohos/images/mkimage/vendor_image_conf.txt",
584                                  "packages/imagesconf/vendor_image_conf.txt", "packages/phone/images/vendor.img")
585        }
586    ]
587    return images_commands
588
589
590def get_images_system_commands():
591    images_system_commands = [
592        {
593            "echo": "update eng_system.img in packages/phone/images",
594            "cmd": images_cmmands("gen/build/ohos/images/phone_eng_system_image.d", "eng_system",
595                                  "packages/phone/eng_system",
596                                  "../../build/ohos/images/mkimage/eng_system_image_conf.txt",
597                                  "packages/imagesconf/eng_system_image_conf.txt",
598                                  "packages/phone/images/eng_system.img")
599        },
600        {
601            "echo": "update sys_prod.img in packages/phone/images",
602            "cmd": images_cmmands("gen/build/ohos/images/phone_sys_prod_image.d", "sys_prod", "packages/phone/sys_prod",
603                                  "../../build/ohos/images/mkimage/sys_prod_image_conf.txt",
604                                  "packages/imagesconf/sys_prod_image_conf.txt", "packages/phone/images/sys_prod.img")
605        },
606        {
607            "echo": "update userdata.img in packages/phone/images",
608            "cmd": images_cmmands("gen/build/ohos/images/phone_userdata_image.d", "userdata", "packages/phone/data",
609                                  "../../build/ohos/images/mkimage/userdata_image_conf.txt",
610                                  "packages/imagesconf/userdata_image_conf.txt", "packages/phone/images/userdata.img")
611        },
612        {
613            "echo": "update updater_ramdisk.img in packages/phone/images",
614            "cmd": images_cmmands("gen/build/ohos/images/phone_updater_ramdisk_image.d", "updater_ramdisk",
615                                  "packages/phone/updater",
616                                  "../../build/ohos/images/mkimage/updater_ramdisk_image_conf.txt",
617                                  "packages/imagesconf/updater_ramdisk_image_conf.txt", "updater_ramdisk.img")
618        },
619        {
620            "echo": "update system.img in packages/phone/images",
621            "cmd": images_cmmands("gen/build/ohos/images/phone_system_image.d", "system", "packages/phone/system",
622                                  "../../build/ohos/images/mkimage/system_image_conf.txt",
623                                  "packages/imagesconf/system_image_conf.txt", "packages/phone/images/system.img")
624        }
625    ]
626    return images_system_commands
627
628
629def regenerate_packages_images():
630    work_dir = os.path.dirname(os.path.abspath(__file__))
631    out_dir = os.path.join(work_dir, 'out', 'rk3568')
632    os.chdir(out_dir)
633    current_dir = os.getcwd()
634    print(f"The current dir is {current_dir}")
635    print("The third step: begin to regenerate img files, please wait ...")
636    images_vendor_commands = get_images_vendor_commands()
637    images_system_commands = get_images_system_commands()
638    images_commands = list(chain(images_vendor_commands, images_system_commands))
639    for cmd_info in images_commands:
640        print(cmd_info["echo"])
641        try:
642            result = subprocess.run(cmd_info["cmd"], check=True, text=True)
643        except subprocess.CalledProcessError as e:
644            print(f"Error executing command: {cmd_info['cmd']}")
645            print(f"Return code: {e.returncode}")
646            print(f"Error output: {e.stderr}")
647        except Exception as e:
648            print(f"An unexpected error occurred: {e}")
649    print("The third step finished successfully")
650    os.chdir(os.path.dirname(os.path.dirname(work_dir)))
651
652
653def build_trees(part, list_text, files, paths):
654    if not part:
655        _build_dayu200()
656        print('Prebuilt build')
657    indep_components = list_text['data']['indep_list']
658    if len(part) == 1 and part[0] in indep_components:
659        print('Independent compilation')
660        _prebuild_build()
661        _hb_build(part[0])
662        if _check_inner_api(part[0], files, CURRENT_DIRECTORY):
663            _create_datapart_json(1, True)
664            _build_dayu200()
665        else:
666            _create_datapart_json(1, False)
667            replace_part_sofile(part[0], './', './')
668            regenerate_packages_images()
669    elif len(part) > 1 and all(item in indep_components for item in other_list) and len(part) == len(paths):
670        _build_dayu200()
671        print('Multiple independent components being compiled together')
672    else:
673        _create_datapart_json(3, True)
674        print('Inherited dayu200 result or dependent compilation')
675
676
677def _run_build_script(product_name, build_option):
678    result = subprocess.run(['./build.sh', '--product-name', product_name, build_option],
679                            check=True, text=True)
680
681
682def _load_json_file(file_path):
683    try:
684        with open(file_path, 'r') as json_file:
685            return json.load(json_file)
686    except FileNotFoundError:
687        print(f"The file at {file_path} was not found.")
688        return None
689    except json.JSONDecodeError:
690        print(f"Failed to decode JSON at {file_path}.")
691        return None
692
693
694def _get_parts_deps(call, product_name='rk3568', current_dir_abspath=None):
695    if current_dir_abspath is None:
696        current_dir_abspath = os.path.abspath(os.getcwd())
697
698    build_option = '--build-only-load' if call == "deps" else '--build-only-gn'
699    base_path = os.path.join(current_dir_abspath, 'out', product_name, 'build_configs', 'parts_info')
700    file_path = os.path.join(base_path, 'parts_deps.json' if call == "deps" else 'parts_info.json')
701    if os.path.exists(file_path):
702        print(f"File {file_path} already exists, skipping build script execution.")
703        parts_json = _load_json_file(file_path)
704        return parts_json
705    else:
706        try:
707            _run_build_script(product_name, build_option)
708            parts_json = _load_json_file(file_path)
709            return parts_json
710        except subprocess.CalledProcessError as e:
711            print(f"Error occurred during subprocess execution: {e}")
712            print(e.stderr)
713            return None
714
715
716def _bool_only_build(house, export_button):
717    try:
718        deps_files = json.loads(export_button)
719    except json.JSONDecodeError:
720        print("export_button not json file")
721        return True
722    deps_value = deps_files.get(house, [])
723    top_file_bool = False
724    for file in deps_value:
725        ext = os.path.splitext(file)[1].lower()
726        if ext in ['.hpp', '.inl', '.inc', '.h', '.in']:
727            print(f"{file} ends with a valid extension: {ext}")
728            top_file_bool = True
729            return top_file_bool
730        else:
731            print(f"{file} does not end with a valid extension.")
732    return top_file_bool
733
734
735def _load_parts_info_json():
736    file_path = os.path.join('out', 'rk3568', 'build_configs', 'parts_info', 'parts_info.json')
737    try:
738        with open(file_path, 'r') as json_file:
739            return json.load(json_file)
740    except FileNotFoundError:
741        print("parts_info.json not found")
742        return None
743    except json.JSONDecodeError:
744        print("parts_info.json decode error")
745        return None
746
747
748def _generate_file_path(parts_data, component):
749    if component in parts_data and isinstance(parts_data[component], list):
750        subsystem_name = [item['subsystem_name'] for item in parts_data[component] if 'subsystem_name' in item]
751        origin_part_name = [item['origin_part_name'] for item in parts_data[component] if 'origin_part_name' in item]
752        if subsystem_name and origin_part_name:
753            return os.path.join(CURRENT_DIRECTORY, 'out', 'rk3568', subsystem_name[0], origin_part_name[0], 'publicinfo')
754    return None
755
756
757def _load_and_extract_include_dirs(json_file_path):
758    include_dirs = []
759    try:
760        with open(json_file_path, "r") as f:
761            innerapi_json = json.load(f)
762            for public_config in innerapi_json.get('public_configs', []):
763                dirs = public_config.get('include_dirs', [])
764                include_dirs.extend(dirs)
765    except (FileNotFoundError, json.JSONDecodeError):
766        pass
767    return include_dirs
768
769
770def _extract_include_dirs_from_file(file_path):
771    innerapi_path = []
772    if os.path.isdir(file_path):
773        for filename in os.listdir(file_path):
774            if filename.endswith(".json"):
775                print(f"find innerapi file: {filename}")
776                json_file_path = os.path.join(file_path, filename)
777                innerapi_path.extend(_load_and_extract_include_dirs(json_file_path))
778    return innerapi_path
779
780
781def _path_inner_api(component):
782    parts_data = _load_parts_info_json()
783    if parts_data is None:
784        return ["noinnerapi"]
785    file_path = _generate_file_path(parts_data, component)
786    if file_path is None:
787        print("no inner api")
788        return ["noinnerapi"]
789    innerapi_path = _extract_include_dirs_from_file(file_path)
790    path_set = [item for index, item in enumerate(innerapi_path) if item not in innerapi_path[:index]]
791    return path_set
792
793
794def _file_paths(component, house, export_button):
795    file_path = os.path.join('out', 'rk3568', 'build_configs', 'parts_info', 'parts_path_info.json')
796    try:
797        with open(file_path, 'r') as json_file:
798            parts_data = json.load(json_file)
799    except FileNotFoundError:
800        print("parts_path_info.json not found")
801    except json.JSONDecodeError:
802        print("parts_path_info.json decode error")
803    component_mkdir = parts_data[component]
804    deps_files = json.loads(export_button)
805    deps_value = deps_files.get(house, [])
806    files_path = []
807    for file in deps_value:
808        files_path.append(os.path.join(component_mkdir, file))
809    return files_path
810
811
812def _bool_target_build(house, component):
813    export_files_info = _get_export_files('PR_FILE_PATHS')
814    if not _bool_only_build(house, export_files_info):
815        return True
816    innerapi_paths = _path_inner_api(component)
817    if len(innerapi_paths) == 0:
818        return True
819    elif innerapi_paths[0] == 'noinnerapi':
820        return False
821    file_paths = _file_paths(component, house, export_button)
822    for file_path in file_paths:
823        for innerapi_path in innerapi_paths:
824            if innerapi_path.startswith('//'):
825                innerapi_path = innerapi_path[2:]
826            if file_path.startswith(innerapi_path):
827                return False
828    return True
829
830
831def get_build_target(deps, default_target='make_all'):
832    try:
833        if not deps:
834            return f' --build-target {default_target} '
835        build_targets = [f' --build-target {dep}' for dep in deps]
836        return ''.join(build_targets)
837    except TypeError:
838        print(f"deps failed  {type(deps)}")
839        return f' --build-target {default_target} '
840
841
842def execute_build_command(build_target, gnargs):
843    _build_pre_compile()
844    current_env = os.environ.copy()
845    current_env['CCACHE_BASE'] = os.getcwd()
846    current_env['NO_DEVTOOL'] = '1'
847    current_env['CCACHE_LOG_SUFFIX'] = 'dayu200-arm32'
848    current_env['CCACHE_NOHASHDIR'] = 'true'
849    current_env['CCACHE_SLOPPINESS'] = 'include_file_ctime'
850    build_cmd = ['./build.sh', '--product-name', 'rk3568']
851    build_cmd.extend(shlex.split(build_target))
852    build_cmd.extend(shlex.split(gnargs))
853    try:
854        result = subprocess.run(build_cmd, check=True, text=True)
855        print(result.stdout)
856    except subprocess.CalledProcessError as e:
857        print(f"Build failed with error: {e.stderr}")
858    _build_after_compile()
859
860
861def add_dependent_components(component_name, component_dep_list, parts_deps):
862    for c, info in parts_deps.items():
863        if info and (
864            (info.get("components") and component_name in info.get("components")) or
865            (info.get("third_party") and component_name in info.get("third_party"))
866        ):
867            component_dep_list.append(c)
868
869
870def precise_dayu200_build(gnargs):
871    project_list = _get_export_project('project_list')
872    values_to_all = {'build', 'manifest'}
873    subprocess.run(['./build/prebuilts_download.sh'], check=True, text=True)
874    if not project_list or not values_to_all.isdisjoint(set(project_list)):
875        print("need full build")
876        execute_build_command('--build-target make_all', gnargs)
877    parts_deps = _get_parts_deps("deps")
878    component_dir = _get_api_mkdir(project_list)
879    component_dep_list = []
880    for part_house, part in component_dir.items():
881        if part and part.keys():
882            component_name = list(part.keys())[0]
883            component_dep_list.append(component_name)
884            if _bool_target_build(part_house, component_name):
885                continue
886            else:
887                add_dependent_components(component_name, component_dep_list, parts_deps)
888        else:
889            execute_build_command('--build-target make_all', gnargs)
890            print("part not found full build")
891    component_dep_list = list(set(component_dep_list))
892    targets = get_build_target(component_dep_list)
893    print(f' targets : {targets} ')
894    execute_build_command(targets, gnargs)
895
896
897if __name__ == '__main__':
898    parser = argparse.ArgumentParser()
899    parser.add_argument('--gnargs', required=True)
900    args = parser.parse_args()
901    request_param = _get_export_project('project_list')
902    if not request_param:
903        subprocess.run(['./build/prebuilts_download.sh'], check=True, text=True)
904        _build_dayu200()
905        print('Prebuilt build')
906    else:
907        mkdir_text = _get_api_mkdir(request_param)
908        file_list = _get_export_files('PR_FILE_PATHS')
909        parts = _get_dep_parts(mkdir_text, file_list)
910        whitelist_parts = _get_part_list()
911        build_trees(parts, whitelist_parts, file_list, request_param)
912