Skip to content

Commit bc93fef

Browse files
cortinicofacebook-github-bot
authored andcommitted
Refactor Extract Headers and JNI from AARs to an internal task (#32426)
Summary: Pull Request resolved: #32426 This diff refactors the extractHeader and extractJni tasks to a single Gradle task in the `.internal` package. The reason for this change is that those two tasks were always running, therefore invalidating the whole native build cache. Changelog: [Internal] [Changed] - Refactor Extract Headers and JNI from AARs to an internal task Reviewed By: mdvacca, ShikaSD Differential Revision: D31682942 fbshipit-source-id: 191cc77902e82c0425949cee743d240ded790137
1 parent 75b2e5c commit bc93fef

File tree

3 files changed

+134
-27
lines changed

3 files changed

+134
-27
lines changed

ReactAndroid/build.gradle

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ def getNdkBuildFullPath() {
284284
}
285285

286286
def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) {
287-
dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFmt, prepareFolly, prepareGlog, prepareLibevent, extractAARHeaders, extractJNIFiles)
287+
dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFmt, prepareFolly, prepareGlog, prepareLibevent, extractNativeDependencies)
288288
dependsOn("generateCodegenArtifactsFromSchema");
289289

290290
inputs.dir("$projectDir/../ReactCommon")
@@ -343,32 +343,18 @@ def packageReactNdkLibsForBuck = tasks.register("packageReactNdkLibsForBuck", Co
343343
into("src/main/jni/prebuilt/lib")
344344
}
345345

346-
task extractAARHeaders {
347-
doLast {
348-
configurations.extractHeaders.files.each {
349-
def file = it.absoluteFile
350-
def packageName = file.name.tokenize('-')[0]
351-
copy {
352-
from zipTree(file)
353-
into "$projectDir/src/main/jni/first-party/$packageName/headers"
354-
include "**/*.h"
355-
}
356-
}
357-
}
358-
}
359-
360-
task extractJNIFiles {
361-
doLast {
362-
configurations.extractJNI.files.each {
363-
def file = it.absoluteFile
364-
def packageName = file.name.tokenize('-')[0]
365-
copy {
366-
from zipTree(file)
367-
into "$projectDir/src/main/jni/first-party/$packageName/"
368-
include "jni/**/*"
369-
}
370-
}
371-
}
346+
final def extractNativeDependencies = tasks.register('extractNativeDependencies', ExtractJniAndHeadersTask) {
347+
it.extractHeadersConfiguration.setFrom(configurations.extractHeaders)
348+
it.extractJniConfiguration.setFrom(configurations.extractJNI)
349+
it.baseOutputDir = project.file("src/main/jni/first-party/")
350+
// Sadly this task as an output folder path that is directly dependent on
351+
// the task input (i.e. src/main/jni/first-party/<package-name>/...
352+
// This means that this task is using the parent folder (first-party/) as
353+
// @OutputFolder. The `prepareHermes` task will also output inside that
354+
// folder and if the two tasks happen to be inside the same run, we want
355+
// `extractNativeDependencies` to run after `prepareHermes` to do not
356+
// invalidate the input/output calculation for this task.
357+
it.mustRunAfter(prepareHermes)
372358
}
373359

374360
task installArchives {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.tasks.internal
9+
10+
import org.gradle.api.DefaultTask
11+
import org.gradle.api.file.ConfigurableFileCollection
12+
import org.gradle.api.file.DirectoryProperty
13+
import org.gradle.api.tasks.*
14+
15+
/**
16+
* A task that takes care of extracting JNIs and Headers from a custom Gradle configuration into an
17+
* output folder. Users are most likely not going to use this task but it will be used when building
18+
* the React Native project.
19+
*/
20+
abstract class ExtractJniAndHeadersTask : DefaultTask() {
21+
22+
@get:InputFiles abstract val extractHeadersConfiguration: ConfigurableFileCollection
23+
24+
@get:InputFiles abstract val extractJniConfiguration: ConfigurableFileCollection
25+
26+
@get:OutputDirectory abstract val baseOutputDir: DirectoryProperty
27+
28+
@TaskAction
29+
fun taskAction() {
30+
extractJniConfiguration.files.forEach {
31+
val file = it.absoluteFile
32+
val packageName = file.name.split("-", ".").first()
33+
project.copy { copySpec ->
34+
copySpec.from(project.zipTree(file))
35+
copySpec.into(baseOutputDir.dir(packageName))
36+
copySpec.include("jni/**/*")
37+
}
38+
}
39+
extractHeadersConfiguration.files.forEach {
40+
val file = it.absoluteFile
41+
val packageName = file.name.split("-", ".").first()
42+
project.copy { copySpec ->
43+
copySpec.from(project.zipTree(file))
44+
copySpec.into(baseOutputDir.get().dir("$packageName/headers"))
45+
copySpec.include("**/*.h")
46+
}
47+
}
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.tasks.internal
9+
10+
import com.facebook.react.tests.createProject
11+
import com.facebook.react.tests.createTestTask
12+
import com.facebook.react.tests.zipFiles
13+
import java.io.*
14+
import java.util.zip.ZipEntry
15+
import java.util.zip.ZipOutputStream
16+
import org.junit.Assert.assertTrue
17+
import org.junit.Rule
18+
import org.junit.Test
19+
import org.junit.rules.TemporaryFolder
20+
21+
class ExtractJniAndHeadersTaskTest {
22+
23+
@get:Rule val tempFolder = TemporaryFolder()
24+
25+
@Test
26+
fun extractJniAndHeadersTask_extractsHeadersCorrectly() {
27+
val project = createProject()
28+
val aarFile = File(project.projectDir, "libheader.aar")
29+
val headerFile = tempFolder.newFile("justaheader.h")
30+
val output = tempFolder.newFolder("output")
31+
zipFiles(aarFile, listOf(headerFile))
32+
33+
val task =
34+
createTestTask<ExtractJniAndHeadersTask>(project = project) {
35+
it.extractHeadersConfiguration.setFrom(aarFile)
36+
it.baseOutputDir.set(output)
37+
}
38+
39+
task.taskAction()
40+
41+
assertTrue(File(output, "libheader/headers/justaheader.h").exists())
42+
}
43+
44+
@Test
45+
fun extractJniAndHeadersTask_extractsJniCorrectly() {
46+
val project = createProject()
47+
val aarFile = File(project.projectDir, "something.aar")
48+
File(tempFolder.root, "jni/libsomething.so").apply {
49+
parentFile.mkdirs()
50+
createNewFile()
51+
}
52+
val output = tempFolder.newFolder("output")
53+
ZipOutputStream(BufferedOutputStream(FileOutputStream(aarFile.absolutePath))).use { out ->
54+
FileInputStream(aarFile).use { fi ->
55+
BufferedInputStream(fi).use { origin ->
56+
out.putNextEntry(ZipEntry("jni/"))
57+
out.putNextEntry(ZipEntry("jni/libsomething.so"))
58+
origin.copyTo(out, 1024)
59+
}
60+
}
61+
}
62+
val task =
63+
createTestTask<ExtractJniAndHeadersTask>(project = project) {
64+
it.extractJniConfiguration.setFrom(aarFile)
65+
it.baseOutputDir.set(output)
66+
}
67+
68+
task.taskAction()
69+
70+
assertTrue(File(output, "something/jni/libsomething.so").exists())
71+
}
72+
}

0 commit comments

Comments
 (0)