Skip to content

Commit 44ade83

Browse files
andrewdacenkofacebook-github-bot
authored andcommitted
Add gflags (facebook#52015)
Summary: Pull Request resolved: facebook#52015 Changelog: [Internal] Add gflags to fantom_tester so we can pass in data like featureFlags Reviewed By: cortinico Differential Revision: D76618409
1 parent 3c17e10 commit 44ade83

10 files changed

Lines changed: 475 additions & 7 deletions

File tree

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and 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 javax.inject.Inject
11+
import org.gradle.api.DefaultTask
12+
import org.gradle.api.file.ConfigurableFileCollection
13+
import org.gradle.api.file.CopySpec
14+
import org.gradle.api.file.DirectoryProperty
15+
import org.gradle.api.file.DuplicatesStrategy
16+
import org.gradle.api.file.FileSystemOperations
17+
import org.gradle.api.provider.Property
18+
import org.gradle.api.tasks.*
19+
20+
/**
21+
* A task that takes care of extracting gflags from a source folder/zip and preparing it to be
22+
* consumed by the NDK. This task will also take care of applying the mapping for gflags parameters.
23+
*/
24+
abstract class PrepareGflagsTask : DefaultTask() {
25+
26+
@get:InputFiles abstract val gflagsPath: ConfigurableFileCollection
27+
@get:InputDirectory abstract val gflagsThirdPartyPath: DirectoryProperty
28+
@get:Input abstract val gflagsVersion: Property<String>
29+
30+
@get:OutputDirectory abstract val outputDir: DirectoryProperty
31+
32+
@get:Inject abstract val fs: FileSystemOperations
33+
34+
@TaskAction
35+
fun taskAction() {
36+
val commonCopyConfig: (action: CopySpec) -> Unit = { action ->
37+
action.from(gflagsPath)
38+
action.from(gflagsThirdPartyPath)
39+
action.duplicatesStrategy = DuplicatesStrategy.INCLUDE
40+
action.includeEmptyDirs = false
41+
action.into(outputDir)
42+
}
43+
44+
fs.copy { action ->
45+
commonCopyConfig(action)
46+
action.include(
47+
"gflags-${gflagsVersion.get()}/src/*.h",
48+
"gflags-${gflagsVersion.get()}/src/*.cc",
49+
"CMakeLists.txt")
50+
action.filesMatching("*/src/*") { matchedFile ->
51+
matchedFile.path = "gflags/${matchedFile.name}"
52+
}
53+
}
54+
55+
fs.copy { action ->
56+
commonCopyConfig(action)
57+
action.include("gflags-${gflagsVersion.get()}/src/gflags_declare.h.in")
58+
action.filesMatching("*/src/*") { matchedFile ->
59+
matchedFile.filter { line ->
60+
// Replace all placeholders with appropriate values
61+
// see https://github.com/gflags/gflags/blob/v2.2.0/src/gflags_declare.h.in
62+
line
63+
.replace(Regex("@GFLAGS_NAMESPACE@"), "gflags")
64+
.replace(
65+
Regex(
66+
"@(HAVE_STDINT_H|HAVE_SYS_TYPES_H|HAVE_INTTYPES_H|GFLAGS_INTTYPES_FORMAT_C99)@"),
67+
"1")
68+
.replace(Regex("@([A-Z0-9_]+)@"), "1")
69+
}
70+
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
71+
}
72+
}
73+
74+
fs.copy { action ->
75+
commonCopyConfig(action)
76+
action.include("gflags-${gflagsVersion.get()}/src/config.h.in")
77+
action.filesMatching("*/src/*") { matchedFile ->
78+
matchedFile.filter { line -> line.replace(Regex("^#cmakedefine"), "//cmakedefine") }
79+
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
80+
}
81+
}
82+
83+
fs.copy { action ->
84+
commonCopyConfig(action)
85+
action.include("gflags-${gflagsVersion.get()}/src/gflags_ns.h.in")
86+
action.filesMatching("*/src/*") { matchedFile ->
87+
matchedFile.filter { line ->
88+
line.replace(Regex("@ns@"), "google").replace(Regex("@NS@"), "google".uppercase())
89+
}
90+
matchedFile.path = "gflags/gflags_google.h"
91+
}
92+
}
93+
94+
fs.copy { action ->
95+
commonCopyConfig(action)
96+
action.include("gflags-${gflagsVersion.get()}/src/gflags.h.in")
97+
action.filesMatching("*/src/*") { matchedFile ->
98+
matchedFile.filter { line ->
99+
line
100+
.replace(Regex("@GFLAGS_ATTRIBUTE_UNUSED@"), "")
101+
.replace(Regex("@INCLUDE_GFLAGS_NS_H@"), "#include \"gflags/gflags_google.h\"")
102+
}
103+
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
104+
}
105+
}
106+
107+
fs.copy { action ->
108+
commonCopyConfig(action)
109+
action.include("gflags-${gflagsVersion.get()}/src/gflags_completions.h.in")
110+
action.filesMatching("*/src/*") { matchedFile ->
111+
matchedFile.filter { line -> line.replace(Regex("@GFLAGS_NAMESPACE@"), "gflags") }
112+
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
113+
}
114+
}
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and 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 java.io.*
13+
import org.assertj.core.api.Assertions.assertThat
14+
import org.junit.Assert.assertEquals
15+
import org.junit.Rule
16+
import org.junit.Test
17+
import org.junit.rules.TemporaryFolder
18+
19+
class PrepareGflagsTaskTest {
20+
21+
@get:Rule val tempFolder = TemporaryFolder()
22+
23+
@Test(expected = IllegalStateException::class)
24+
fun prepareGflagsTask_withMissingConfiguration_fails() {
25+
val task = createTestTask<PrepareGflagsTask>()
26+
27+
task.taskAction()
28+
}
29+
30+
@Test
31+
fun prepareGflagsTask_copiesCMakefile() {
32+
val gflagspath = tempFolder.newFolder("gflagspath")
33+
val output = tempFolder.newFolder("output")
34+
val project = createProject()
35+
val gflagsThirdPartyPath = File(project.projectDir, "src/main/jni/third-party/gflags/")
36+
val task =
37+
createTestTask<PrepareGflagsTask>(project = project) {
38+
it.gflagsPath.setFrom(gflagspath)
39+
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
40+
it.gflagsVersion.set("1.0.0")
41+
it.outputDir.set(output)
42+
}
43+
File(gflagsThirdPartyPath, "CMakeLists.txt").apply {
44+
parentFile.mkdirs()
45+
createNewFile()
46+
}
47+
task.taskAction()
48+
49+
assertThat(output.listFiles()!!.any { it.name == "CMakeLists.txt" }).isTrue()
50+
}
51+
52+
@Test
53+
fun prepareGflagsTask_copiesSourceCodeAndHeaders() {
54+
val gflagspath = tempFolder.newFolder("gflagspath")
55+
val gflagsThirdPartyPath = tempFolder.newFolder("gflagspath/jni")
56+
val output = tempFolder.newFolder("output")
57+
val task =
58+
createTestTask<PrepareGflagsTask> {
59+
it.gflagsPath.setFrom(gflagspath)
60+
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
61+
it.gflagsVersion.set("1.0.0")
62+
it.outputDir.set(output)
63+
}
64+
File(gflagspath, "gflags-1.0.0/src/gflags.cc").apply {
65+
parentFile.mkdirs()
66+
createNewFile()
67+
}
68+
File(gflagspath, "gflags-1.0.0/src/util.h").apply {
69+
parentFile.mkdirs()
70+
createNewFile()
71+
}
72+
73+
task.taskAction()
74+
75+
assertThat(File(output, "gflags/gflags.cc").exists()).isTrue()
76+
assertThat(File(output, "gflags/util.h").exists()).isTrue()
77+
}
78+
79+
@Test
80+
fun prepareGflagsTask_replacesTokenCorrectly() {
81+
val gflagspath = tempFolder.newFolder("gflagspath")
82+
val gflagsThirdPartyPath = tempFolder.newFolder("gflagspath/jni")
83+
val output = tempFolder.newFolder("output")
84+
val task =
85+
createTestTask<PrepareGflagsTask> {
86+
it.gflagsPath.setFrom(gflagspath)
87+
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
88+
it.gflagsVersion.set("1.0.0")
89+
it.outputDir.set(output)
90+
}
91+
File(gflagspath, "gflags-1.0.0/src/gflags_declare.h.in").apply {
92+
parentFile.mkdirs()
93+
writeText(
94+
"""
95+
#define GFLAGS_NAMESPACE @GFLAGS_NAMESPACE@
96+
#include <string>
97+
#if @HAVE_STDINT_H@
98+
# include <stdint.h>
99+
#elif @HAVE_SYS_TYPES_H@
100+
# include <sys/types.h>
101+
#elif @HAVE_INTTYPES_H@
102+
# include <inttypes.h>
103+
#endif
104+
105+
106+
namespace GFLAGS_NAMESPACE {
107+
108+
#if @GFLAGS_INTTYPES_FORMAT_C99@ // C99
109+
typedef int32_t int32;
110+
typedef uint32_t uint32;
111+
typedef int64_t int64;
112+
typedef uint64_t uint64;
113+
#elif @GFLAGS_INTTYPES_FORMAT_BSD@ // BSD
114+
typedef int32_t int32;
115+
typedef u_int32_t uint32;
116+
typedef int64_t int64;
117+
typedef u_int64_t uint64;
118+
#elif @GFLAGS_INTTYPES_FORMAT_VC7@ // Windows
119+
typedef __int32 int32;
120+
typedef unsigned __int32 uint32;
121+
typedef __int64 int64;
122+
typedef unsigned __int64 uint64;
123+
#else
124+
# error Do not know how to define a 32-bit integer quantity on your system
125+
#endif
126+
127+
} // namespace GFLAGS_NAMESPACE
128+
""")
129+
}
130+
File(gflagspath, "gflags-1.0.0/src/config.h.in").apply {
131+
parentFile.mkdirs()
132+
createNewFile()
133+
writeText("#cmakedefine")
134+
}
135+
File(gflagspath, "gflags-1.0.0/src/gflags_ns.h.in").apply {
136+
parentFile.mkdirs()
137+
createNewFile()
138+
writeText("@ns@ @NS@")
139+
}
140+
File(gflagspath, "gflags-1.0.0/src/gflags.h.in").apply {
141+
parentFile.mkdirs()
142+
createNewFile()
143+
writeText("@GFLAGS_ATTRIBUTE_UNUSED@\n@INCLUDE_GFLAGS_NS_H@")
144+
}
145+
File(gflagspath, "gflags-1.0.0/src/gflags_completions.h.in").apply {
146+
parentFile.mkdirs()
147+
createNewFile()
148+
writeText("@GFLAGS_NAMESPACE@")
149+
}
150+
151+
task.taskAction()
152+
153+
val declareFile = File(output, "gflags/gflags_declare.h")
154+
assertThat(declareFile.exists()).isTrue()
155+
assertEquals(
156+
declareFile.readText(),
157+
"""
158+
#define GFLAGS_NAMESPACE gflags
159+
#include <string>
160+
#if 1
161+
# include <stdint.h>
162+
#elif 1
163+
# include <sys/types.h>
164+
#elif 1
165+
# include <inttypes.h>
166+
#endif
167+
168+
169+
namespace GFLAGS_NAMESPACE {
170+
171+
#if 1 // C99
172+
typedef int32_t int32;
173+
typedef uint32_t uint32;
174+
typedef int64_t int64;
175+
typedef uint64_t uint64;
176+
#elif 1 // BSD
177+
typedef int32_t int32;
178+
typedef u_int32_t uint32;
179+
typedef int64_t int64;
180+
typedef u_int64_t uint64;
181+
#elif 1 // Windows
182+
typedef __int32 int32;
183+
typedef unsigned __int32 uint32;
184+
typedef __int64 int64;
185+
typedef unsigned __int64 uint64;
186+
#else
187+
# error Do not know how to define a 32-bit integer quantity on your system
188+
#endif
189+
190+
} // namespace GFLAGS_NAMESPACE
191+
""")
192+
193+
val configFile = File(output, "gflags/config.h")
194+
assertThat(configFile.exists()).isTrue()
195+
assertEquals(configFile.readText(), "//cmakedefine")
196+
197+
val nsFile = File(output, "gflags/gflags_google.h")
198+
assertThat(nsFile.exists()).isTrue()
199+
assertEquals(nsFile.readText(), "google GOOGLE")
200+
201+
val gflagsFile = File(output, "gflags/gflags.h")
202+
assertThat(gflagsFile.exists()).isTrue()
203+
assertEquals(gflagsFile.readText(), "\n#include \"gflags/gflags_google.h\"")
204+
205+
val completionsFile = File(output, "gflags/gflags_completions.h")
206+
assertThat(completionsFile.exists()).isTrue()
207+
assertEquals(completionsFile.readText(), "gflags")
208+
}
209+
}

packages/react-native/gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ fastFloat="8.0.0"
4646
fmt="11.0.2"
4747
folly="2024.11.18.00"
4848
glog="0.3.5"
49+
gflags="2.2.0"
4950

5051
[libraries]
5152
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and 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+
import com.android.build.gradle.internal.tasks.factory.dependsOn
9+
import com.facebook.react.tasks.internal.*
10+
import com.facebook.react.tasks.internal.utils.*
11+
import de.undercouch.gradle.tasks.download.Download
12+
13+
plugins {
14+
id("com.facebook.react")
15+
alias(libs.plugins.download)
16+
}
17+
18+
val GFLAGS_VERSION = libs.versions.gflags.get()
19+
20+
val buildDir = project.layout.buildDirectory.get().asFile
21+
val downloadsDir =
22+
if (System.getenv("REACT_NATIVE_DOWNLOADS_DIR") != null) {
23+
File(System.getenv("REACT_NATIVE_DOWNLOADS_DIR"))
24+
} else {
25+
File("$buildDir/downloads")
26+
}
27+
val thirdParty = File("$buildDir/third-party")
28+
val reactNativeRootDir = projectDir.parent
29+
30+
val createNativeDepsDirectories by
31+
tasks.registering {
32+
downloadsDir.mkdirs()
33+
thirdParty.mkdirs()
34+
}
35+
36+
val downloadGflagsDest = File(downloadsDir, "gflags-${GFLAGS_VERSION}.tar.gz")
37+
val downloadGflags by
38+
tasks.registering(Download::class) {
39+
dependsOn(createNativeDepsDirectories)
40+
src("https://github.com/gflags/gflags/archive/v${GFLAGS_VERSION}.tar.gz")
41+
onlyIfModified(true)
42+
overwrite(false)
43+
retries(5)
44+
quiet(true)
45+
dest(downloadGflagsDest)
46+
}
47+
48+
val prepareGflags by
49+
tasks.registering(PrepareGflagsTask::class) {
50+
dependsOn(listOf(downloadGflags))
51+
gflagsPath.setFrom(tarTree(downloadGflagsDest))
52+
gflagsThirdPartyPath.set(project.file("tester/third-party/gflags/"))
53+
gflagsVersion.set(GFLAGS_VERSION)
54+
outputDir.set(File(thirdParty, "gflags"))
55+
}
56+
57+
// Tasks used by Fantom to download the Native 3p dependencies used.
58+
val prepareNative3pDependencies by
59+
tasks.registering {
60+
dependsOn(
61+
prepareGflags,
62+
)
63+
}

0 commit comments

Comments
 (0)