@@ -6,10 +6,14 @@ package util.tasks
66
77import org.gradle.api.DefaultTask
88import org.gradle.api.Project
9+ import org.gradle.api.file.FileTree
10+ import org.gradle.api.provider.ListProperty
911import org.gradle.api.provider.Property
1012import org.gradle.api.tasks.Copy
13+ import org.gradle.api.tasks.Exec
1114import org.gradle.api.tasks.Input
1215import org.gradle.api.tasks.InputFile
16+ import org.gradle.api.tasks.InputFiles
1317import org.gradle.api.tasks.OutputFile
1418import org.gradle.api.tasks.TaskAction
1519import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo
@@ -21,8 +25,40 @@ import util.other.libs
2125import java.io.File
2226
2327const val CONFORMANCE_TEST_RUNNER_CONFIGURATION = " conformanceTestRunner"
28+ const val PROTOC_TESTING_CONFIGURATION = " protoc_internalTesting"
2429const val UNZIP_PROTOBUF_CONFORMANCE_TASK = " unzipProtobufConformance"
2530const val WRITE_CONFORMANCE_EXECUTABLE_PATH_TASK = " writeConformanceExecutablePath"
31+ const val PAYLOAD_PB = " payload.pb"
32+ const val CONFORMANCE_PB = " conformance.pb"
33+
34+ private fun Project.getBinFrom (configuration : String ): File {
35+ return configurations.getByName(configuration).map {
36+ zipTree(it).matching { include(" bin/**" ) }.files.first()
37+ }.single()
38+ }
39+
40+ private fun Project.getIncludeFrom (configuration : String ): List <FileTree > {
41+ return configurations.getByName(configuration).map {
42+ zipTree(it).matching { include(" include/**" ) }
43+ }
44+ }
45+
46+ private fun List<String>.commonPrefix (): String {
47+ return fold(first()) { acc, s -> acc.commonPrefixWith(s) }
48+ }
49+
50+ private fun Project.pbFile (name : String ): File {
51+ return layout.buildDirectory.get()
52+ .dir(" protobuf-conformance" )
53+ .file(name)
54+ .asFile
55+ .apply {
56+ if (! exists()) {
57+ parentFile.mkdirs()
58+ createNewFile()
59+ }
60+ }
61+ }
2662
2763abstract class ConformanceExecutablePathWriter : DefaultTask () {
2864 @get:Input
@@ -64,6 +100,48 @@ abstract class ConformanceExecutablePathWriter : DefaultTask() {
64100 }
65101}
66102
103+ abstract class GenerateConformanceFileDescriptorSet : Exec () {
104+ @get:InputFiles
105+ abstract val wktFilesCollection: ListProperty <File >
106+
107+ @get:InputFiles
108+ abstract val conformanceFilesCollection: ListProperty <File >
109+
110+ @get:InputFile
111+ abstract val bin: Property <File >
112+
113+ @get:OutputFile
114+ abstract val outputFile: Property <File >
115+
116+ @TaskAction
117+ fun generate () {
118+ val wktFiles = wktFilesCollection.get().map { it.absolutePath }
119+ val conformanceFiles = conformanceFilesCollection.get().map { it.absolutePath }
120+
121+ val wktProtoPath = if (wktFiles.isEmpty()) {
122+ emptyList()
123+ } else {
124+ listOf (" --proto_path=${wktFiles.commonPrefix().substringBefore(" /google/protobuf/" )} " )
125+ }
126+
127+ val conformanceIncludeDir = conformanceFiles
128+ .commonPrefix()
129+ .substringBefore(" /google/protobuf/" )
130+ .substringBefore(" /conformance/" )
131+
132+ val conformanceProtoPath = " --proto_path=$conformanceIncludeDir "
133+
134+ commandLine(
135+ bin.get().absolutePath,
136+ * wktProtoPath.toTypedArray(),
137+ conformanceProtoPath,
138+ " -o" , outputFile.get().absolutePath,
139+ * wktFiles.toTypedArray(),
140+ * conformanceFiles.toTypedArray(),
141+ )
142+ }
143+ }
144+
67145fun Project.setupProtobufConformanceResources () {
68146 val os = System .getProperty(" os.name" ).lowercase()
69147 val osPart = when {
@@ -79,7 +157,7 @@ fun Project.setupProtobufConformanceResources() {
79157
80158 // https://stackoverflow.com/questions/23023069/gradle-download-and-unzip-file-from-url
81159 repositories.ivy {
82- name = " protobuf-conformance- github"
160+ name = " github"
83161 url = uri(" https://github.com" )
84162
85163 patternLayout {
@@ -93,6 +171,7 @@ fun Project.setupProtobufConformanceResources() {
93171 }
94172
95173 configurations.create(CONFORMANCE_TEST_RUNNER_CONFIGURATION )
174+ configurations.create(PROTOC_TESTING_CONFIGURATION )
96175
97176 // https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/dsl/DependencyHandler.html
98177 dependencies {
@@ -115,10 +194,28 @@ fun Project.setupProtobufConformanceResources() {
115194 }
116195 }
117196
197+ dependencies {
198+ addExternalModuleDependencyTo(
199+ this ,
200+ PROTOC_TESTING_CONFIGURATION ,
201+ group = " protocolbuffers" ,
202+ name = " protobuf" ,
203+ version = libs.versions.protobuf.asProvider().get().substringAfter(" ." ),
204+ classifier = null ,
205+ ext = null ,
206+ configuration = null ,
207+ ) {
208+ artifact {
209+ name = " protoc"
210+ type = " zip"
211+ extension = " zip"
212+ classifier = " $osPart -$archPart "
213+ }
214+ }
215+ }
216+
118217 val unzipProtobufConformance = tasks.register<Copy >(UNZIP_PROTOBUF_CONFORMANCE_TASK ) {
119- from(configurations.getByName(CONFORMANCE_TEST_RUNNER_CONFIGURATION ).map {
120- zipTree(it).matching { include(" include/**" ) }
121- })
218+ from(getIncludeFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION ))
122219
123220 val destDir = project.layout.projectDirectory
124221 .dir(" src" )
@@ -140,6 +237,34 @@ fun Project.setupProtobufConformanceResources() {
140237 }
141238 }
142239
240+ tasks.register<GenerateConformanceFileDescriptorSet >(" generateConformanceFileDescriptorSet_conformance" ) {
241+ wktFilesCollection.set(emptyList())
242+
243+ val conformanceFiles = project.getIncludeFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION ).flatMap { it.files }
244+ .filter { it.name == " conformance.proto" }
245+
246+ conformanceFilesCollection.set(conformanceFiles)
247+
248+ bin.set(getBinFrom(PROTOC_TESTING_CONFIGURATION ))
249+
250+ outputFile.set(project.pbFile(CONFORMANCE_PB ))
251+ }
252+
253+ tasks.register<GenerateConformanceFileDescriptorSet >(" generateConformanceFileDescriptorSet_payload" ) {
254+ val wktFiles = project.getIncludeFrom(PROTOC_TESTING_CONFIGURATION ).flatMap { it.files }
255+ wktFilesCollection.set(wktFiles)
256+
257+ // editions are not supported in protoscope and proto2 fails
258+ val conformanceFiles = project.getIncludeFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION ).flatMap { it.files }
259+ .filter { it.name == " test_messages_proto3.proto" }
260+
261+ conformanceFilesCollection.set(conformanceFiles)
262+
263+ bin.set(getBinFrom(PROTOC_TESTING_CONFIGURATION ))
264+
265+ outputFile.set(project.pbFile(PAYLOAD_PB ))
266+ }
267+
143268 val writeConformanceExecutablePath =
144269 tasks.register<ConformanceExecutablePathWriter >(WRITE_CONFORMANCE_EXECUTABLE_PATH_TASK ) {
145270 outputDir.set(
@@ -156,11 +281,7 @@ fun Project.setupProtobufConformanceResources() {
156281 .asFile
157282 )
158283
159- executable.set(
160- configurations.getByName(CONFORMANCE_TEST_RUNNER_CONFIGURATION ).map {
161- zipTree(it).matching { include(" bin/**" ) }.files.first()
162- }.single()
163- )
284+ executable.set(getBinFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION ))
164285
165286 destination.set(
166287 project.layout.buildDirectory.get()
0 commit comments