React Native 构建错误:文本不能为空或为空

Posted

技术标签:

【中文标题】React Native 构建错误:文本不能为空或为空【英文标题】:React Native build error: Text must not be null or empty 【发布时间】:2019-12-09 09:14:11 【问题描述】:

我的 Jenkins 版本给了我以下错误:

13:18:22 FAILURE: Build failed with an exception.
13:18:22 
13:18:22 * Where:
13:18:22 Script '/Users/abcd/Jenkins/Jenkins-Workspaces/ABCD/ABCDL/node_modules/@react-native-community/cli-platform-android/native_modules.gradle' line: 190
13:18:22 
13:18:22 * What went wrong:
13:18:22 A problem occurred evaluating settings 'AppName'.
13:18:22 > Text must not be null or empty
13:18:22 

似乎问题出在@react-native-community/cli-platform 节点模块上,但请阅读这个已关闭的问题: https://github.com/facebook/react-native/issues/25479

我不清楚提出的和最终的解决方案到底是什么。

在这个 react-native 问题中有一个更直接的修复建议: https://github.com/facebook/react-native/issues/25822

但我的错误不是抱怨那条线。

至于安装@react-native-community/cli,我相信我的package-lock.json 文件中已经有了它:

"react-native": 
      "version": "0.60.4",
      "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.60.4.tgz",
      "integrity": "sha512-WE41lbGQjnzM9srIFtMDtMJkQAvk95iZwuFvAxl68s80bkYa7Ou9sGFHpeYIV6cY8yHtheCSo5q6YMxhdfkdOw==",
      "requires": 
        "@babel/runtime": "^7.0.0",
        "@react-native-community/cli": "^2.0.1",
        "@react-native-community/cli-platform-android": "^2.0.1",
        "@react-native-community/cli-platform-ios": "^2.0.1",

其他人提到了一些关于app/build.gradle的事情,这是我的相关部分:

// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) 
    from configurations.compile
    into 'libs'


apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

还提到了android/settings.gradle,这个是我的:

