Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

启用 Hermes #26

Closed
zhiqingchen opened this issue Dec 2, 2021 · 3 comments
Closed

启用 Hermes #26

zhiqingchen opened this issue Dec 2, 2021 · 3 comments

Comments

@zhiqingchen
Copy link
Member

zhiqingchen commented Dec 2, 2021

文档

https://reactnative.dev/docs/hermes

问题

  • Taro RN 在编译时,没有使用 react-native/scripts/react-native-xcode.shnode_modules/react-native/react.gradle
    以至于没有进行字节码的打包,相关内容需要进行重构
  • 入口文件通过 --entry-file 传入 node_modules/metro/src/node-haste/DependencyGraph/assets/empty-module.js
@charlzyx
Copy link

charlzyx commented Feb 14, 2022

补充一点 Android 相关的 gradle 分析

简要介绍: (个人理解, 非官方) Taro RN 暂时没有支持 Hermes 是因为有一部分字节码打包操作是在 node_modules/react-native/react.gradle 这里, 但是在 Taro RN 中的 bundle 和 assets 资源的处理是在 rn-runner 里面做的处理, 所以需要

  1. rn-runner 添加对应 hermes 处理
  2. 修改 node_modules/react-native/react.gradle 来自行配置

先看下 react-native 默认项目的 app/build.gralde 中相关打包配置

有个老外的解释 Bundling React Native during Android release builds

/**
 * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
 * and bundleReleaseJsAndAssets).
 * These basically call `react-native bundle` with the correct arguments during the Android build
 * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
 * bundle directly from the development server. Below you can see all the possible configurations
 * and their defaults. If you decide to add a configuration block, make sure to add it before the
 * `apply from: "../../node_modules/react-native/react.gradle"` line.
 *
 * project.ext.react = [
 *   // the name of the generated asset file containing your JS bundle
 *   bundleAssetName: "index.android.bundle",
 *
 *   // the entry file for bundle generation
 *   entryFile: "index.android.js",
 *
 *   // whether to bundle JS and assets in debug mode
 *   bundleInDebug: false,
.
.
.
*/

稍微翻译一下 node_modules/react-native/react.gradle

概述:

  1. 读取一堆配置, 包括项目目录, react-native cli 可执行文件, hermesc 可执行文件, node 可执行文件等路径
  2. 跑一下 react-native bundle 命令, 生成一下 jsbundle (index.android.bundle) 跟 对应的 sourcemap (index.android.bundle.map)
  3. 如果开启 hermes 的话, 就把生成的 jsbundle 文件转换成 hermes 的字节码文件(hbc), 同时处理一下 sourcemap (如果需要的话)
  4. 打包完成, copy 对应的 assets 与 jsbundle 文件

第3条详细说一下:

3.1 如果开启 hermes, 就把 minify 关掉, 因为没必要, (但是不关闭其实也可以)
3.2 找到 HermesCompiler 可执行文件 (hermesc) node_modules/hermes-engine/osx-bin/hermesc // osx-bin 根据平台修改
3.3 执行参数 hermesc -emit-binary -out <字节码文件输出路径> <jsbundle文件路径>
3.4 如果开启了 sourcemap 选项 ["-O", "--out-source-map"] (gradle 里面那一段), 就追加一个参数, 3.3 中的命令变为
hermesc -emit-binary -out <字节码文件输出路径> <jsbundle文件路径> --out-source-map // 注意, 这里的sourcemap 输出无法自定义名称, 会生成 <jsbundle文件路径.hbc.map> 这种格式(大概, 没仔细验证)
3.5 使用 node_modules/react-native/scripts/compose-source-maps.js 这个小工具, 来拼装 jsbundle.mapcompiler.hbc.map , 整体命令如: node ./node_modules/react-native/scripts/compose-source-maps.js jsbundle.map <jsbundle文件路径.hbc.map> -o <输出合并之后的sourcemap>

至此, 可以获得一个新的, 对应 hbc 代码的 sourcemap, 在下方有一个实现, 但同样没有验证 :XD

题外话, metro 中也包含了一个 metro-hermes-compiler, 也是一个简单封装, 可以参考

/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import org.apache.tools.ant.taskdefs.condition.Os
/* 追加注释:: 读取 app/build.gradle 中的配置 */
def config = project.hasProperty("react") ? project.react : [:];

