Skip to content

Commit bb02ccf

Browse files
shivenmianfacebook-github-bot
authored andcommitted
RNGP - fix: use relative paths for gradle exec invocations (#36080)
Summary: Pull Request resolved: #36080 For Android release builds on Windows, gradle release build fails if there are spaces in path (#34878). This is due to gradle improperly handling arguments with spaces (this is also [an open issue](gradle/gradle#6072) on Gradle). Since the Hermes compilation and other Gradle exec invocations involve arguments which will contain spaces (if there are spaces in your path), this also means it is hard to get around this by simply escaping the spaces (eg: by using double quotes), since these arguments are not properly handled by Gradle itself. As a workaround, this PR uses relative paths for all Gradle commands invoked for Android. As long as there aren't any spaces in the react-native directory structure (i.e this repo), this fix should work. ## Changelog [Android][Fixed] - Used relative paths for gradle commands Pull Request resolved: #36076 Test Plan: `npx react-native run-android` builds and runs the app successfully on Android device, when run inside an RN0711 project with a path containing spaces (and with the changes in this PR applied) on Windows. This includes release builds (i.e with the `--variant=release` flag). Reviewed By: cipolleschi Differential Revision: D43080177 Pulled By: cortinico fbshipit-source-id: 7625f3502af47e9b28c6fc7dfe1459d7c7f1362d
1 parent 2607602 commit bb02ccf

File tree

9 files changed

+279
-39
lines changed

9 files changed

+279
-39
lines changed

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/BundleHermesCTask.kt

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package com.facebook.react.tasks
99

10+
import com.facebook.react.utils.Os.cliPath
1011
import com.facebook.react.utils.detectOSAwareHermesCommand
1112
import com.facebook.react.utils.moveTo
1213
import com.facebook.react.utils.windowsAwareCommandLine
@@ -135,25 +136,26 @@ abstract class BundleHermesCTask : DefaultTask() {
135136
internal fun getBundleCommand(bundleFile: File, sourceMapFile: File): List<Any> =
136137
windowsAwareCommandLine(
137138
buildList {
139+
val rootFile = root.get().asFile
138140
addAll(nodeExecutableAndArgs.get())
139-
add(cliFile.get().asFile.absolutePath)
141+
add(cliFile.get().asFile.cliPath(rootFile))
140142
add(bundleCommand.get())
141143
add("--platform")
142144
add("android")
143145
add("--dev")
144146
add(devEnabled.get().toString())
145147
add("--reset-cache")
146148
add("--entry-file")
147-
add(entryFile.get().asFile.toString())
149+
add(entryFile.get().asFile.cliPath(rootFile))
148150
add("--bundle-output")
149-
add(bundleFile.toString())
151+
add(bundleFile.cliPath(rootFile))
150152
add("--assets-dest")
151-
add(resourcesDir.get().asFile.toString())
153+
add(resourcesDir.get().asFile.cliPath(rootFile))
152154
add("--sourcemap-output")
153-
add(sourceMapFile.toString())
155+
add(sourceMapFile.cliPath(rootFile))
154156
if (bundleConfig.isPresent) {
155157
add("--config")
156-
add(bundleConfig.get().asFile.absolutePath)
158+
add(bundleConfig.get().asFile.cliPath(rootFile))
157159
}
158160
add("--minify")
159161
add(minifyEnabled.get().toString())
@@ -165,26 +167,30 @@ abstract class BundleHermesCTask : DefaultTask() {
165167
hermesCommand: String,
166168
bytecodeFile: File,
167169
bundleFile: File
168-
): List<Any> =
169-
windowsAwareCommandLine(
170-
hermesCommand,
171-
"-emit-binary",
172-
"-out",
173-
bytecodeFile.absolutePath,
174-
bundleFile.absolutePath,
175-
*hermesFlags.get().toTypedArray())
170+
): List<Any> {
171+
val rootFile = root.get().asFile
172+
return windowsAwareCommandLine(
173+
hermesCommand,
174+
"-emit-binary",
175+
"-out",
176+
bytecodeFile.cliPath(rootFile),
177+
bundleFile.cliPath(rootFile),
178+
*hermesFlags.get().toTypedArray())
179+
}
176180

177181
internal fun getComposeSourceMapsCommand(
178182
composeScript: File,
179183
packagerSourceMap: File,
180184
compilerSourceMap: File,
181185
outputSourceMap: File
182-
): List<Any> =
183-
windowsAwareCommandLine(
184-
*nodeExecutableAndArgs.get().toTypedArray(),
185-
composeScript.absolutePath,
186-
packagerSourceMap.toString(),
187-
compilerSourceMap.toString(),
188-
"-o",
189-
outputSourceMap.toString())
186+
): List<Any> {
187+
val rootFile = root.get().asFile
188+
return windowsAwareCommandLine(
189+
*nodeExecutableAndArgs.get().toTypedArray(),
190+
composeScript.cliPath(rootFile),
191+
packagerSourceMap.cliPath(rootFile),
192+
compilerSourceMap.cliPath(rootFile),
193+
"-o",
194+
outputSourceMap.cliPath(rootFile))
195+
}
190196
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTask.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package com.facebook.react.tasks
99

1010
import com.facebook.react.utils.JsonUtils
11+
import com.facebook.react.utils.Os.cliPath
1112
import com.facebook.react.utils.windowsAwareCommandLine
1213
import org.gradle.api.file.Directory
1314
import org.gradle.api.file.DirectoryProperty
@@ -63,16 +64,17 @@ abstract class GenerateCodegenArtifactsTask : Exec() {
6364
}
6465

6566
internal fun setupCommandLine(libraryName: String, codegenJavaPackageName: String) {
67+
val workingDir = project.projectDir
6668
commandLine(
6769
windowsAwareCommandLine(
6870
*nodeExecutableAndArgs.get().toTypedArray(),
69-
reactNativeDir.file("scripts/generate-specs-cli.js").get().asFile.absolutePath,
71+
reactNativeDir.file("scripts/generate-specs-cli.js").get().asFile.cliPath(workingDir),
7072
"--platform",
7173
"android",
7274
"--schemaPath",
73-
generatedSchemaFile.get().asFile.absolutePath,
75+
generatedSchemaFile.get().asFile.cliPath(workingDir),
7476
"--outputDir",
75-
generatedSrcDir.get().asFile.absolutePath,
77+
generatedSrcDir.get().asFile.cliPath(workingDir),
7678
"--libraryName",
7779
libraryName,
7880
"--javaPackageName",

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenSchemaTask.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package com.facebook.react.tasks
99

10+
import com.facebook.react.utils.Os.cliPath
1011
import com.facebook.react.utils.windowsAwareCommandLine
1112
import org.gradle.api.file.DirectoryProperty
1213
import org.gradle.api.file.RegularFile
@@ -63,18 +64,19 @@ abstract class GenerateCodegenSchemaTask : Exec() {
6364
}
6465

6566
internal fun setupCommandLine() {
67+
val workingDir = project.projectDir
6668
commandLine(
6769
windowsAwareCommandLine(
6870
*nodeExecutableAndArgs.get().toTypedArray(),
6971
codegenDir
7072
.file("lib/cli/combine/combine-js-to-schema-cli.js")
7173
.get()
7274
.asFile
73-
.absolutePath,
75+
.cliPath(workingDir),
7476
"--platform",
7577
"android",
76-
generatedSchemaFile.get().asFile.absolutePath,
77-
jsRootDir.asFile.get().absolutePath,
78+
generatedSchemaFile.get().asFile.cliPath(workingDir),
79+
jsRootDir.asFile.get().cliPath(workingDir),
7880
))
7981
}
8082
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/Os.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
package com.facebook.react.utils
99

10-
object Os {
10+
import java.io.File
11+
12+
internal object Os {
1113

1214
fun isWindows(): Boolean =
1315
System.getProperty("os.name")?.lowercase()?.contains("windows") ?: false
@@ -28,4 +30,15 @@ object Os {
2830
it
2931
}
3032
}
33+
34+
/**
35+
* As Gradle doesn't support well path with spaces on Windows, we need to return relative path on
36+
* Win. On Linux & Mac we'll default to return absolute path.
37+
*/
38+
fun File.cliPath(base: File): String =
39+
if (isWindows()) {
40+
this.relativeTo(base).path
41+
} else {
42+
this.absolutePath
43+
}
3144
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PathUtils.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package com.facebook.react.utils
1111

1212
import com.facebook.react.ReactExtension
1313
import com.facebook.react.model.ModelPackageJson
14+
import com.facebook.react.utils.Os.cliPath
1415
import java.io.File
1516
import org.gradle.api.Project
1617

@@ -130,7 +131,7 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String
130131
val builtHermesc =
131132
getBuiltHermescFile(projectRoot, System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR"))
132133
if (builtHermesc.exists()) {
133-
return builtHermesc.absolutePath
134+
return builtHermesc.cliPath(projectRoot)
134135
}
135136

136137
// 3. If the react-native contains a pre-built hermesc, use it.
@@ -142,7 +143,7 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String
142143

143144
val prebuiltHermes = File(projectRoot, prebuiltHermesPath)
144145
if (prebuiltHermes.exists()) {
145-
return prebuiltHermes.absolutePath
146+
return prebuiltHermes.cliPath(projectRoot)
146147
}
147148

148149
error(

packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/BundleHermesCTaskTest.kt

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
package com.facebook.react.tasks
99

10+
import com.facebook.react.tests.OS
1011
import com.facebook.react.tests.OsRule
12+
import com.facebook.react.tests.WithOs
1113
import com.facebook.react.tests.createTestTask
1214
import java.io.File
1315
import org.junit.Assert.*
@@ -205,6 +207,7 @@ class BundleHermesCTaskTest {
205207
val task =
206208
createTestTask<BundleHermesCTask> {
207209
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
210+
it.root.set(tempFolder.root)
208211
it.cliFile.set(cliFile)
209212
it.bundleCommand.set("bundle")
210213
it.devEnabled.set(true)
@@ -244,6 +247,60 @@ class BundleHermesCTaskTest {
244247
assertEquals(24, bundleCommand.size)
245248
}
246249

250+
@Test
251+
@WithOs(OS.WIN)
252+
fun getBundleCommand_onWindows_returnsWinValidCommandsPaths() {
253+
val entryFile = tempFolder.newFile("index.js")
254+
val cliFile = tempFolder.newFile("cli.js")
255+
val bundleFile = tempFolder.newFile("bundle.js")
256+
val sourceMapFile = tempFolder.newFile("bundle.js.map")
257+
val resourcesDir = tempFolder.newFolder("res")
258+
val bundleConfig = tempFolder.newFile("bundle.config")
259+
val task =
260+
createTestTask<BundleHermesCTask> {
261+
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
262+
it.root.set(tempFolder.root)
263+
it.cliFile.set(cliFile)
264+
it.bundleCommand.set("bundle")
265+
it.devEnabled.set(true)
266+
it.entryFile.set(entryFile)
267+
it.resourcesDir.set(resourcesDir)
268+
it.bundleConfig.set(bundleConfig)
269+
it.minifyEnabled.set(true)
270+
it.extraPackagerArgs.set(listOf("--read-global-cache"))
271+
}
272+
273+
val bundleCommand = task.getBundleCommand(bundleFile, sourceMapFile)
274+
275+
assertEquals("cmd", bundleCommand[0])
276+
assertEquals("/c", bundleCommand[1])
277+
assertEquals("node", bundleCommand[2])
278+
assertEquals("arg1", bundleCommand[3])
279+
assertEquals("arg2", bundleCommand[4])
280+
assertEquals(cliFile.relativeTo(tempFolder.root).path, bundleCommand[5])
281+
assertEquals("bundle", bundleCommand[6])
282+
assertEquals("--platform", bundleCommand[7])
283+
assertEquals("android", bundleCommand[8])
284+
assertEquals("--dev", bundleCommand[9])
285+
assertEquals("true", bundleCommand[10])
286+
assertEquals("--reset-cache", bundleCommand[11])
287+
assertEquals("--entry-file", bundleCommand[12])
288+
assertEquals(entryFile.relativeTo(tempFolder.root).path, bundleCommand[13])
289+
assertEquals("--bundle-output", bundleCommand[14])
290+
assertEquals(bundleFile.relativeTo(tempFolder.root).path, bundleCommand[15])
291+
assertEquals("--assets-dest", bundleCommand[16])
292+
assertEquals(resourcesDir.relativeTo(tempFolder.root).path, bundleCommand[17])
293+
assertEquals("--sourcemap-output", bundleCommand[18])
294+
assertEquals(sourceMapFile.relativeTo(tempFolder.root).path, bundleCommand[19])
295+
assertEquals("--config", bundleCommand[20])
296+
assertEquals(bundleConfig.relativeTo(tempFolder.root).path, bundleCommand[21])
297+
assertEquals("--minify", bundleCommand[22])
298+
assertEquals("true", bundleCommand[23])
299+
assertEquals("--read-global-cache", bundleCommand[24])
300+
assertEquals("--verbose", bundleCommand[25])
301+
assertEquals(26, bundleCommand.size)
302+
}
303+
247304
@Test
248305
fun getBundleCommand_withoutConfig_returnsCommandWithoutConfig() {
249306
val entryFile = tempFolder.newFile("index.js")
@@ -254,6 +311,7 @@ class BundleHermesCTaskTest {
254311
val task =
255312
createTestTask<BundleHermesCTask> {
256313
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
314+
it.root.set(tempFolder.root)
257315
it.cliFile.set(cliFile)
258316
it.bundleCommand.set("bundle")
259317
it.devEnabled.set(true)
@@ -274,7 +332,10 @@ class BundleHermesCTaskTest {
274332
val bytecodeFile = tempFolder.newFile("bundle.js.hbc")
275333
val bundleFile = tempFolder.newFile("bundle.js")
276334
val task =
277-
createTestTask<BundleHermesCTask> { it.hermesFlags.set(listOf("my-custom-hermes-flag")) }
335+
createTestTask<BundleHermesCTask> {
336+
it.root.set(tempFolder.root)
337+
it.hermesFlags.set(listOf("my-custom-hermes-flag"))
338+
}
278339

279340
val hermesCommand = task.getHermescCommand(customHermesc, bytecodeFile, bundleFile)
280341

@@ -287,6 +348,31 @@ class BundleHermesCTaskTest {
287348
assertEquals(6, hermesCommand.size)
288349
}
289350

351+
@Test
352+
@WithOs(OS.WIN)
353+
fun getHermescCommand_onWindows_returnsRelativePaths() {
354+
val customHermesc = "hermesc"
355+
val bytecodeFile = tempFolder.newFile("bundle.js.hbc")
356+
val bundleFile = tempFolder.newFile("bundle.js")
357+
val task =
358+
createTestTask<BundleHermesCTask> {
359+
it.root.set(tempFolder.root)
360+
it.hermesFlags.set(listOf("my-custom-hermes-flag"))
361+
}
362+
363+
val hermesCommand = task.getHermescCommand(customHermesc, bytecodeFile, bundleFile)
364+
365+
assertEquals("cmd", hermesCommand[0])
366+
assertEquals("/c", hermesCommand[1])
367+
assertEquals(customHermesc, hermesCommand[2])
368+
assertEquals("-emit-binary", hermesCommand[3])
369+
assertEquals("-out", hermesCommand[4])
370+
assertEquals(bytecodeFile.relativeTo(tempFolder.root).path, hermesCommand[5])
371+
assertEquals(bundleFile.relativeTo(tempFolder.root).path, hermesCommand[6])
372+
assertEquals("my-custom-hermes-flag", hermesCommand[7])
373+
assertEquals(8, hermesCommand.size)
374+
}
375+
290376
@Test
291377
fun getComposeSourceMapsCommand_returnsCorrectCommand() {
292378
val packagerMap = tempFolder.newFile("bundle.js.packager.map")
@@ -296,6 +382,7 @@ class BundleHermesCTaskTest {
296382
val composeSourceMapsFile = File(reactNativeDir, "scripts/compose-source-maps.js")
297383
val task =
298384
createTestTask<BundleHermesCTask> {
385+
it.root.set(tempFolder.root)
299386
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
300387
}
301388

@@ -312,4 +399,34 @@ class BundleHermesCTaskTest {
312399
assertEquals(outputMap.absolutePath, composeSourcemapCommand[7])
313400
assertEquals(8, composeSourcemapCommand.size)
314401
}
402+
403+
@Test
404+
@WithOs(OS.WIN)
405+
fun getComposeSourceMapsCommand_onWindows_returnsRelativePaths() {
406+
val packagerMap = tempFolder.newFile("bundle.js.packager.map")
407+
val compilerMap = tempFolder.newFile("bundle.js.compiler.map")
408+
val outputMap = tempFolder.newFile("bundle.js.map")
409+
val reactNativeDir = tempFolder.newFolder("node_modules/react-native")
410+
val composeSourceMapsFile = File(reactNativeDir, "scripts/compose-source-maps.js")
411+
val task =
412+
createTestTask<BundleHermesCTask> {
413+
it.root.set(tempFolder.root)
414+
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
415+
}
416+
417+
val composeSourcemapCommand =
418+
task.getComposeSourceMapsCommand(composeSourceMapsFile, packagerMap, compilerMap, outputMap)
419+
420+
assertEquals("cmd", composeSourcemapCommand[0])
421+
assertEquals("/c", composeSourcemapCommand[1])
422+
assertEquals("node", composeSourcemapCommand[2])
423+
assertEquals("arg1", composeSourcemapCommand[3])
424+
assertEquals("arg2", composeSourcemapCommand[4])
425+
assertEquals(composeSourceMapsFile.relativeTo(tempFolder.root).path, composeSourcemapCommand[5])
426+
assertEquals(packagerMap.relativeTo(tempFolder.root).path, composeSourcemapCommand[6])
427+
assertEquals(compilerMap.relativeTo(tempFolder.root).path, composeSourcemapCommand[7])
428+
assertEquals("-o", composeSourcemapCommand[8])
429+
assertEquals(outputMap.relativeTo(tempFolder.root).path, composeSourcemapCommand[9])
430+
assertEquals(10, composeSourcemapCommand.size)
431+
}
315432
}

0 commit comments

Comments
 (0)