rootProject.name = 'NFIBEngage'
include ':react-native-device-info'
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
include ':appcenter-crashes'
project(':appcenter-crashes').projectDir = new File(rootProject.projectDir, '../node_modules/appcenter-crashes/android')
include ':appcenter-analytics'
project(':appcenter-analytics').projectDir = new File(rootProject.projectDir, '../node_modules/appcenter-analytics/android')
include ':appcenter'
project(':appcenter').projectDir = new File(rootProject.projectDir, '../node_modules/appcenter/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'

根据我在这里收集的内容:

https://react-native-community.github.io/upgrade-helper/?from=0.53.3&to=0.60.4

以上文件正确。

那么这里到底出了什么问题,我该如何解决呢?

node_modules/@react-native-community/cli-platform-android/native_modules.gradle line 190来说是这个:

def json = new JsonSlurper().parseText(reactNativeConfigOutput)

问题可能出在我如何编写 index.js 文件:

/**
 * @format
 */

import  AppRegistry  from "react-native";
// old config code
import KeyboardManager from "react-native-keyboard-manager";
// old config code ^^^
import NFIBEngage from "./App";
import  name as appName  from "./app.json";

// old config code
import  Sentry  from "react-native-sentry";

Sentry.config(
  "https://asdf@sentry.io/123456677"
).install();

KeyboardManager.setToolbarPreviousNextButtonEnable(true);
// old config code ^^^

AppRegistry.registerComponent("NFIBEngage", () => NFIBEngage);

AppRegistry.registerComponent() 写对了吗?

我在本地运行了 Jenkins 脚本,我相信这个脚本就在这里:

import fs from "fs-extra";
import eachSeries from "async/eachSeries";
import  exec  from "child_process";
import  androidDirectory  from "../../app.json";
import  resolveFromRoot, distDir, createLogger  from "../build";

const logger = createLogger("android");

const APK_PATTERN = /release\.apk$/i;

function copyArtifactsToDist() 
  logger.logHeader("Copying APK to Dist",  repeatChar: "=" );
  const baseDir = `$androidDirectory/app/build/outputs/apk`;

  const allFlavs = ["dev", "qa", "ua", "prod"];
  const branchName = process.env.GitVersion_BranchName || "";
  const buildFlavour = branchName.startsWith("release/") ? allFlavs : ["dev"];
  const envs = 
    dev: "INT",
    qa: "QA",
    ua: "UA",
    prod: ""
  ;

  buildFlavour
    .map(env => 
      const apkOutputDir = resolveFromRoot(`$baseDir/$env/release`);
      return 
        apkOutputDir,
        env
      ;
    )
    .forEach(( apkOutputDir, env ) => 
      const src = `$apkOutputDir/app-$env-release.apk`;
      //prettier-ignore
      const binaryName = env === 'prod' ? 'ENGAL.apk' : `ENGAL-$envs[env].apk`;
      const dest = `$distDir/$binaryName`;
      fs.copy(src, dest, (err: Error) => 
        if (err) 
          logger.error(err);
        
      );
    );


function run() 
  logger.logHeader("Starting Android Builds",  repeatChar: "#" );
  const flavours = [
    
      endpoint: "dv",
      flavour: "Dev",
      appcenterKey: "<hashKeys>"
    ,
    
      endpoint: "qa",
      flavour: "Qa",
      appcenterKey: "<hashKeys>"
    ,
    
      endpoint: "ua",
      flavour: "Ua",
      appcenterKey: "<hashKeys>"
    ,
    
      endpoint: "prod",
      flavour: "Prod",
      appcenterKey: "<hashKeys>"
    
  ];

  const versionCode = process.env.Build || 1;
  const release = process.env.GitVersion_MajorMinorPatch || "1.0.0";
  const fullAppVersion = `$release-$versionCode`;

  const devFlav = flavours.find(f => f.flavour.toLocaleLowerCase() === "dev");

  const branchName = process.env.GitVersion_BranchName || "";
  const buildFlavour = branchName.startsWith("release/") ? flavours : [devFlav];

  eachSeries(
    buildFlavour,
    (f, callback) => 
      //prettier-ignore
      logger.logHeader(
        `starting gradle assemble$f.flavourRelease with flag - versionName=$fullAppVersion -PversionCode=$versionCode`,
        repeatChar: '-'
      );

      const engaInfo = `ENGAGE_VERSION=$fullAppVersion`;
      const engaEndpoint = `ENGAGE_ENDPOINT=$f.endpoint`;
      const engaCenter = `APPCENTER_KEY=$f.appcenterKey`;
      const engaPlatform = "APPCENTER_PLATFORM=android";
      //prettier-ignore
      const prepare = `$engaEndpoint $engaCenter $engaInfo $engaPlatform npm run setup`;
      const cd = `cd $androidDirectory`;
      //prettier-ignore
      const releaseCmd = `./gradlew assemble$f.flavourRelease -PversionName=$fullAppVersion -PversionCode=$versionCode && cd ..`;

      exec(`$prepare && $cd && $releaseCmd`, err => 
        if (err) 
          return callback(err);
        

        logger.logHeader(`$f.flavour Android Build Successful!`, 
          repeatChar: "#"
        );
        logger.close();
        callback(null);
      );
    ,
    error => 
      if (error) 
        logger.logHeader("Android Builds Failed!", 
          repeatChar: "#"
        );
        logger.error(error);
        logger.close();
      
      copyArtifactsToDist();
    
  );


run();

通过npm run build 在本地我收到此错误:

FAILURE: Build failed with an exception.

* What went wrong:
Task 'assembleDevRelease' not found in root project 'AppName'. Some candidates are: 'assembleRelease'.

这些是相关的错误吗?任何有 React Native 构建经验的人?

按照建议,我查看了我的 android/app/build.gradle 文件中的 productFlavors 并注意到它们确实在此处丢失了:

buildTypes 
        debug 
            signingConfig signingConfigs.debug
        
        release 
            // Caution! In production, you need to generate your own keystore file.
            // see https://facebook.github.io/react-native/docs/signed-apk-android.
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        
    

    // applicationVariants are e.g. debug, release
    applicationVariants.all  variant ->
        variant.outputs.each  output ->
            // For each separate APK per architecture, set a unique version code as described here:
            // https://developer.android.com/studio/build/configure-apk-splits.html
            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null)   // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        versionCodes.get(abi) * 123456 + defaultConfig.versionCode
            

        
    

所以我是这样添加的:

