From b5d770cb9fab7bd772c37e31793d9b4261ad58d7 Mon Sep 17 00:00:00 2001 From: Yevhenii Date: Fri, 16 Oct 2026 05:34:20 +0300 Subject: [PATCH 1/2] Test that the prefab headers task tracks the headers it copies --- .../internal/PreparePrefabHeadersTaskTest.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt index b692c65e4b8d..4b1e714e710b 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt @@ -204,6 +204,42 @@ class PreparePrefabHeadersTaskTest { assertThat(File(outputDir, "sample_library/boost/anothermodule/wedontuse.hpp")).doesNotExist() } + @Test + fun preparePrefabHeadersTask_declaresTheHeadersItCopiesAsInputs() { + val header = File(tempFolder.root, "input/hello.h").apply { createNewPathAndFile() } + File(tempFolder.root, "input/hello.cpp").createNewPathAndFile() + + val project = createProject(projectDir = tempFolder.root) + val task = + createTestTask(project = project) { + it.outputDir.set(tempFolder.newFolder("output")) + it.input.set(listOf(PrefabPreprocessingEntry("sample_library", "input/" to ""))) + } + + assertThat(task.inputs.files.files).containsExactly(header) + } + + @Test + fun preparePrefabHeadersTask_withARemovedHeader_removesItsCopy() { + val outputDir = tempFolder.newFolder("output") + val removedHeader = File(tempFolder.root, "input/removed.h").apply { createNewPathAndFile() } + File(tempFolder.root, "input/kept.h").createNewPathAndFile() + + val project = createProject(projectDir = tempFolder.root) + val task = + createTestTask(project = project) { + it.outputDir.set(outputDir) + it.input.set(listOf(PrefabPreprocessingEntry("sample_library", "input/" to ""))) + } + + task.taskAction() + removedHeader.delete() + task.taskAction() + + assertThat(File(outputDir, "sample_library/removed.h")).doesNotExist() + assertThat(File(outputDir, "sample_library/kept.h")).exists() + } + private fun File.createNewPathAndFile() { parentFile.mkdirs() createNewFile() From efb85fdfac6bd52ae3d6341ff2d8725e9d1b346c Mon Sep 17 00:00:00 2001 From: Yevhenii Date: Fri, 16 Oct 2026 05:34:20 +0300 Subject: [PATCH 2/2] Re-copy the prefab headers when a header changes PreparePrefabHeadersTask declared only the directory paths it copies from, so Gradle found it up to date after a header under them changed and the prefab package every app and library compiles against kept the old header. The headers it copies are now its input files, read through the same file trees the copy uses, and each library's folder is rebuilt from scratch so a removed header leaves no copy behind. --- .../internal/PreparePrefabHeadersTask.kt | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt index cb3991b03739..555c7f649082 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt @@ -12,10 +12,14 @@ import java.io.File import javax.inject.Inject import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.FileTree import org.gradle.api.file.RegularFile +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction @@ -35,44 +39,64 @@ abstract class PreparePrefabHeadersTask : DefaultTask() { @get:OutputDirectory abstract val outputDir: DirectoryProperty + @get:InputFiles + val headers: FileCollection + get() = + objects + .fileCollection() + .from( + input.map { entries -> + entries.flatMap { (_, pathToPrefixCouples) -> + pathToPrefixCouples.map { (headerPath, _) -> headersUnder(headerPath) } + } + } + ) + @get:Inject abstract val fs: FileSystemOperations + @get:Inject abstract val objects: ObjectFactory + @TaskAction fun taskAction() { input.get().forEach { (libraryName, pathToPrefixCouples) -> val outputFolder: RegularFile = outputDir.file(libraryName).get() + fs.delete { deleteSpec -> deleteSpec.delete(outputFolder) } pathToPrefixCouples.forEach { (headerPath, headerPrefix) -> fs.copy { copySpec -> - copySpec.from(headerPath) - copySpec.include("**/*.h") - copySpec.exclude("**/*.cpp") - copySpec.exclude("**/*.txt") - // We don't want to copy all the boost headers as they are 250Mb+ - copySpec.include("boost/config.hpp") - copySpec.include("boost/config/**/*.hpp") - copySpec.include("boost/core/*.hpp") - copySpec.include("boost/detail/workaround.hpp") - copySpec.include("boost/operators.hpp") - copySpec.include("boost/preprocessor/**/*.hpp") - // Headers needed for exposing rrc_text and rrc_textinput - copySpec.include("boost/container_hash/**/*.hpp") - copySpec.include("boost/detail/**/*.hpp") - copySpec.include("boost/intrusive/**/*.hpp") - copySpec.include("boost/iterator/**/*.hpp") - copySpec.include("boost/move/**/*.hpp") - copySpec.include("boost/mpl/**/*.hpp") - copySpec.include("boost/mp11/**/*.hpp") - copySpec.include("boost/describe/**/*.hpp") - copySpec.include("boost/type_traits/**/*.hpp") - copySpec.include("boost/utility/**/*.hpp") - copySpec.include("boost/assert.hpp") - copySpec.include("boost/static_assert.hpp") - copySpec.include("boost/cstdint.hpp") - copySpec.include("boost/utility.hpp") - copySpec.include("boost/version.hpp") + copySpec.from(headersUnder(headerPath)) copySpec.into(File(outputFolder.asFile, headerPrefix)) } } } } + + private fun headersUnder(headerPath: String): FileTree = + objects.fileTree().from(headerPath).matching { patterns -> + patterns.include("**/*.h") + patterns.exclude("**/*.cpp") + patterns.exclude("**/*.txt") + // We don't want to copy all the boost headers as they are 250Mb+ + patterns.include("boost/config.hpp") + patterns.include("boost/config/**/*.hpp") + patterns.include("boost/core/*.hpp") + patterns.include("boost/detail/workaround.hpp") + patterns.include("boost/operators.hpp") + patterns.include("boost/preprocessor/**/*.hpp") + // Headers needed for exposing rrc_text and rrc_textinput + patterns.include("boost/container_hash/**/*.hpp") + patterns.include("boost/detail/**/*.hpp") + patterns.include("boost/intrusive/**/*.hpp") + patterns.include("boost/iterator/**/*.hpp") + patterns.include("boost/move/**/*.hpp") + patterns.include("boost/mpl/**/*.hpp") + patterns.include("boost/mp11/**/*.hpp") + patterns.include("boost/describe/**/*.hpp") + patterns.include("boost/type_traits/**/*.hpp") + patterns.include("boost/utility/**/*.hpp") + patterns.include("boost/assert.hpp") + patterns.include("boost/static_assert.hpp") + patterns.include("boost/cstdint.hpp") + patterns.include("boost/utility.hpp") + patterns.include("boost/version.hpp") + } }