/* 追加注释:: 查找入口文件, 优先级 ENV.ENTRY_FILE -> config.entryFile ->  ${projectDir}/../../index.android.js -> index.js */
def detectEntryFile(config) {
    if (System.getenv('ENTRY_FILE')) {
        return System.getenv('ENTRY_FILE')
    } else if (config.entryFile) {
        return config.entryFile
    } else if ((new File("${projectDir}/../../index.android.js")).exists()) {
        return "index.android.js"
    }

    return "index.js";
}

/* 追加注释:: 查找 react-native 命令所在位置, 优先级 config.cliPath ->  ${projectDir}/../../node_modules/react-native/cli.js  */
/**
 * Detects CLI location in a similar fashion to the React Native CLI
 */
def detectCliPath(config) {
    if (config.cliPath) {
        return "${projectDir}/${config.cliPath}"
    }
    if (new File("${projectDir}/../../node_modules/react-native/cli.js").exists()) {
        return "${projectDir}/../../node_modules/react-native/cli.js"
    }
    throw new Exception("Couldn't determine CLI location. " +
            "Please set `project.ext.react.cliPath` to the path of the react-native cli.js");
}

/* 追加注释:: 拼装 sourcemap 的小工具 */
def composeSourceMapsPath = config.composeSourceMapsPath ?: "node_modules/react-native/scripts/compose-source-maps.js"
/* 追加注释:: bundle的名称, TODO: 分包处理这里需要改动 */
def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"
/* 追加注释:: 调用上面函数获取配置 */
def entryFile = detectEntryFile(config)
/* 追加注释:: 打包命令名称, 默认 bundle */
def bundleCommand = config.bundleCommand ?: "bundle"
/* 追加注释:: 根路径 */
def reactRoot = file(config.root ?: "../../")
/* 追加注释:: 排除路径, TODO: 分包处理这块也需要改动 */
def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]
/* 追加注释:: 打包配置 */
def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ;
/* 追加注释:: 开启Vm CleanUp, 下面有用, 类似resetCache? */
def enableVmCleanup = config.enableVmCleanup == null ? true : config.enableVmCleanup
/* 追加注释:: hermesc 命令所在位置, 根据系统区分 */
def hermesCommand = config.hermesCommand ?: "../../node_modules/hermes-engine/%OS-BIN%/hermesc"

/* 追加注释:: 获取 React Native Dev Server 端口 */
def reactNativeDevServerPort() {
    def value = project.getProperties().get("reactNativeDevServerPort")
    return value != null ? value : "8081"
}

/* 追加注释:: 获取 React Native 开发工具 Server 端口 */
def reactNativeInspectorProxyPort() {
    def value = project.getProperties().get("reactNativeInspectorProxyPort")
    return value != null ? value : reactNativeDevServerPort()
}

/* 追加注释:: 获取上面 hermsCommons 会用到的 %OS-BIN% 变量 */
def getHermesOSBin() {
    if (Os.isFamily(Os.FAMILY_WINDOWS)) return "win64-bin";
    if (Os.isFamily(Os.FAMILY_MAC)) return "osx-bin";
    if (Os.isOs(null, "linux", "amd64", null)) return "linux64-bin";
    throw new Exception("OS not recognized. Please set project.ext.react.hermesCommand " +
            "to the path of a working Hermes compiler.");
}

/* 追加注释:: 替换一下定义中的 %OS-BIN% 变量, 获取真正的 hermesc 可执行文件路径 */
/* 追加注释:: 原注释内容大意为, 确保 herems 配置不会被 inspect, 只有 JSC 可以 remote debugger, 但实际上并不是这里做的操作, 蜜汁注释 */
// Make sure not to inspect the Hermes config unless we need it,
// to avoid breaking any JSC-only setups.
def getHermesCommand = {
    // If the project specifies a Hermes command, don't second guess it.
    if (!hermesCommand.contains("%OS-BIN%")) {
        return hermesCommand
    }

    // Execution on Windows fails with / as separator
    return hermesCommand
            .replaceAll("%OS-BIN%", getHermesOSBin())
            .replace('/' as char, File.separatorChar);
}

/* 追加注释:: enableHermesForVariant 是函数的话, 就执行一下, 否则, 就读取 true/false 值 */
// Set enableHermesForVariant to a function to configure per variant,
// or set `enableHermes` to True/False to set all of them
def enableHermesForVariant = config.enableHermesForVariant ?: {
    def variant -> config.enableHermes ?: false
}