buildTypes 
        debug 
            signingConfig signingConfigs.debug
        
        release 
            // Caution! In production, you need to generate your own keystore file.
            // see https://facebook.github.io/react-native/docs/signed-apk-android.
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        
    

    productFlavors 
      dev 
        resValue "string", "app_name", getAppName("INT")
        resValue "string", "link_launcher", getLauncher("dv")
        applicationIdSuffix ".dv"
        manifestPlaceholders = [onesignal_app_id: "<hash_id>",
                                onesignal_google_project_number: "123456789"]
      
      qa 
        resValue "string", "app_name", getAppName("QA")
        resValue "string", "link_launcher", getLauncher("qa")
        applicationIdSuffix ".qa"
        manifestPlaceholders = [onesignal_app_id: "<hash_id>",
                                onesignal_google_project_number: "123456789"]
      
      ua 
        resValue "string", "app_name", getAppName("UA")
        resValue "string", "link_launcher", getLauncher("ua")
        applicationIdSuffix ".ua"
        manifestPlaceholders = [onesignal_app_id: "<hash_id>",
                                onesignal_google_project_number: "123456789"]
      
      prod 
        resValue "string", "app_name", getAppName()
        resValue "string", "link_launcher", getLauncher()
        manifestPlaceholders = [onesignal_app_id: "<hash_id>",
                                onesignal_google_project_number: "601125149914"]
      
    

    // applicationVariants are e.g. debug, release
    applicationVariants.all  variant ->
        variant.outputs.each  output ->
            // For each separate APK per architecture, set a unique version code as described here:
            // https://developer.android.com/studio/build/configure-apk-splits.html
            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null)   // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
            

        
    

buildTypes 看起来与原来的旧版buildTypes 有点不同,所以我不确定这是否可以,但无论如何我在本地再次运行 npm run build 并收到此错误:

* Where:
Build file '/Users/danale/Projects/NFIBEngage/android/app/build.gradle' line: 168

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not find method getAppName() for arguments [INT] on ProductFlavor_Decoratedname=dev, dimension=null, minSdkVersion=null, targetSdkVersion=null, renderscriptTargetApi=null, renderscriptSupportModeEnabled=null, renderscriptSupportModeBlasEnabled=null, renderscriptNdkModeEnabled=null, versionCode=null, versionName=null, applicationId=null, testApplicationId=null, testInstrumentationRunner=null, testInstrumentationRunnerArguments=, testHandleProfiling=null, testFunctionalTest=null, signingConfig=null, resConfig=null, mBuildConfigFields=, mResValues=, mProguardFiles=[], mConsumerProguardFiles=[], mManifestPlaceholders=, mWearAppUnbundled=null of type com.android.build.gradle.internal.dsl.ProductFlavor.

我能够通过添加缺少的方法来解决本地错误,如下所示:

def appName = "Engage";

/**
 * Get the version name from command line param
 *
 * @return int If the param -PversionName is present then return int value or -1
 */
def getAppName =  env ->
  return (env ? appName + " ("+ env + ")" : appName);


/**
 * Get the version name from command line param
 *
 * @return int If the param -PversionName is present then return int value or -1
 */
def getLauncher =  env ->
    return (env ? "engage-" + env + ".nfib.org" : "engage.nfib.org");


android 
    compileSdkVersion rootProject.ext.compileSdkVersion

    compileOptions 
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    

    flavorDimensions "default"

    defaultConfig 
        applicationId "com.nfib.engage"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
    
    splits 
        abi 
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
        
    
    signingConfigs 
        debug 
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        
    
    buildTypes 
        debug 
            signingConfig signingConfigs.debug
        
        release 
            // Caution! In production, you need to generate your own keystore file.
            // see https://facebook.github.io/react-native/docs/signed-apk-android.
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        
    

    productFlavors 
      dev 
        dimension 'default'
        resValue "string", "app_name", getAppName("INT")
        resValue "string", "link_launcher", getLauncher("dv")
        applicationIdSuffix ".dv"
        manifestPlaceholders = [onesignal_app_id: "b78285eb-f1ec-46f3-9ad0-c7efe691a401",
                                onesignal_google_project_number: "584236827312"]
      
      qa 
        dimension 'default'
        resValue "string", "app_name", getAppName("QA")
        resValue "string", "link_launcher", getLauncher("qa")
        applicationIdSuffix ".qa"
        manifestPlaceholders = [onesignal_app_id: "e4280f5e-62ec-41a4-bd86-f5b94e471a36",
                                onesignal_google_project_number: "162802054510"]
      
      ua 
        dimension 'default'
        resValue "string", "app_name", getAppName("UA")
        resValue "string", "link_launcher", getLauncher("ua")
        applicationIdSuffix ".ua"
        manifestPlaceholders = [onesignal_app_id: "2ffd8dc0-9c6b-4035-999d-fc694194725a",
                                onesignal_google_project_number: "594905904045"]
      
      prod 
        dimension 'default'
        resValue "string", "app_name", getAppName()
        resValue "string", "link_launcher", getLauncher()
        manifestPlaceholders = [onesignal_app_id: "82dcb42f-1d35-4b79-bc28-2d1d02dbda36",
                                onesignal_google_project_number: "601125149914"]
      
    

