OTA 升级编译包总结
在laval和csdn找了些OTA升级相关的内容,把OTA升级的流程,用python总结成了脚本,在开源4.1上测试ok。 如有不对,欢迎交流,感谢! 参考:OpenHarmony 4.0 本地OTA升级_openharmony ota-CSDN博客 注意:1)脚本运行目录:4.1/base/update/packaging_tools 2)差分包,注意image要有变化;先将现有的im
在laval和csdn找了些OTA升级相关的内容,把OTA升级的流程,用python总结成了脚本,在开源4.1上测试ok。
如有不对,欢迎交流,感谢!
参考:OpenHarmony 4.0 本地OTA升级_openharmony ota-CSDN博客
注意:1)脚本运行目录:4.1/base/update/packaging_tools
2)差分包,注意image要有变化;先将现有的image拷贝到target目录,然后修改代码并编译;脚本会将target中的拷贝到source中,然后将新的image拷贝到target中;可以 使用diff new.image old.image 比较,必须存在差异;
3)4.1/base/update/packaging_tools/build_update.py中increment_image_processing函数需要做以下修改,否则,差分包报错找不到4.1/base/update/packaging_tools/target_package/updater_config/updater_specified_config.xml中的image。
脚本如下:
#全量包py
import os
import shutil
import subprocess
import datetime
import re
CURRENT_PATH = os.path.join(os.getcwd(), "../../../")
BASE_PATH = os.path.join(CURRENT_PATH, "base", "update", "packaging_tools")
# BASE_PATH = CURRENT_PATH
CONFIG_PATH = os.path.join(BASE_PATH, "target_package", "updater_config")
SIGN_PATH = os.path.join(BASE_PATH, "sign_cert")
BUILD_SCRIPT = os.path.join(BASE_PATH, "build_update.py")
TARGET_PATH = os.path.join(BASE_PATH, "target_package")
OUTPUT_PATH = os.path.join(BASE_PATH, "output_package")
PRIVATE_KEY = os.path.join(TARGET_PATH, "rsa_private_key2048.pem")
SOFTWARERE_VISION = "OpenHarmony 4.1.7.8"
def create_folders(folders):
for folder in folders:
if not os.path.exists(folder):
try:
os.makedirs(folder)
print(f"文件夹 {folder} 创建成功")
except OSError as e:
print(f"错误: 创建 {folder} 文件夹失败 - {e}")
return False
else:
print(f"文件夹 {folder} 已存在,跳过创建")
return True
def find_and_copy_files(source_dir, destination_dir, files_to_copy):
if not os.path.exists(destination_dir):
try:
os.makedirs(destination_dir)
print(f"文件夹 {destination_dir} 创建成功")
except OSError as e:
print(f"错误: 创建 {destination_dir} 文件夹失败 - {e}")
return False
for root, _, files in os.walk(source_dir):
for file in files:
if file in files_to_copy:
file_path = os.path.join(root, file)
destination_path = os.path.join(destination_dir, file)
if not os.path.exists(destination_path):
try:
shutil.copy(file_path, destination_path)
print(f"文件 {file_path} 拷贝成功到 {destination_path}")
except Exception as e:
print(f"错误: 拷贝 {file_path} 失败 - {e}")
return False
else:
print(f"文件 {destination_path} 已存在,跳过拷贝")
return True
def modify_file(file_path, content_to_check, new_content):
if not os.path.exists(file_path):
print(f"错误: 文件 {file_path} 不存在,无法修改")
return False
if new_content not in open(file_path).read():
try:
with open(file_path, 'a+') as f:
f.write(new_content)
print(f"向 {file_path} 中增加 {new_content.strip()} 成功")
except Exception as e:
print(f"错误: 向 {file_path} 中增加内容失败 - {e}")
return False
else:
print(f"文件 {file_path} 中已存在 {new_content.strip()},跳过修改")
return True
def modify_updater_config_xml():
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
current_time = datetime.datetime.now().strftime("%H:%M")
destination = os.path.join(CONFIG_PATH, "updater_specified_config.xml")
if not os.path.exists(destination):
print(f"错误: 文件 {destination} 不存在,无法修改")
return False
replacements = {
r'fileVersion="[^"]*"': r'fileVersion="02"',
r'softVersion="[^"]*"': r'softVersion="{}"'.format(SOFTWARERE_VISION),
r'date="[^\"]*"': r'date="{}"'.format(current_date),
r'time="[^\"]*"': r'time="{}"'.format(current_time),
r'\.\\IMAGES\\': r'./images/'
}
try:
with open(destination, 'r') as file:
filedata = file.read()
for pattern, replacement in replacements.items():
filedata = re.sub(pattern, replacement, filedata)
with open(destination, 'w') as file:
file.write(filedata)
print(f"文件 {destination} 修改成功")
except Exception as e:
print(f"错误: 修改 updater_specified_config.xml 失败 - {e}")
return False
return True
def copy_keys_and_certs(src_private_key, src_signing_cert):
destination_private_key = os.path.join(TARGET_PATH, "rsa_private_key2048.pem")
destination_signing_cert = os.path.join(SIGN_PATH, "signing_cert.crt")
if not os.path.exists(destination_private_key):
try:
shutil.copy(src_private_key, destination_private_key)
print(f"文件 {src_private_key} 已成功拷贝到 {destination_private_key}")
except Exception as e:
print(f"错误: 拷贝 rsa_private_key2048.pem 失败 {e}")
return False
if not os.path.exists(destination_signing_cert):
try:
shutil.copy(src_signing_cert, destination_signing_cert)
print(f"文件 {src_signing_cert} 已成功拷贝到 {destination_signing_cert}")
except Exception as e:
print(f"错误: 拷贝 signing_cert.crt 失败 {e}")
return False
return True
def copy_images_to_target_package(source_images_path, target_package_path):
if not os.path.exists(source_images_path):
print(f"错误: 源文件夹 {source_images_path} 不存在")
return False
target_images_path = os.path.join(target_package_path, "images")
if os.path.exists(target_images_path):
try:
shutil.rmtree(target_images_path) # 删除已存在的目标文件夹
print(f"已删除旧的 images 文件夹: {target_images_path}")
except Exception as e:
print(f"错误: 删除 images 文件夹失败 - {e}")
return False
try:
shutil.copytree(source_images_path, target_images_path)
print(f"文件夹 {source_images_path} 已成功拷贝到 {target_images_path}")
except Exception as e:
print(f"错误: 拷贝 images 文件夹失败 {e}")
return False
return True
def copy_updater_binary(source_updater_binary_path, target_package_path):
if not os.path.exists(source_updater_binary_path):
print(f"错误: 源文件夹 {source_updater_binary_path} 不存在")
return False
if not os.path.exists(target_package_path):
try:
os.makedirs(target_package_path)
print(f"文件夹 {target_package_path} 创建成功")
except OSError as e:
print(f"错误: 创建 {target_package_path} 文件夹失败 - {e}")
return False
try:
shutil.copy(source_updater_binary_path, os.path.join(target_package_path, "updater_binary"))
print(f"文件 {source_updater_binary_path} 已成功拷贝到 {target_package_path}")
except Exception as e:
print(f"错误: 拷贝 updater_binary 文件失败 {e}")
return False
return True
def build_update_package():
try:
subprocess.run(['python3', BUILD_SCRIPT, TARGET_PATH, OUTPUT_PATH, '-pk', PRIVATE_KEY], check=True)
print("更新包构建成功")
except subprocess.CalledProcessError as e:
print(f"错误: 更新包构建失败 - {e}")
return False
return True
def main(device_name):
folders = [CONFIG_PATH, OUTPUT_PATH, SIGN_PATH]
print(f"{CONFIG_PATH}")
print(f"{OUTPUT_PATH}")
print(f"{SIGN_PATH}")
if not create_folders(folders):
print("错误: 创建文件夹失败,退出程序")
return
source_dir = os.path.join(CURRENT_PATH, "device", "board")
destination_dir = os.path.join(BASE_PATH, "target_package", "updater_config")
files_to_copy = ["updater_specified_config.xml", "BOARD.list", "VERSION.mbn"]
if not find_and_copy_files(source_dir, destination_dir, files_to_copy):
print("错误: 拷贝文件失败,退出程序")
return
if device_name == "dayu210":
board_list_path = os.path.join(CONFIG_PATH, "BOARD.list")
if not modify_file(board_list_path, device_name, f"\nrk3588"):
print("错误: 修改 BOARD.list 文件失败,退出程序")
return
else:
board_list_path = os.path.join(CONFIG_PATH, "BOARD.list")
if not modify_file(board_list_path, device_name, f"\n{device_name}"):
print("错误: 修改 BOARD.list 文件失败,退出程序")
return
version_mbn_path = os.path.join(CONFIG_PATH, "VERSION.mbn")
if not modify_file(version_mbn_path, SOFTWARERE_VISION, SOFTWARERE_VISION):
print("错误: 修改 VERSION.mbn 文件失败,退出程序")
return
if not modify_updater_config_xml():
print("错误: 修改 updater_specified_config.xml 文件失败,退出程序")
return
src_private_key = os.path.join(CURRENT_PATH, "base/update/updater/test/unittest/test_data/src/rsa_private_key2048.pem")
src_signing_cert = os.path.join(CURRENT_PATH, "base/update/updater/test/unittest/test_data/src/signing_cert.crt")
if not copy_keys_and_certs(src_private_key, src_signing_cert):
print("错误: 拷贝密钥和证书失败,退出程序")
return
if device_name == "dayu210":
source_images_path = os.path.join(CURRENT_PATH, "out", "rk3588", "packages/phone/images")
if not copy_images_to_target_package(source_images_path, TARGET_PATH):
print("错误: 拷贝 images 文件夹失败,退出程序")
return
source_updater_binary_path = os.path.join(CURRENT_PATH, "out", "rk3588", "packages/phone/updater/bin/updater_binary")
if not copy_updater_binary(source_updater_binary_path, TARGET_PATH):
print("错误: 拷贝 updater_binary 文件失败,退出程序")
return
else:
source_images_path = os.path.join(CURRENT_PATH, "out", device_name, "packages/phone/images")
if not copy_images_to_target_package(source_images_path, TARGET_PATH):
print("错误: 拷贝 images 文件夹失败,退出程序")
return
source_updater_binary_path = os.path.join(CURRENT_PATH, "out", device_name, "packages/phone/updater/bin/updater_binary")
if not copy_updater_binary(source_updater_binary_path, TARGET_PATH):
print("错误: 拷贝 updater_binary 文件失败,退出程序")
return
if not build_update_package():
print("错误: 构建更新包失败,退出程序")
return
def usage():
print("OTA 全量升级")
print("用法: python3 update_diff.py <device_name>")
print("参数:")
print(" device_name: 设备名称,如 rk3588、dayu210")
print("注意: 1. 请在 4.1/base/update/packaging_tools 目录下执行脚本")
print(" 2. 请确保已经编译完成,并将编译结果拷贝到 4.1/out/<device_name> 目录下")
print(" 3. SOFTWARERE_VISION 值需要修改为当前版本号")
print(" 4. 全量编译:python3 build_update.py ./target_package/ ./output_package/ -pk ./target_package/rsa_private_key2048.pem")
if __name__ == "__main__":
import sys
usage()
if len(sys.argv) != 2:
print("错误: 需要一个设备名称参数")
exit(1)
else:
main(sys.argv[1])
#差分包
import os
import shutil
import subprocess
import datetime
import re
# 差分升级包是建立在全量升级包的基础上的
SOFTWARE_VERSION = "OpenHarmony 4.1.7.8"
SOFTWARE_VERSION_NEW = "OpenHarmony 4.1.7.9"
CURRENT_PATH = os.path.join(os.getcwd(), "../../../")
BASE_PATH = os.path.join(CURRENT_PATH, "base", "update", "packaging_tools")
TARGET_PATH = os.path.join(BASE_PATH, "target_package")
SOURCE_PATH = os.path.join(BASE_PATH, "source_package")
OUTPUT_PATH = os.path.join(BASE_PATH, "output_package")
LIB_PATH = os.path.join(BASE_PATH, "lib")
CONFIG_PATH = os.path.join(TARGET_PATH, "updater_config")
BUILD_SCRIPT = os.path.join(BASE_PATH, "build_update.py")
PRIVATE_KEY = os.path.join(TARGET_PATH, "rsa_private_key2048.pem")
# 建立lib目录,并拷贝文件
def copy_lib_files(device_name):
if device_name == "dayu210":
source_diff_path = os.path.join(CURRENT_PATH, "out", "rk3588", "clang_x64", "updater", "updater", "diff")
source_e2fsprogs_dir = os.path.join(CURRENT_PATH, "out", "rk3588", "clang_x64", "thirdparty", "e2fsprogs")
else:
source_diff_path = os.path.join(CURRENT_PATH, "out", device_name, "clang_x64", "updater", "updater", "diff")
source_e2fsprogs_dir = os.path.join(CURRENT_PATH, "out", device_name, "clang_x64", "thirdparty", "e2fsprogs")
# 创建lib目录(如果它不存在)
os.makedirs(LIB_PATH, exist_ok=True)
# 检查 source_diff_path 是文件还是目录并复制
if os.path.isdir(source_diff_path):
shutil.copytree(source_diff_path, os.path.join(LIB_PATH, os.path.basename(source_diff_path)), dirs_exist_ok=True)
elif os.path.isfile(source_diff_path):
shutil.copy(source_diff_path, LIB_PATH)
else:
raise FileNotFoundError(f"错误: {source_diff_path} 既不是目录也不是文件。")
# 复制 source_e2fsprogs_dir 中的所有文件到 LIB_PATH
for root, _, files in os.walk(source_e2fsprogs_dir):
for file in files:
src_file = os.path.join(root, file)
relative_path = os.path.relpath(src_file, source_e2fsprogs_dir)
dst_file = os.path.join(LIB_PATH, relative_path)
os.makedirs(os.path.dirname(dst_file), exist_ok=True)
shutil.copy(src_file, dst_file)
# 检查并安装 libselinux
def check_and_install_libselinux():
e2fsdroid_path = os.path.join(LIB_PATH, "e2fsdroid")
try:
output = subprocess.check_output(["ldd", e2fsdroid_path], text=True)
if "not found" in output:
print(f"warning: libselinux 未找到,正在安装...")
subprocess.run(["sudo", "apt-get", "update"], check=True)
subprocess.run(["sudo", "apt-get", "install", "-y", "libselinux1"], check=True)
print(f"info: libselinux 安装完成")
# 查找 libselinux.so 或 libselinux.so.1 路径
libselinux_path = find_libselinux_so()
if libselinux_path:
print(f"info: libselinux.so 路径为:{libselinux_path}")
# 创建软链接
subprocess.run(["sudo", "ln", "-sf", libselinux_path, "/usr/lib/x86_64-linux-gnu/libselinux.so"], check=True)
else:
raise FileNotFoundError(f"error: libselinux.so 或 libselinux.so.1 未找到")
else:
print(f"info: libselinux 已安装")
except subprocess.CalledProcessError as e:
raise RuntimeError(f"error: 检查依赖时出错: {e}")
# 查找 libselinux.so 或 libselinux.so.1 路径
def find_libselinux_so():
for library_name in ["libselinux.so", "libselinux.so.1"]:
libselinux_path = find_library(library_name, "libselinux.so.1.1")
if libselinux_path:
return libselinux_path
return None
# 查找库文件路径
def find_library(library_name, exclude_name):
try:
output = subprocess.check_output(["find", "/", "-name", library_name, "-not", "-name", exclude_name], text=True)
paths = output.strip().split('\n')
if paths and paths[0]:
return paths[0]
except subprocess.CalledProcessError as e:
print(f"error: 查找库文件时出错: {e}")
return None
# 检查依赖
def check_dependencies():
e2fsdroid_path = os.path.join(LIB_PATH, "e2fsdroid")
if not os.path.isfile(e2fsdroid_path):
raise FileNotFoundError(f"e2fsdroid 工具未找到,请检查编译和复制过程")
print(f"info: 所有依赖检查完成")
# 建立source_package目录,并拷贝文件
def copy_source_package():
source_package_path = os.path.join(BASE_PATH, "source_package")
os.makedirs(source_package_path, exist_ok=True)
# 将target_package 目录拷贝到 source_package 目录
shutil.copytree(TARGET_PATH, source_package_path, dirs_exist_ok=True)
# 拷贝新的镜像和 updater_binary 到 target_package
def copy_images_and_updater_binary(device_name):
if device_name == "dayu210":
source_images_path = os.path.join(CURRENT_PATH, "out", "rk3588", "packages/phone/images")
source_updater_binary_path = os.path.join(CURRENT_PATH, "out", "rk3588", "packages/phone/updater/bin/updater_binary")
else:
source_images_path = os.path.join(CURRENT_PATH, "out", device_name, "packages/phone/images")
source_updater_binary_path = os.path.join(CURRENT_PATH, "out", device_name, "packages/phone/updater/bin/updater_binary")
if not os.path.exists(source_images_path):
raise FileNotFoundError(f"错误: 源文件夹 {source_images_path} 不存在")
if not os.path.exists(source_updater_binary_path):
raise FileNotFoundError(f"错误: 文件 {source_updater_binary_path} 不存在")
# 删除并重新拷贝 images 文件夹
target_images_path = os.path.join(TARGET_PATH, "images")
if os.path.exists(target_images_path):
shutil.rmtree(target_images_path)
print(f"已删除旧的 images 文件夹: {target_images_path}")
shutil.copytree(source_images_path, target_images_path)
print(f"文件夹 {source_images_path} 已成功拷贝到 {target_images_path}")
# 拷贝 updater_binary
if not os.path.exists(TARGET_PATH):
os.makedirs(TARGET_PATH)
shutil.copy(source_updater_binary_path, os.path.join(TARGET_PATH, "updater_binary"))
print(f"文件 {source_updater_binary_path} 已成功拷贝到 {TARGET_PATH}")
# 替换 target 中的 updater_specified_config.xml 和 VERSION.mbn
def modify_version_mbn():
version_mbn_path = os.path.join(CONFIG_PATH, "VERSION.mbn")
if not os.path.exists(version_mbn_path):
raise FileNotFoundError(f"错误: 文件 {version_mbn_path} 不存在")
try:
with open(version_mbn_path, 'r') as file:
content = file.read()
# 替换 SOFTWARE_VERSION 为 SOFTWARE_VERSION_NEW
if SOFTWARE_VERSION in content:
new_content = content.replace(SOFTWARE_VERSION, SOFTWARE_VERSION_NEW)
else:
new_content = content + f"\n{SOFTWARE_VERSION_NEW}\n"
with open(version_mbn_path, 'w') as file:
file.write(new_content)
print(f"文件 {version_mbn_path} 修改成功")
except Exception as e:
raise RuntimeError(f"错误: 修改 VERSION.mbn 失败 - {e}")
def modify_updater_config_xml():
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
current_time = datetime.datetime.now().strftime("%H:%M")
destination = os.path.join(CONFIG_PATH, "updater_specified_config.xml")
if not os.path.exists(destination):
raise FileNotFoundError(f"错误: 文件 {destination} 不存在,无法修改")
replacements = {
r'softVersion="[^"]*"': r'softVersion="{}"'.format(SOFTWARE_VERSION_NEW),
r'date="[^\"]*"': r'date="{}"'.format(current_date),
r'time="[^\"]*"': r'time="{}"'.format(current_time),
r'compType="[^"]*"': r'compType="1"'
}
try:
with open(destination, 'r') as file:
filedata = file.read()
for pattern, replacement in replacements.items():
filedata = re.sub(pattern, replacement, filedata)
with open(destination, 'w') as file:
file.write(filedata)
print(f"文件 {destination} 修改成功")
except Exception as e:
raise RuntimeError(f"错误: 修改 updater_specified_config.xml 失败 - {e}")
# 编译指令
def build_update_package():
try:
subprocess.run(['python3', BUILD_SCRIPT, TARGET_PATH, OUTPUT_PATH, '-s', SOURCE_PATH, '-pk', PRIVATE_KEY], check=True)
#
print("更新包构建成功")
except subprocess.CalledProcessError as e:
raise RuntimeError(f"错误: 更新包构建失败 - {e}")
def main(device_name):
try:
copy_lib_files(device_name)
check_dependencies()
check_and_install_libselinux()
copy_source_package()
copy_images_and_updater_binary(device_name)
modify_version_mbn()
modify_updater_config_xml()
build_update_package()
print("OTA 差分升级包制作完成")
except Exception as e:
print(e)
exit(1)
def usage():
print("OTA 差分升级")
print("用法: python3 update_diff.py <device_name>")
print("参数:")
print(" device_name: 设备名称,如 rk3588、dayu210")
print("注意: 1. 请在 4.1/base/update/packaging_tools 目录下执行脚本")
print(" 2. 请确保已经编译完成,并将编译结果拷贝到 4.1/out/<device_name> 目录下")
print(" 3. 请确保已经安装 libselinux-utils 包")
print(" 4. 请确保已经安装 python3 包")
print(" 5. 避免多次执行脚本,以免产生重复文件")
print(" 6. SOFTWARERE_VISION 值需要修改为当前版本号")
print(" 7. 差分编译:python3 build_update.py ./target_package/ ./output_package/ -s ./source_package/ -pk ./target_package/rsa_private_key2048.pem")
# 主函数
if __name__ == "__main__":
import sys
usage()
if len(sys.argv) != 2:
print("错误: 需要一个设备名称参数")
else:
main(sys.argv[1])
更多推荐
所有评论(0)