/* 追加注释:: 跟上面意思差不多, 区分一下 debug/release, release 会追加一下 sourcemap输出 */
// Set hermesFlagsForVariant to a function to configure per variant,
// or set `hermesFlagsRelease` and `hermesFlagsDebug` to an array
def hermesFlagsForVariant = config.hermesFlagsForVariant ?: {
    def variant ->
        def hermesFlags;
        if (variant.name.toLowerCase().contains("release")) {
            // Can't use ?: since that will also substitute valid empty lists
            hermesFlags = config.hermesFlagsRelease
            if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"]
        } else {
            hermesFlags = config.hermesFlagsDebug
            if (hermesFlags == null) hermesFlags = []
        }
        return hermesFlags
}
/* 追加注释:: 跟上面意思也差不多,deleteDebugFilesForVariant 不是函数的话, deleteDebugFiles 就靠 variant名字是不是 release 来判断*/
// Set deleteDebugFilesForVariant to a function to configure per variant,
// defaults to True for Release variants and False for debug variants
def deleteDebugFilesForVariant = config.deleteDebugFilesForVariant ?: {
    def variant -> variant.name.toLowerCase().contains("release")
}

/* 追加注释::  获取React Native DevServer 跟 inspector Proxy Port (remote Debugger)用 */
android {
    buildTypes.all {
        resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort()
        resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort()
    }
}