不幸的是,我在 Jenkins 中继续遇到同样的错误。

【问题讨论】:

检查你的 react-native-community/cli 是否是最新版本。它在 35 分钟前更新。 link @firats,我的项目中什至没有那个包:✗ npm ls @react-native-community/cli NFIBEngage@0.0.1 /Users/danale/Projects/NFIBEngage └── (empty) 实际上您链接中的一些 cmets 指向安装 @react-native-community/cli 作为解决方案。你试过了吗? @firats,我相信我在刚刚添加的内容中解决了您的问题。 从您的 package.json 中,您的 cli 版本位于 ^2.0.12.0.1 确实是存在您从 github.com/facebook/react-native/issues/25479 链接到的问题的 cli 版本您是否验证过您的node_modules/@react-native-community/cli-platform-android/native_modules.gradle 中类似于def command = "../node_modules/.bin/react-native config"(来自github.com/facebook/react-native/issues/…)的行是否正确?您还应该确保安装的 cli 版本 >= 2.0.2。 【参考方案1】:

来自 cmets 的一些使构建工作的更改:

从您的 package.json 中,您的 cli 版本位于 ^2.0.12.0.1 确实是存在您从 github.com/facebook/react-native/issues/25479 链接到的问题的 cli 版本您确认node_modules/@react-native-community/cli-platform-android/native_modules.gradle 中类似于def command = "../node_modules/.bin/react-native config"(来自github.com/facebook/react-native/issues/...)的行是正确的?您还应该确保您安装的 cli 版本 >= 2.0.2

确保您在android/app/build.gradle 中的buildTypesproductFlavors 定义设置为包含您尝试在jenkins 工作中构建的所有不同变体。看起来你有 devqauaprod 的口味。查看 gradle 文档 developer.android.com/studio/build/build-variants#build-types 了解更多信息。

您的build.gradle 中似乎缺少getAppName 函数。像ext.getAppName = suffix = '' -&gt; 'MyAppName' + suffix 这样的东西。快速扫描您的 build.gradle 看起来您需要另一个名为 getLauncher 的字符串,它会为您使用 link_launcher 的任何内容返回一个适当的字符串。

【讨论】:

后面部分已经解决了,我加了def getAppName = env -&gt;def getLauncher = env -&gt;,但是Jenkins中问题依旧。我会更新 OP。 关于cli 的版本,当我尝试这样做时,即使我将其物理更改为2.0.2,它也会继续安装相同的版本。 你在哪里改?在package-lock.json 中的@react-native-community/cli 条目中?我通常使用 yarn 并且没有使用 npm 的内置锁定文件的经验,但请确保您更改的是 cli 包的条目,而不是 react-native 下的条目。 我从package-lock.json 文件中删除它并运行npm install,然后我返回并手动将其更改为所需的版本并运行npm install,两次都安装了我已经拥有的版本. 今天我注意到我的node_modules 中根本没有@react-native-community/cli【参考方案2】:

我在我的 CI 工具上升级了我的节点版本并修复了这个确切的错误。 我之前是第 6 版,后来升级到了 10

【讨论】:

我使用的是节点版本 11,我最近不得不将其降级到 10 才能使用expo-cli【参考方案3】:

我也遇到过这个问题

很容易解决这个问题

移除节点模块

重新安装 node_module : npm install

react-native run-android

【讨论】:

以上是关于React Native 构建错误:文本不能为空或为空的主要内容,如果未能解决你的问题,请参考以下文章

条带错误“资源 ID 不能为空或空格”

React Native Expo - Jest - React Native Firebase - Invariant Violation:Native 模块不能为空

在使用 React Native 的 IOS 运行时出现“未处理的 JS 异常:本机模块不能为空”错误

不变违规:本机模块不能为空。 React Native Firebase 应用程序

引起:java.lang.IllegalArgumentException:模式不能为空或为空

ReactJS - 超级表达式必须为空或函数,而不是未定义