From 4ff12e5522b530dc2dac3164f67c17ae278d3e19 Mon Sep 17 00:00:00 2001 From: xuchang Date: Fri, 22 Sep 2023 15:58:32 +0800 Subject: [PATCH] =?UTF-8?q?IssueNo:=20#I83LPI=20=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E4=B8=AD=E9=97=B4=E4=BA=A7=E7=89=A9=E7=BC=96=E8=AF=91=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=20Description:=20=E5=9C=A8flutter=20tools=E5=AE=8C?= =?UTF-8?q?=E5=85=A8=E5=85=BC=E5=AE=B9=E5=89=8D=EF=BC=8C=E5=8F=AF=E4=BD=BF?= =?UTF-8?q?=E7=94=A8py=E8=84=9A=E6=9C=AC=E7=BC=96=E8=AF=91=E4=B8=AD?= =?UTF-8?q?=E9=97=B4=E4=BA=A7=E7=89=A9=20Sig:=20OpenHarmony-SIG/flutter-fl?= =?UTF-8?q?utter=20Feature=20or=20Bugfix:=20Feature=20Binary=20Source:=20N?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: xuchang --- app_build/.gitignore | 18 +++++ app_build/__init__.py | 12 +++ app_build/build_dill.py | 163 +++++++++++++++++++++++++++++++++++++++ app_build/config.py | 110 ++++++++++++++++++++++++++ app_build/debug.py | 36 +++++++++ app_build/env_util.py | 30 +++++++ app_build/excute_util.py | 69 +++++++++++++++++ app_build/file_util.py | 30 +++++++ app_build/release.py | 38 +++++++++ 9 files changed, 506 insertions(+) create mode 100644 app_build/.gitignore create mode 100644 app_build/__init__.py create mode 100644 app_build/build_dill.py create mode 100644 app_build/config.py create mode 100644 app_build/debug.py create mode 100644 app_build/env_util.py create mode 100644 app_build/excute_util.py create mode 100644 app_build/file_util.py create mode 100644 app_build/release.py diff --git a/app_build/.gitignore b/app_build/.gitignore new file mode 100644 index 0000000000..a43bd6fdf3 --- /dev/null +++ b/app_build/.gitignore @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +debug_out +release_out +__pycache__ +resource/.dart_tool +/flutter_assets \ No newline at end of file diff --git a/app_build/__init__.py b/app_build/__init__.py new file mode 100644 index 0000000000..8a0d2b732f --- /dev/null +++ b/app_build/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. diff --git a/app_build/build_dill.py b/app_build/build_dill.py new file mode 100644 index 0000000000..a26e6cfaea --- /dev/null +++ b/app_build/build_dill.py @@ -0,0 +1,163 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +#!/usr/bin/python +import excute_util +import shutil +import os +import config +import env_util + +hasInit = False + +# 执行命令行 + + +def excute(cmd, path="."): + return excute_util.excute(cmd, path) + + +def checkExists(file): + if not os.path.exists(file): + print("该文件{}不存在,请检查环境".format(file)) + return False + return True + +# 检查环境 + + +def checkEnv(isDebug=True): + if not checkExists(config.getOutRoot(isDebug)): + return False + if not checkExists(config.getFrontendServerDartSnapshot(isDebug)): + return False + if not checkExists(config.getFlutterPatchedSdkProduct(isDebug)): + return False + if not checkExists(config.getVmIsolateSnapshotBin(isDebug)): + return False + if not checkExists(config.getIsolateSnapshotBin(isDebug)): + return False + if not checkExists(config.getMainPath()): + return False + if not checkExists(config.project_root): + return False + global hasInit + hasInit = os.path.exists(config.dart_sdk_path) + print("flutter是否已初始化:"+str(hasInit)) + env_util.setEnv("PUB_HOSTED_URL", "https://pub.flutter-io.cn") + env_util.setEnv("FLUTTER_STORAGE_BASE_URL", + "https://storage.flutter-io.cn") + return True + +# flutter初始化,执行flutter指令,会缓存dark环境到/bin/cache/.. +def flutterInit(): + if not hasInit: + print('执行首次flutter初始化,时间会比较长') + excute('../bin/flutter') + +# 拷贝resource/darkSdk替换当前flutter darkSdk,因为默认darkSdk,会校验 sdk_hash,而resource/darkSdk下的已经修改 verify_sdk_hash = false +def copyDarkSdk(isDebug=True): + source = config.resource_sdk_path_debug if isDebug else config.resource_sdk_path_release + if os.path.exists(config.dart_sdk_path): + shutil.rmtree(config.dart_sdk_path) + shutil.copytree(source, config.dart_sdk_path) + print('dark_sdk替换结束') + +# 构建dill命令 +def build(isDebug=True): + out_path = config.out_path if isDebug else config.release_out_path + if not os.path.exists(out_path): + os.makedirs(out_path) + cmd = config.dart_path + cmd += " --disable-dart-dev " + cmd += config.getFrontendServerDartSnapshot(isDebug) + cmd += " --sdk-root " + cmd += config.getFlutterPatchedSdkProduct(isDebug) + cmd += " --target=flutter" + cmd += " --packages " + cmd += config.getPackagePath() + cmd += " --no-print-incremental-dependencies " + cmd += " --output-dill " + if isDebug: + cmd += config.dill_out_file + else: + cmd += config.release_dill_out_file + cmd += " -Ddart.vm.profile=false " + cmd += " -Ddart.vm.product=true " + cmd += " --aot" + cmd += " --tfa " + cmd += " --verbose " + cmd += config.getMainPath() + result = excute(cmd) + return result.find("Error: ") == -1 + + +def copyFile(sourceFile, targetFile): + if os.path.exists(targetFile): + os.remove(targetFile) + shutil.copy(sourceFile, targetFile) + print("拷贝从{}到{}结束。".format(sourceFile, targetFile)) + + +def copyFiles(sourceFiles, targetFiles): + if os.path.exists(targetFiles): + shutil.rmtree(targetFiles) + shutil.copytree(sourceFiles, targetFiles) + print("拷贝从{}到{}结束。".format(sourceFiles, targetFiles)) + + +# 用flutter指令创建一个helloWorld工程,然后把.dart_tools拷贝到当前目录 +def mkDartTool(): + if os.path.exists(config.dart_tool_path): + print(config.dart_tool_path+"已存在,无需生成。") + return + if not os.path.exists(config.cache_dir): + os.mkdir(config.cache_dir) + if not os.path.exists(config.cache_project_dart_tool): + cmd = "../flutter/bin/flutter" + cmd += " create "+config.cache_project_name + excute(cmd, config.cache_dir) # 这个执行目录,是在cachedir + copyFiles(config.cache_project_dart_tool, config.dart_tool_path) + # 拷贝完后,删除缓存 + shutil.rmtree(config.cache_project_path) + +# 获取flutter程序的绝对路径,请确保在app_build目录下执行 +def getAbsoluteFlutterPath(): + app_build_path = excute("pwd") + return app_build_path.replace("app_build", "bin/flutter") + + +# 可以在目标项目,创建.dart_tool和flutter_asset文件 +def buildBundle(): + cmd = getAbsoluteFlutterPath() + cmd += " build bundle" + # 在项目目录中,执行flutter build bundle + excute(cmd, config.project_root) + + +def copyFlutterAssets(): + copyFiles(config.getFlutterAssetsPath(), config.flutter_assets_target_path) + + +def buildDill(isDebug=True): + # 检查环境和产物 + if not checkEnv(isDebug): + return False + # 任务开始 + flutterInit() + # 执行flutter build bundle,创造.dart_tools和flutter_assets + buildBundle() + # 拷贝资源 + copyFlutterAssets() + # 构建指令 + return build(isDebug) diff --git a/app_build/config.py b/app_build/config.py new file mode 100644 index 0000000000..4943799cb8 --- /dev/null +++ b/app_build/config.py @@ -0,0 +1,110 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +#!/usr/bin/python +import sys + +# 请在release.py或者debug.py第一个命令行参数中,定义 +project_root = "not defined" + +def checkArgs(): + # 获取命令行参数列表 + args = sys.argv + # 打印传递的参数 + if len(args) <= 1: + print("没有在命令行传递要编译项目的根目录参数,请检查") + return False + else: + global project_root + project_root = args[1] + print("当前正编译的项目路径为:{}".format(project_root)) + return True + +# debug engin产物 +debug_out_root = "../../src/out/ohos_debug_unopt_arm64" +# release engine产物 +release_out_root = "../../src/out/ohos_release_arm64" + + + +release_x86_out_root = release_out_root + "/clang_x64" +gen_snapshot = release_x86_out_root + "/gen_snapshot" + + +def getOutRoot(isDebug=True): + return debug_out_root if isDebug else release_out_root + + +def getFrontendServerDartSnapshot(isDebug=True): + return getOutRoot(isDebug) + "/dart-sdk/bin/snapshots/frontend_server.dart.snapshot" + + +def getFlutterPatchedSdkProduct(isDebug=True): + return getOutRoot(isDebug) + "/flutter_patched_sdk" + + +def getVmIsolateSnapshotBin(isDebug=True): + return getOutRoot(isDebug) + "/gen/flutter/lib/snapshot/vm_isolate_snapshot.bin" + + +def getIsolateSnapshotBin(isDebug=True): + return getOutRoot(isDebug) + "/gen/flutter/lib/snapshot/isolate_snapshot.bin" + + +def getMainPath(): + return project_root + "/lib/main.dart" + + +# Flutter根目录 +flutter_root = ".." + +dart_sdk_path = '../bin/cache/dart-sdk' +resource_sdk_path_debug = './resource/dart-sdk-debug' +resource_sdk_path_release = './resource/dart-sdk-release' + +# 下面是构建所需文件 +dart_path = flutter_root + "/bin/cache/dart-sdk/bin/dart" +flutter_path = flutter_root + "/bin/flutter" + +# package,和project_root相关的路径,都要临时获取 +def getDarkToolPath(): + return project_root + "/.dart_tool" +def getPackagePath(): + return getDarkToolPath() + "/package_config.json" + + +# debug产出目录 +out_path = "./debug_out/" +dill_name = "app.dill" + +debug_out_desc = "Debug编译完成!产物路径:flutter/app_build/debug_out,资源请拷贝:flutter/app_build/flutter_assets" +release_out_desc = "Release编译完成!产物路径:flutter/app_build/release_out,资源请拷贝:flutter/app_build/flutter_assets" + +dill_out_file = out_path + dill_name +out_kernel_blob_bin = out_path + "kernel_blob.bin" +out_vm_snapshot_data = out_path + "vm_snapshot_data" +out_isolate_snapshot_data = out_path + "isolate_snapshot_data" + +# release产出目录 +release_out_path = "./release_out/" +release_dill_out_file = release_out_path + dill_name + +# 缓存目录,在flutter的外层目录,因为 Cannot create a project within the Flutter SDK. Target directory '/home/xuchang/code/flutter/app_build/cache/hello_world' is within the Flutter SDK at '/home/xuchang/code/flutter'. +cache_dir = "../../.cache/" +cache_project_name = "hello_world" +cache_project_path = cache_dir + cache_project_name +cache_project_dart_tool = cache_project_path + "/.dart_tool" + +def getFlutterAssetsPath(): + return project_root + "/build/flutter_assets" +flutter_assets_target_path = "./flutter_assets" diff --git a/app_build/debug.py b/app_build/debug.py new file mode 100644 index 0000000000..350ae37213 --- /dev/null +++ b/app_build/debug.py @@ -0,0 +1,36 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +#!/usr/bin/python +import file_util +import config +import build_dill +import sys + +# 拷贝和重命名产物 +def copyOut(): + file_util.copyFile(config.dill_out_file, config.out_kernel_blob_bin) + file_util.copyFile(config.getVmIsolateSnapshotBin(), + config.out_vm_snapshot_data) + file_util.copyFile(config.getIsolateSnapshotBin(), + config.out_isolate_snapshot_data) + + +if not config.checkArgs(): + sys.exit(1) + +if not build_dill.buildDill(): + print("编译失败!") +else: + copyOut() + print(config.debug_out_desc) diff --git a/app_build/env_util.py b/app_build/env_util.py new file mode 100644 index 0000000000..8fbc139d88 --- /dev/null +++ b/app_build/env_util.py @@ -0,0 +1,30 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +#!/usr/bin/python +import os +# import excute_util + + +def setEnv(key, value): + os.environ[key] = value + print("修改环境变量{}值为{}".format(key, value)) + + +def getEnv(key): + return os.environ.get(key) + + +# setEnv("PUB_HOSTED_URL", "123") +# print(getEnv("PUB_HOSTED_URL")) +# excute_util.excute("echo $PUB_HOSTED_URL") diff --git a/app_build/excute_util.py b/app_build/excute_util.py new file mode 100644 index 0000000000..6dbd289833 --- /dev/null +++ b/app_build/excute_util.py @@ -0,0 +1,69 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +import subprocess +from datetime import datetime + +# 执行字符串命令行 +def excute(cmdStr, path="."): + if (path != "."): + print("下个指令执行目录:"+path) + # 创建子进程 + proc = subprocess.Popen(cmdStr, cwd=path, shell=True, + stdout=subprocess.PIPE, text=True) + return excuteProgress(cmdStr, proc) + +# 执行数组命令 +def excuteArr(cmdArr): + # 创建子进程 + proc = subprocess.Popen(cmdArr, stdout=subprocess.PIPE, text=True) + str = ' '.join(cmdArr) + return excuteProgress(str, proc) + + +def getDateStr(date): + hour = date.hour + minute = date.minute + second = date.second + return "{}时{}分{}秒".format(hour, minute, second) + + +def excuteProgress(cmdStr, proc): + start_time = datetime.now() + print("开始执行命令:{} ,开始时间:{}".format(cmdStr, getDateStr(start_time))) + result = "" + # 逐行读取输出结果 + for line in proc.stdout: + data = line.strip() + print(data) + result += data + # 等待子进程结束 + proc.wait() + end_time = datetime.now() + print("执行命令结束:{} ,结束时间:{} ,任务耗时: {}".format( + cmdStr, getDateStr(end_time), end_time - start_time)) + return result + + +# 测试代码 +def testExcuteStr(): + cmdStr = "ls" + excute(cmdStr, path="/") + + +def testExcuteArr(): + cmdArr = ["ping", "baidu.com"] + excuteArr(cmdArr) + +# testExcuteStr() +# testExcuteArr() diff --git a/app_build/file_util.py b/app_build/file_util.py new file mode 100644 index 0000000000..0189665f06 --- /dev/null +++ b/app_build/file_util.py @@ -0,0 +1,30 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +#!/usr/bin/python +import shutil +import os + + +def copyFile(sourceFile, targetFile): + if os.path.exists(targetFile): + os.remove(targetFile) + shutil.copy(sourceFile, targetFile) + print("拷贝从{}到{}结束。".format(sourceFile, targetFile)) + + +def copyFiles(sourceFiles, targetFiles): + if os.path.exists(targetFiles): + shutil.rmtree(targetFiles) + shutil.copytree(sourceFiles, targetFiles) + print("拷贝从{}到{}结束。".format(sourceFiles, targetFiles)) diff --git a/app_build/release.py b/app_build/release.py new file mode 100644 index 0000000000..62427c3eee --- /dev/null +++ b/app_build/release.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. + +#!/usr/bin/python +import config +import build_dill +import excute_util +import sys + +def buildAot(): + cmd = config.gen_snapshot + cmd += " --deterministic " + cmd += " --snapshot_kind=app-aot-elf " + cmd += " --elf={}libapp.so".format(config.release_out_path) + cmd += " --strip " + cmd += config.release_dill_out_file + excute_util.excute(cmd) + + + +if not config.checkArgs(): + sys.exit(1) + +if not build_dill.buildDill(False): + print("编译失败!") +else: + buildAot() + print(config.release_out_desc) -- Gitee