afterEvaluate {
    def isAndroidLibrary = plugins.hasPlugin("com.android.library")
    def variants = isAndroidLibrary ? android.libraryVariants : android.applicationVariants
    variants.all { def variant ->
        /* 追加注释:: targetName 通常是 Debug | Release */
        // Create variant and target names
        def targetName = variant.name.capitalize()
        def targetPath = variant.dirName
        /* 追加注释::
         * TODO: 这里要大改这一堆配置要大改了, 现在大多数都在都在 config/rn 字段里面
         */
        // React js bundle directories
        /* 追加注释:: bundle 输出目录,  然而, Taro Rn 的bundle 并不是这里, 所以emm, 要改 */
        def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}")
        /* 追加注释:: 资源目录,  然而, Taro Rn 的资源配置也不在这里, 所以emm, 要改 */
        def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")
        /* 追加注释:: sourcemap 文件名称,  emm TODO: 分包要改这里 */
        def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
        /* 追加注释:: sourcemap 文件名称,  emm TODO: 分包要改这里 */
        def jsSourceMapsDir = file("$buildDir/generated/sourcemaps/react/${targetPath}")
        /* 追加注释:: FIXME: 没看懂这个是什么 */
        def jsIntermediateSourceMapsDir = file("$buildDir/intermediates/sourcemaps/react/${targetPath}")
        /* 追加注释:: 这两个emm看不懂啊!!, 盲猜是 js 打包的mapper 跟 字节码的 mapper */
        def jsPackagerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.packager.map")
        def jsCompilerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.compiler.map")
        /* 追加注释:: 由上面两个 mapper 合并起来的 mapper */
        def jsOutputSourceMapFile = file("$jsSourceMapsDir/${bundleAssetName}.map")

        /* 追加注释:: 添加 node 命令到cli, 以及 react-native 这个命令 */
        // Additional node and packager commandline arguments
        def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"]
        def cliPath = detectCliPath(config)

        def execCommand = []

        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            execCommand.addAll(["cmd", "/c", *nodeExecutableAndArgs, cliPath])
        } else {
            execCommand.addAll([*nodeExecutableAndArgs, cliPath])
        }

        /* 追加注释:: 是否开启 hermes, 从配置中读取 */
        def enableHermes = enableHermesForVariant(variant)

        /* 追加注释:: 创建 bundle${targetName}JsAndAssets Gradle 任务  */
        def currentBundleTask = tasks.create(
                name: "bundle${targetName}JsAndAssets",
                type: Exec) {
            group = "react"
            description = "bundle JS and assets for ${targetName}."

            /* 追加注释:: 清理文件夹与创建初始文件  */
            // Create dirs if they are not there (e.g. the "clean" task just ran)
            doFirst {
                jsBundleDir.deleteDir()
                jsBundleDir.mkdirs()
                resourcesDir.deleteDir()
                resourcesDir.mkdirs()
                jsIntermediateSourceMapsDir.deleteDir()
                jsIntermediateSourceMapsDir.mkdirs()
                jsSourceMapsDir.deleteDir()
                jsSourceMapsDir.mkdirs()
            }

            // Set up inputs and outputs so gradle can cache the result
            inputs.files fileTree(dir: reactRoot, excludes: inputExcludes)
            outputs.dir(jsBundleDir)
            outputs.dir(resourcesDir)

            // Set up the call to the react-native cli
            workingDir(reactRoot)

            // Set up dev mode
            def devEnabled = !(config."devDisabledIn${targetName}"
                    || targetName.toLowerCase().contains("release"))

            def extraArgs = []

            /* 追加注释:: 下面一堆都是设置 react-native bundle 命令参数, 条件判断追加一堆  */
            if (bundleConfig) {
                extraArgs.add("--config")
                extraArgs.add(bundleConfig)
            }

            // Hermes doesn't require JS minification.
            if (enableHermes && !devEnabled) {
                extraArgs.add("--minify")
                extraArgs.add("false")
            }

            if (config.extraPackagerArgs) {
                extraArgs.addAll(config.extraPackagerArgs)
            }

            commandLine(*execCommand, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",
                    "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir,
                    "--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs)

            /* 追加注释:: 重点来了, 如果开始了 enableHermes */
            if (enableHermes) {
                doLast {
                    /* 追加注释:: 从配置中读取一下 hermes 相关的配置 */
                    def hermesFlags = hermesFlagsForVariant(variant)
                    /* 追加注释:: 要输出的 hbc 字节码文件临时文件名 */
                    def hbcTempFile = file("${jsBundleFile}.hbc")
                    /* 追加注释:: 拼装执行命令 */
                    exec {
                        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                            commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
                        } else {
                            commandLine(getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
                        }
                    }
                    /* 追加注释:: 字节码转换完毕之后, 将字节码文件替换原来的 bundle 文件 */
                    ant.move(
                            file: hbcTempFile,
                            toFile: jsBundleFile
                    );
                    /* 追加注释:: 处理 sourcemap , 如果需要的话 */
                    if (hermesFlags.contains("-output-source-map")) {
                        ant.move(
                                // Hermes will generate a source map with this exact name
                                file: "${jsBundleFile}.hbc.map",
                                tofile: jsCompilerSourceMapFile
                        );
                        exec {
                            // TODO: set task dependencies for caching

                            // Set up the call to the compose-source-maps script
                            workingDir(reactRoot)
                            if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                                commandLine("cmd", "/c", *nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
                            } else {
                                commandLine(*nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
                            }
                        }
                    }
                }
            }

            /* 追加注释:: 是否启用这个任务, 也就是最上面的那个小例子里面的 bundleInDebug | bundleInRelease, 当然, Taro RN 目前(3.4.1) 并不能直接启用 */
            enabled config."bundleIn${targetName}" != null
                    ? config."bundleIn${targetName}"
                    : config."bundleIn${variant.buildType.name.capitalize()}" != null
                    ? config."bundleIn${variant.buildType.name.capitalize()}"
                    : targetName.toLowerCase().contains("release")
        }

        /* 追加注释:: 整理一下本任务[bundle${targetName}JsAndAssets]对外开放的最小接口 */
        // Expose a minimal interface on the application variant and the task itself:
        variant.ext.bundleJsAndAssets = currentBundleTask
        currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
        currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)

        /* 追加注释:: 注册资源生成资源文件目录, for Android plugin 3.x */
        // registerGeneratedResFolders for Android plugin 3.x
        if (variant.respondsTo("registerGeneratedResFolders")) {
            variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)
        } else {
            variant.registerResGeneratingTask(currentBundleTask)
        }
        /* 追加注释:: 啊, mergeResourcesProvider 依赖我 [bundle${targetName}JsAndAssets] */
        variant.mergeResourcesProvider.get().dependsOn(currentBundleTask)

        /* 追加注释:: 啊找找名为 packageApplication 的task  */
        // packageApplication for Android plugin 3.x
        def packageTask = variant.hasProperty("packageApplication")
                ? variant.packageApplicationProvider.get()
                : tasks.findByName("package${targetName}")
        if (variant.hasProperty("packageLibrary")) {
            packageTask = variant.packageLibrary
        }

        /* 追加注释:: 啊找找名为 buildPreBundleTask 的task  */
        // pre bundle build task for Android plugin 3.2+
        def buildPreBundleTask = tasks.findByName("build${targetName}PreBundle")

        /* 追加注释:: 
         * 啊找找名为 resourcesDirConfigValue 的配置, 名字类似 resourcesDirDebug | resourcesDirRelease 这样 
         * 如果存在的话, 就 copy 一下 资源文件们
         */
        def resourcesDirConfigValue = config."resourcesDir${targetName}"
        if (resourcesDirConfigValue) {
           /* 追加注释:: 如果有的话, 就创建一个 [copy${targetName}BundledResources] 的任务, 把资源文件copy 一下 */
            def currentCopyResTask = tasks.create(
                    name: "copy${targetName}BundledResources",
                    type: Copy) {
                group = "react"
                description = "copy bundled resources into custom location for ${targetName}."

                from(resourcesDir)
                into(file(resourcesDirConfigValue))

                dependsOn(currentBundleTask)

                enabled(currentBundleTask.enabled)
            }

            packageTask.dependsOn(currentCopyResTask)
            if (buildPreBundleTask != null) {
                buildPreBundleTask.dependsOn(currentCopyResTask)
            }
        }

        /* 
         * 追加注释: 创建一个 [copy${targetName}BundledJs] 的任务, 把 bundle 文件copy 一下, 
         * 一如既往, 包含一堆判断
         */
        def currentAssetsCopyTask = tasks.create(
                name: "copy${targetName}BundledJs",
                type: Copy) {
            group = "react"
            description = "copy bundled JS into ${targetName}."

            if (config."jsBundleDir${targetName}") {
                from(jsBundleDir)
                into(file(config."jsBundleDir${targetName}"))
            } else {
                into ("$buildDir/intermediates")
                if (isAndroidLibrary) {
                    into ("library_assets/${variant.name}/out") {
                        from(jsBundleDir)
                    }
                } else {
                    into ("assets/${targetPath}") {
                        from(jsBundleDir)
                    }

                    // Workaround for Android Gradle Plugin 3.2+ new asset directory
                    into ("merged_assets/${variant.name}/merge${targetName}Assets/out") {
                        from(jsBundleDir)
                    }

                    // Workaround for Android Gradle Plugin 3.4+ new asset directory
                    into ("merged_assets/${variant.name}/out") {
                        from(jsBundleDir)
                    }
                }
            }

            // mergeAssets must run first, as it clears the intermediates directory
            dependsOn(variant.mergeAssetsProvider.get())

            enabled(currentBundleTask.enabled)
            dependsOn(currentBundleTask)
        }

        /* 
         * 追加注释:: 如果是 Android plugin 4.1+ 的话, 
         * 就把上面  资源 assets copy 的操作追加到这个 mergeResourcesTask [merge${targetName}Resources] 这货的依赖
         * 如果存在 buildPreBundleTask, 就追加到 buildPreBundleTask 的依赖 (Android plugin 3.x?) 
         */
        // mergeResources task runs before the bundle file is copied to the intermediate asset directory from Android plugin 4.1+.
        // This ensures to copy the bundle file before mergeResources task starts
        def mergeResourcesTask = tasks.findByName("merge${targetName}Resources")
        mergeResourcesTask.dependsOn(currentAssetsCopyTask)

        packageTask.dependsOn(currentAssetsCopyTask)
        if (buildPreBundleTask != null) {
            buildPreBundleTask.dependsOn(currentAssetsCopyTask)
        }

        /* 
         * 追加注释:: 删掉一些不用的 .so 文件, 如果 'enableVmCleanup: true' 的话
         * 比如 hermes 的 release 删掉 debug 相关, jsc 的话, 就把 所有 hemres 相关删掉
         * 用来减小包的体积, 所以动态修改 JS 引擎, 至少在配置上是可行的(修改这里)
         */ 
        // Delete the VM related libraries that this build doesn't need.
        // The application can manage this manually by setting 'enableVmCleanup: false'
        //
        // This should really be done by packaging all Hermes related libs into
        // two separate HermesDebug and HermesRelease AARs, but until then we'll
        // kludge it by deleting the .so files out of the /transforms/ directory.
        def cleanup = deleteDebugFilesForVariant(variant)

        def vmSelectionAction = { libDir ->
            fileTree(libDir).matching {
                if (enableHermes) {
                    // For Hermes, delete all the libjsc* files
                    include "**/libjsc*.so"

                    if (cleanup) {
                        // Reduce size by deleting the debugger/inspector
                        include '**/libhermes-inspector.so'
                        include '**/libhermes-executor-debug.so'
                        include '**/libhermes-executor-common-debug.so'
                    } else {
                        // Release libs take precedence and must be removed
                        // to allow debugging
                        include '**/libhermes-executor-release.so'
                        include '**/libhermes-executor-common-release.so'
                    }
                } else {
                    // For JSC, delete all the libhermes* files
                    include "**/libhermes*.so"
                }
            }.visit { details ->
                def targetVariant1 = ".*/transforms/[^/]*/${targetPath}/.*"
                def targetVariant2 = ".*/merged_native_libs/${targetPath}/out/lib/.*"
                def targetVariant3 = ".*/stripped_native_libs/${targetPath}/out/lib/.*"
                def path = details.file.getAbsolutePath().replace(File.separatorChar, '/' as char)
                if ((path.matches(targetVariant1) || path.matches(targetVariant2) || path.matches(targetVariant3)) && details.file.isFile()) {
                    details.file.delete()
                }
            }
        }
        
        /* 
         * 追加注释:: 具体的clean 操作
         */ 
        if (enableVmCleanup) {
            def task = tasks.findByName("package${targetName}")
            def transformsLibDir = "$buildDir/intermediates/transforms/"
            task.doFirst { vmSelectionAction(transformsLibDir) }

            def sTask = tasks.findByName("strip${targetName}DebugSymbols")
            if (sTask != null) {
                def strippedLibDir = "$buildDir/intermediates/stripped_native_libs/${targetPath}/out/lib/"
                sTask.doLast { vmSelectionAction(strippedLibDir) }
            }

            def mTask = tasks.findByName("merge${targetName}NativeLibs")
            if (mTask != null) {
                def mergedLibDir = "$buildDir/intermediates/merged_native_libs/${targetPath}/out/lib/"
                mTask.doLast { vmSelectionAction(mergedLibDir) }
            }
        }
    }
}

