react-native-config: Gradle 4.2.0 (distUrl: 6.7.1) fails with 'buildConfigFields' is final error

Hi,

Latest gradle v4.2.0 has issues with react-native-config:

FAILURE: Build failed with an exception.
--
  |  
  | * Where:
  | Script '/usr/src/app/node_modules/react-native-config/android/dotenv.gradle' line: 90
  |  
  | * What went wrong:
  | Execution failed for task ':app:generateIntegrationBuildConfig'.
  | > The value for property 'buildConfigFields' is final and cannot be changed any further.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 38
  • Comments: 38 (7 by maintainers)

Most upvoted comments

I followed the example and added the line apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" to the top of the app/build.gradle file

I guess that allows the variables to be set before gradle sets them as final, and it worked for me

Great that it’s working for others, I’ve created a PR now. @pedro @luancurti if you can’t see any problems with this could we get it merged please?

With the release of React Native 65, bumping the gradle version is part of the update process. This issue starts to be problematic… Any solutions in sight?

See https://react-native-community.github.io/upgrade-helper/?from=0.64.2&to=0.65.1

I’ve ran into the same issue when I’ve updated my project to latest Gradle plugin versions.

I solved the issue by removing in file /node_modules/react-native-config/android/dotenv.gradle the following lines :

tasks.whenTaskAdded { task ->
    if (project.hasProperty("envConfigFiles")) {
        project.envConfigFiles.each { envConfigName, envConfigFile ->
            if (task.name.toLowerCase() == "generate"+envConfigName+"buildconfig") {
                task.doFirst() {
                    android.applicationVariants.all { variant ->
                        def variantConfigString = variant.getName()
                        if (envConfigName.contains(variantConfigString.toLowerCase())) {
                            loadDotEnv(envConfigName)
                            >> project.env.each { k, v ->
                            >>      def escaped = v.replaceAll("%","\\\\u0025")
                            >>      variant.buildConfigField "String", k, "\"$v\""
                            >>      variant.resValue "string", k, "\"$escaped\""
                            >>  }
                        }
                    }
                }
            }
        }
    }
}

I was tinkering with dependencies and got different errors depending on versions used. Solution that worked for me was upgrading PATCH version of build tools (following error for queries https://stackoverflow.com/questions/62969917/how-to-fix-unexpected-element-queries-found-in-manifest-error). Before that I was getting buildConfigFields error. I was using classpath(“com.android.tools.build:gradle:3.5.3”) and update to 4.0 or 4.2 just created different errors. Upgrading only PATCH version to classpath(“com.android.tools.build:gradle:3.5.4”) resolved the issue. In the link above you can see relative PATCH updates for different major/minor releases including this manifest queries field 3.3.3 3.4.3 3.5.4 3.6.4 4.0.1

Ended up updating SDK, Android Studio, build tools, some RN dependencies in the project (react-reanimated) and got back to square 1 until changed build tools from 3.5.3 to 3.5.4

I downgraded the version of gradle to 4.1.2 and it built and deployed just fine. I’m running on latest RN 0.64 so I’m sure something may be depending on newer gradle 4.2 but I haven’t seen it yet.

As explained here: https://docs.gradle.org/current/userguide/build_lifecycle.html, there are several stages to the Gradle build process. I believe that this is an issue with how react-native-config uses Gradle, not a Gradle issue.

The problem is that react-native-config adds a task.doFirst callback. This callback is created during the configuration stage, but is run during the execution phase. During execution, buildConfigFields cannot be modified, it must be done during the configuration phase. I am wondering whether a possible solution is to just remove the callback to run the code immediately during configuration, like this:

tasks.whenTaskAdded { task ->
    if (project.hasProperty("envConfigFiles")) {
        project.envConfigFiles.each { envConfigName, envConfigFile ->
            if (task.name.toLowerCase() == "generate" + envConfigName + "buildconfig") {
                android.applicationVariants.all { variant ->
                    def variantConfigString = variant.getName()
                    if (envConfigName.contains(variantConfigString.toLowerCase())) {
                        loadDotEnv(envConfigName)
                        project.env.each { k, v ->
                            def escaped = v.replaceAll("%", "\\\\u0025")
                            variant.buildConfigField "String", k, "\"$v\""
                            variant.resValue "string", k, "\"$escaped\""
                        }
                    }
                }
            }
        }
    }
}

I’m just experimenting with this at the moment, I’ll post again if this helps.

I’ve ran into the same issue when I’ve updated my project to latest Gradle plugin versions.

I solved the issue by removing in file /node_modules/react-native-config/android/dotenv.gradle the following lines :

tasks.whenTaskAdded { task ->
    if (project.hasProperty("envConfigFiles")) {
        project.envConfigFiles.each { envConfigName, envConfigFile ->
            if (task.name.toLowerCase() == "generate"+envConfigName+"buildconfig") {
                task.doFirst() {
                    android.applicationVariants.all { variant ->
                        def variantConfigString = variant.getName()
                        if (envConfigName.contains(variantConfigString.toLowerCase())) {
                            loadDotEnv(envConfigName)
                            >> project.env.each { k, v ->
                            >>      def escaped = v.replaceAll("%","\\\\u0025")
                            >>      variant.buildConfigField "String", k, "\"$v\""
                            >>      variant.resValue "string", k, "\"$escaped\""
                            >>  }
                        }
                    }
                }
            }
        }
    }
}

and you still have env after 😃) ?

@jihokim2 I managed to solve the issue for my case. Maybe it could give some insights:

Hope that helps someone else! 😄🚀

Having the same issue from the OP and trying some solutions from this thread didn’t solve the issue. Reading about the opened issue at Gradle repo from @rahulr4 , something seems strange: One of the maintainers said it seems to be related to the Android Gradle plugin and not actually to gradle itself. 🤔

My case was: I once created the project and changed the package_name on some places from com.original.reactnative.package_name to com.caiangums.calculator by hand but totally forgot about

  • AndroidManifest.xml from release and debug variants
  • there’s a sequence of directories inside the android/app/src/{main,debug}/com/original/reactnative/package_name and imports at Kotlin/Java code that I updated to match my new package_name (🔍 Search by: MainActivity.java)

I’m still using gradle version 4.2 to compile the project and now all seems to be working 🙏

Note: package names are purely fictional 😄

Same Issue. Does anyone have a solution?

It seems to have been added here: https://github.com/luggit/react-native-config/commit/be8d77e501733430fddb351bdebddc9d3a736700

There doesn’t seem to be any particular comment on why task.doFirst was used rather than just running it directly.

I’ve ran into the same issue when I’ve updated my project to latest Gradle plugin versions. I solved the issue by removing in file /node_modules/react-native-config/android/dotenv.gradle the following lines :

tasks.whenTaskAdded { task ->
    if (project.hasProperty("envConfigFiles")) {
        project.envConfigFiles.each { envConfigName, envConfigFile ->
            if (task.name.toLowerCase() == "generate"+envConfigName+"buildconfig") {
                task.doFirst() {
                    android.applicationVariants.all { variant ->
                        def variantConfigString = variant.getName()
                        if (envConfigName.contains(variantConfigString.toLowerCase())) {
                            loadDotEnv(envConfigName)
                            >> project.env.each { k, v ->
                            >>      def escaped = v.replaceAll("%","\\\\u0025")
                            >>      variant.buildConfigField "String", k, "\"$v\""
                            >>      variant.resValue "string", k, "\"$escaped\""
                            >>  }
                        }
                    }
                }
            }
        }
    }
}

and you still have env after 😃) ?

Yes, I’ve just tested my app and everything is working fine ! 😃