Skip to content

Commit 701fafc

Browse files
authored
Add core-sparql-protos-google (#713)
1 parent 28c9f70 commit 701fafc

4 files changed

Lines changed: 99 additions & 5 deletions

File tree

build.sbt

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,13 @@ def doPrepareGoogleProtos(baseDir: File): Seq[File] = {
127127
outputFile
128128
}
129129
.map { file =>
130-
// Append java options to the file
131-
val outPackage = "eu.neverblink.jelly.core.proto.google.v1" +
132-
(if (file.getName == "patch.proto") ".patch" else "")
130+
// Append java options to the file. The sub-package must match the one the crunchy plugin
131+
// uses for the same file, so that the two sets of classes can live side by side.
132+
val outPackage = "eu.neverblink.jelly.core.proto.google.v1" + (file.getName match {
133+
case "patch.proto" => ".patch"
134+
case "sparql.proto" => ".sparql"
135+
case _ => ""
136+
})
133137
val content = IO.read(file)
134138
val newContent = content +
135139
f"""
@@ -440,7 +444,42 @@ lazy val coreSparql = (project in file("core-sparql"))
440444
commonSettings,
441445
commonJavaSettings,
442446
)
443-
.dependsOn(core % "compile->compile;test->test")
447+
.dependsOn(
448+
core % "compile->compile;test->test",
449+
// Test-time dependency on Google protos for SparqlProtoSpec
450+
coreSparqlProtosGoogle % "test->compile",
451+
)
452+
453+
lazy val coreSparqlProtosGoogle = (project in file("core-sparql-protos-google"))
454+
.enablePlugins(ProtobufPlugin)
455+
.settings(
456+
name := "jelly-core-sparql-protos-google",
457+
description := "Optional proto classes for Jelly-SPARQL (sparql.proto) compiled with Google's " +
458+
"official Java protoc plugin. This is not needed, unless you need some functionality " +
459+
"that is only available with the more heavyweight, Google-style proto classes, like " +
460+
"support for the Protobuf Text Format.",
461+
libraryDependencies ++= Seq("com.google.protobuf" % "protobuf-java" % protobufV),
462+
prepareGoogleProtos := Def.uncached { doPrepareGoogleProtos(baseDirectory.value) },
463+
Compile / compile := Def.uncached((Compile / compile).dependsOn(prepareGoogleProtos).value),
464+
ProtobufConfig / protobufRunProtoc := Def.uncached(
465+
(ProtobufConfig / protobufRunProtoc).dependsOn(
466+
prepareGoogleProtos,
467+
).value,
468+
),
469+
// See the comment in corePatchProtosGoogle: the scan of src/main/protobuf has to be ordered
470+
// after prepareGoogleProtos, or a clean checkout generates nothing.
471+
ProtobufConfig / protobufSources := Def.uncached(
472+
(ProtobufConfig / protobufSources).dependsOn(prepareGoogleProtos).value,
473+
),
474+
ProtobufConfig / protobufIncludeFilters := Seq(
475+
Glob(baseDirectory.value.toPath) / "**" / "sparql.proto",
476+
),
477+
// Don't throw errors, because Google's protoc generates code with a lot of warnings
478+
javacOptions := javacOptions.value.filterNot(_ == "-Werror"),
479+
commonSettings,
480+
commonJavaSettings,
481+
// sparql.proto imports rdf.proto and rdf2.proto, whose Google classes live in coreProtosGoogle
482+
).dependsOn(coreProtosGoogle)
444483

445484
lazy val jena = (project in file("jena"))
446485
.settings(
@@ -736,6 +775,7 @@ lazy val root = (project in file("."))
736775
corePatch,
737776
corePatchProtosGoogle,
738777
coreSparql,
778+
coreSparqlProtosGoogle,
739779
jena,
740780
jenaPatch,
741781
jenaSparql,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Protos are copied here by sbt, they should not be committed.
2+
*.proto

core-sparql-protos-google/src/main/protobuf/.gitkeep

Whitespace-only changes.

core-sparql/src/test/scala/eu/neverblink/jelly/core/sparql/SparqlProtoSpec.scala

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package eu.neverblink.jelly.core.sparql
22

3-
import com.google.protobuf.{ByteString, InvalidProtocolBufferException}
3+
import com.google.protobuf.{ByteString, InvalidProtocolBufferException, TextFormat}
44
import eu.neverblink.jelly.core.proto.v1.*
55
import eu.neverblink.jelly.core.proto.v1.sparql.*
6+
import eu.neverblink.jelly.core.proto.google.v1.sparql as google
67
import eu.neverblink.protoc.java.runtime.ProtoMessage
78
import org.scalatest.matchers.should.Matchers
89
import org.scalatest.wordspec.AnyWordSpec
@@ -12,6 +13,9 @@ import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream}
1213
/** Tests for the generated Protobuf messages of Jelly-SPARQL: the serialization round-trips and the
1314
* auxiliary methods (clone, copyFrom, mergeFrom, clear, equals) that the encoder and decoder do
1415
* not exercise by themselves.
16+
*
17+
* This also tests the classes generated by Google's protoc-java, from the
18+
* core-sparql-protos-google module.
1519
*/
1620
class SparqlProtoSpec extends AnyWordSpec, Matchers:
1721

@@ -500,3 +504,51 @@ class SparqlProtoSpec extends AnyWordSpec, Matchers:
500504
a.mergeFrom(b) shouldBe merged
501505
}
502506
}
507+
508+
// Tests for the core-sparql-protos-google module. These also check that the two code generators
509+
// agree on the wire format.
510+
"proto.google.v1.sparql.SparqlResultsFrame" should {
511+
val cases = Seq(
512+
"frame with every field set" -> fullFrame,
513+
"frame with a datatype-monomorphic literal column" -> SparqlResultsFrame
514+
.newInstance()
515+
.setOptions(JellySparqlOptions.SMALL)
516+
.setRowCount(3)
517+
.addVariables(SparqlVariable.newInstance().setName("x").setColumnIndex(0))
518+
.addLiteralColumns(lexLiteralColumn),
519+
)
520+
521+
"round-trip in non-delimited binary form" when {
522+
for (name, frame) <- cases do
523+
s"a $name" in {
524+
val gFrame = google.SparqlResultsFrame.parseFrom(frame.toByteArray)
525+
SparqlResultsFrame.parseFrom(gFrame.toByteArray) shouldBe frame
526+
}
527+
}
528+
529+
"round-trip in delimited binary form" when {
530+
for (name, frame) <- cases do
531+
s"a $name" in {
532+
val out = ByteArrayOutputStream()
533+
frame.writeDelimitedTo(out)
534+
val gFrame =
535+
google.SparqlResultsFrame.parseDelimitedFrom(ByteArrayInputStream(out.toByteArray))
536+
val gOut = ByteArrayOutputStream()
537+
gFrame.writeDelimitedTo(gOut)
538+
SparqlResultsFrame.parseDelimitedFrom(
539+
ByteArrayInputStream(gOut.toByteArray),
540+
) shouldBe frame
541+
}
542+
}
543+
544+
"round-trip the message in Text Format" when {
545+
for (name, frame) <- cases do
546+
s"a $name" in {
547+
val gFrame = google.SparqlResultsFrame.parseFrom(frame.toByteArray)
548+
val text = gFrame.toString
549+
val gFrame2 = TextFormat.parse(text, classOf[google.SparqlResultsFrame])
550+
gFrame2 shouldBe gFrame
551+
SparqlResultsFrame.parseFrom(gFrame2.toByteArray) shouldBe frame
552+
}
553+
}
554+
}

0 commit comments

Comments
 (0)