JS 版本的实现

const hermesCLIPath = {
  // hermesCommand="./node_modules/hermes-engine/osx-bin/hermesc";
  // # composeSourceMapsPath="./node_modules/react-native/scripts/compose-source-maps.js";
  // for mac 其他系统先不写, osx-bin 这一段替换为对应系统就行, 可以通过 os.platform 判断
  hermesCommandPath: require.resolve('hermes-engine/osx-bin/hermesc'),
  composeSourceMapsPath: require.resolve(
    'react-native/scripts/compose-source-maps.js'
  )
}

/** 
 * hermes hbc 编译 && sourmcemap 处理 
 **/
const processHermesHBCAndSourcemap = (
  bundleFileName: string,
  bundleSourcemapFileName?: string
) => {
  const hbcOutput = bundleFileName + '.hbc'

  let hbcCLI = `${hermesCLIPath.hermesCommandPath} -emit-binary -out ${hbcOutput} ${bundleFileName}`
  try {
    if (bundleSourcemapFileName) {
      /**
       * -output-source-map 这个参数输出的 map,
       * 会直接在 bundle 名称之后追展现一个 hbc.map: (待验证), 不支持自定义名称
       **/
      const compilerSourcemap = bundleFileName + '.hbc.map'

      const hermesSourceMapOutput = compilerSourcemap.replace(
        '.map',
        '.hermes.map'
      )
      hbcCLI += ' -output-source-map'
      const composeSourcemap = `node ${hermesCLIPath.composeSourceMapsPath} ${bundleSourcemapFileName} ${compilerSourcemap} -o ${hermesSourceMapOutput}`

      console.log(`RUN CLI:: ${hbcCLI}`)
      spawnSync(hbcCLI, { shell: true, stdio: 'inherit' })
      console.log(`RUN CLI:: ${composeSourcemap}`)
      spawnSync(composeSourcemap, { shell: true, stdio: 'inherit' })
    } else {
      console.log(`RUN CLI:: ${hbcCLI}`)
      spawnSync(hbcCLI, { shell: true, stdio: 'inherit' })
    }
  } catch (error) {
    console.log('hermes hbc package error ', error)
    console.trace(error)
  }
}

@snice
Copy link

snice commented Feb 24, 2023

1.8.0开始已经默认用hermes了吧

@zhiqingchen
Copy link
Member Author

是的,Hermes可自行开启或关闭,使用方案与 RN 一致,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants