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