From f6fa0f9a8bc05cd07785133d9359cf1f14b492e6 Mon Sep 17 00:00:00 2001 From: Nadav Samet Date: Mon, 11 Apr 2016 22:22:49 -0700 Subject: [PATCH] Add single_file and preamble. Fixes #67 --- .../java/com/trueaccord/scalapb/Scalapb.java | 408 +++++++++++++++++- .../scalapb/compiler/DescriptorPimps.scala | 1 - .../scalapb/compiler/ProtobufGenerator.scala | 63 ++- e2e/src/main/protobuf/sealed_trait.proto | 33 ++ .../src/test/scala/GeneratedCodeSpec.scala | 4 +- proptest/src/test/scala/GraphGen.scala | 2 + protobuf/scalapb/scalapb.proto | 8 + .../java/com/trueaccord/scalapb/Scalapb.java | 408 +++++++++++++++++- 8 files changed, 889 insertions(+), 38 deletions(-) create mode 100644 e2e/src/main/protobuf/sealed_trait.proto diff --git a/compiler-plugin/src/main/java/com/trueaccord/scalapb/Scalapb.java b/compiler-plugin/src/main/java/com/trueaccord/scalapb/Scalapb.java index be8d79fa4..ef8f6daf6 100644 --- a/compiler-plugin/src/main/java/com/trueaccord/scalapb/Scalapb.java +++ b/compiler-plugin/src/main/java/com/trueaccord/scalapb/Scalapb.java @@ -102,6 +102,64 @@ public interface ScalaPbOptionsOrBuilder extends */ com.google.protobuf.ByteString getImportBytes(int index); + + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + boolean hasSingleFile(); + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + boolean getSingleFile(); + + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + com.google.protobuf.ProtocolStringList + getPreambleList(); + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + int getPreambleCount(); + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + java.lang.String getPreamble(int index); + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + com.google.protobuf.ByteString + getPreambleBytes(int index); } /** * Protobuf type {@code scalapb.ScalaPbOptions} @@ -118,6 +176,8 @@ private ScalaPbOptions() { packageName_ = ""; flatPackage_ = false; import_ = com.google.protobuf.LazyStringArrayList.EMPTY; + singleFile_ = false; + preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; } @java.lang.Override @@ -167,6 +227,20 @@ private ScalaPbOptions( import_.add(bs); break; } + case 34: { + com.google.protobuf.ByteString bs = input.readBytes(); + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000010; + } + preamble_.add(bs); + break; + } + case 40: { + bitField0_ |= 0x00000004; + singleFile_ = input.readBool(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -179,6 +253,9 @@ private ScalaPbOptions( if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { import_ = import_.getUnmodifiableView(); } + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = preamble_.getUnmodifiableView(); + } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } @@ -328,6 +405,80 @@ public java.lang.String getImport(int index) { return import_.getByteString(index); } + public static final int SINGLE_FILE_FIELD_NUMBER = 5; + private boolean singleFile_; + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + public boolean hasSingleFile() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + public boolean getSingleFile() { + return singleFile_; + } + + public static final int PREAMBLE_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList preamble_; + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public com.google.protobuf.ProtocolStringList + getPreambleList() { + return preamble_; + } + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public int getPreambleCount() { + return preamble_.size(); + } + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public java.lang.String getPreamble(int index) { + return preamble_.get(index); + } + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public com.google.protobuf.ByteString + getPreambleBytes(int index) { + return preamble_.getByteString(index); + } + private byte memoizedIsInitialized = -1; public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; @@ -349,6 +500,12 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < import_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 3, import_.getRaw(i)); } + for (int i = 0; i < preamble_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, preamble_.getRaw(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBool(5, singleFile_); + } unknownFields.writeTo(output); } @@ -372,6 +529,18 @@ public int getSerializedSize() { size += dataSize; size += 1 * getImportList().size(); } + { + int dataSize = 0; + for (int i = 0; i < preamble_.size(); i++) { + dataSize += computeStringSizeNoTag(preamble_.getRaw(i)); + } + size += dataSize; + size += 1 * getPreambleList().size(); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, singleFile_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -490,6 +659,10 @@ public Builder clear() { bitField0_ = (bitField0_ & ~0x00000002); import_ = com.google.protobuf.LazyStringArrayList.EMPTY; bitField0_ = (bitField0_ & ~0x00000004); + singleFile_ = false; + bitField0_ = (bitField0_ & ~0x00000008); + preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); return this; } @@ -527,6 +700,15 @@ public com.trueaccord.scalapb.Scalapb.ScalaPbOptions buildPartial() { bitField0_ = (bitField0_ & ~0x00000004); } result.import_ = import_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + result.singleFile_ = singleFile_; + if (((bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = preamble_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.preamble_ = preamble_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -561,6 +743,19 @@ public Builder mergeFrom(com.trueaccord.scalapb.Scalapb.ScalaPbOptions other) { } onChanged(); } + if (other.hasSingleFile()) { + setSingleFile(other.getSingleFile()); + } + if (!other.preamble_.isEmpty()) { + if (preamble_.isEmpty()) { + preamble_ = other.preamble_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensurePreambleIsMutable(); + preamble_.addAll(other.preamble_); + } + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -887,6 +1082,196 @@ public Builder addImportBytes( return this; } + private boolean singleFile_ ; + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public boolean hasSingleFile() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public boolean getSingleFile() { + return singleFile_; + } + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public Builder setSingleFile(boolean value) { + bitField0_ |= 0x00000008; + singleFile_ = value; + onChanged(); + return this; + } + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public Builder clearSingleFile() { + bitField0_ = (bitField0_ & ~0x00000008); + singleFile_ = false; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringList preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensurePreambleIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = new com.google.protobuf.LazyStringArrayList(preamble_); + bitField0_ |= 0x00000010; + } + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public com.google.protobuf.ProtocolStringList + getPreambleList() { + return preamble_.getUnmodifiableView(); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public int getPreambleCount() { + return preamble_.size(); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public java.lang.String getPreamble(int index) { + return preamble_.get(index); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public com.google.protobuf.ByteString + getPreambleBytes(int index) { + return preamble_.getByteString(index); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder setPreamble( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreambleIsMutable(); + preamble_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder addPreamble( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreambleIsMutable(); + preamble_.add(value); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder addAllPreamble( + java.lang.Iterable values) { + ensurePreambleIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, preamble_); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder clearPreamble() { + preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder addPreambleBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreambleIsMutable(); + preamble_.add(value); + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:scalapb.ScalaPbOptions) } @@ -2046,17 +2431,18 @@ public com.trueaccord.scalapb.Scalapb.FieldOptions getDefaultInstanceForType() { static { java.lang.String[] descriptorData = { "\n\025scalapb/scalapb.proto\022\007scalapb\032 google" + - "/protobuf/descriptor.proto\"L\n\016ScalaPbOpt" + + "/protobuf/descriptor.proto\"s\n\016ScalaPbOpt" + "ions\022\024\n\014package_name\030\001 \001(\t\022\024\n\014flat_packa" + - "ge\030\002 \001(\010\022\016\n\006import\030\003 \003(\t\"!\n\016MessageOptio" + - "ns\022\017\n\007extends\030\001 \003(\t\"\034\n\014FieldOptions\022\014\n\004t" + - "ype\030\001 \001(\t:G\n\007options\022\034.google.protobuf.F" + - "ileOptions\030\374\007 \001(\0132\027.scalapb.ScalaPbOptio" + - "ns:J\n\007message\022\037.google.protobuf.MessageO" + - "ptions\030\374\007 \001(\0132\027.scalapb.MessageOptions:D" + - "\n\005field\022\035.google.protobuf.FieldOptions\030\374", - "\007 \001(\0132\025.scalapb.FieldOptionsB\030\n\026com.true" + - "accord.scalapb" + "ge\030\002 \001(\010\022\016\n\006import\030\003 \003(\t\022\023\n\013single_file\030" + + "\005 \001(\010\022\020\n\010preamble\030\004 \003(\t\"!\n\016MessageOption" + + "s\022\017\n\007extends\030\001 \003(\t\"\034\n\014FieldOptions\022\014\n\004ty" + + "pe\030\001 \001(\t:G\n\007options\022\034.google.protobuf.Fi" + + "leOptions\030\374\007 \001(\0132\027.scalapb.ScalaPbOption" + + "s:J\n\007message\022\037.google.protobuf.MessageOp" + + "tions\030\374\007 \001(\0132\027.scalapb.MessageOptions:D\n", + "\005field\022\035.google.protobuf.FieldOptions\030\374\007" + + " \001(\0132\025.scalapb.FieldOptionsB\030\n\026com.truea" + + "ccord.scalapb" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { @@ -2076,7 +2462,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_scalapb_ScalaPbOptions_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_scalapb_ScalaPbOptions_descriptor, - new java.lang.String[] { "PackageName", "FlatPackage", "Import", }); + new java.lang.String[] { "PackageName", "FlatPackage", "Import", "SingleFile", "Preamble", }); internal_static_scalapb_MessageOptions_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_scalapb_MessageOptions_fieldAccessorTable = new diff --git a/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/DescriptorPimps.scala b/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/DescriptorPimps.scala index 43bc1ee4e..dc8044e36 100644 --- a/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/DescriptorPimps.scala +++ b/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/DescriptorPimps.scala @@ -400,7 +400,6 @@ trait DescriptorPimps { def baseName(fileName: String) = fileName.split("/").last.replaceAll(raw"[.]proto$$|[.]protodevel", "") - } object Helper { diff --git a/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/ProtobufGenerator.scala b/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/ProtobufGenerator.scala index 8658bf1e4..3a2850648 100644 --- a/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/ProtobufGenerator.scala +++ b/compiler-plugin/src/main/scala/com/trueaccord/scalapb/compiler/ProtobufGenerator.scala @@ -8,6 +8,9 @@ import scala.collection.JavaConversions._ case class GeneratorParams(javaConversions: Boolean = false, flatPackage: Boolean = false, grpc: Boolean = false) +// Exceptions that are caught and passed upstreams as errors. +case class GeneratorException(message: String) extends Exception(message) + class ProtobufGenerator(val params: GeneratorParams) extends DescriptorPimps { def printEnum(e: EnumDescriptor, printer: FunctionalPrinter): FunctionalPrinter = { val name = e.nameSymbol @@ -887,6 +890,9 @@ class ProtobufGenerator(val params: GeneratorParams) extends DescriptorPimps { } def scalaFileHeader(file: FileDescriptor): FunctionalPrinter = { + if (file.scalaOptions.getPreambleList.nonEmpty && !file.scalaOptions.getSingleFile) { + throw new GeneratorException(s"${file.getName}: single_file must be true when a preamble is provided.") + } new FunctionalPrinter().addM( s"""// Generated by the Scala Plugin for the Protocol Buffer Compiler. |// Do not edit! @@ -900,6 +906,8 @@ class ProtobufGenerator(val params: GeneratorParams) extends DescriptorPimps { case (i, printer) => printer.add(s"import $i") } .add("") + .seq(file.scalaOptions.getPreambleList) + .when(file.scalaOptions.getPreambleList.nonEmpty)(_.add("")) } def generateFileDescriptor(file: FileDescriptor)(fp: FunctionalPrinter): FunctionalPrinter = { @@ -960,8 +968,24 @@ class ProtobufGenerator(val params: GeneratorParams) extends DescriptorPimps { } } - def generateScalaFilesForFileDescriptor(file: FileDescriptor): Seq[CodeGeneratorResponse.File] = { - val serviceFiles = if(params.grpc) { + def generateSingleScalaFileForFileDescriptor(file: FileDescriptor): Seq[CodeGeneratorResponse.File] = { + val code = + scalaFileHeader(file) + .print(file.getEnumTypes)(printEnum) + .print(file.getMessageTypes)(printMessage) + .add(s"object ${file.fileDescriptorObjectName} {") + .indent + .call(generateFileDescriptor(file)) + .outdent + .add("}").result() + val b = CodeGeneratorResponse.File.newBuilder() + b.setName(file.scalaPackageName.replace('.', '/') + "/" + file.fileDescriptorObjectName + ".scala") + b.setContent(code) + generateServiceFiles(file) :+ b.build + } + + def generateServiceFiles(file: FileDescriptor): Seq[CodeGeneratorResponse.File] = { + if(params.grpc) { file.getServices.map { service => val p = new GrpcServicePrinter(service, params) val code = p.printService(FunctionalPrinter()).result() @@ -971,6 +995,10 @@ class ProtobufGenerator(val params: GeneratorParams) extends DescriptorPimps { b.build } } else Nil + } + + def generateMultipleScalaFilesForFileDescriptor(file: FileDescriptor): Seq[CodeGeneratorResponse.File] = { + val serviceFiles = generateServiceFiles(file) val enumFiles = for { enum <- file.getEnumTypes @@ -1026,18 +1054,27 @@ object ProtobufGenerator { val b = CodeGeneratorResponse.newBuilder parseParameters(request.getParameter) match { case Right(params) => - val generator = new ProtobufGenerator(params) - val filesByName: Map[String, FileDescriptor] = - request.getProtoFileList.foldLeft[Map[String, FileDescriptor]](Map.empty) { - case (acc, fp) => - val deps = fp.getDependencyList.map(acc) - acc + (fp.getName -> FileDescriptor.buildFrom(fp, deps.toArray)) + try { + val generator = new ProtobufGenerator(params) + import generator.FileDescriptorPimp + val filesByName: Map[String, FileDescriptor] = + request.getProtoFileList.foldLeft[Map[String, FileDescriptor]](Map.empty) { + case (acc, fp) => + val deps = fp.getDependencyList.map(acc) + acc + (fp.getName -> FileDescriptor.buildFrom(fp, deps.toArray)) + } + request.getFileToGenerateList.foreach { + name => + val file = filesByName(name) + val responseFiles = + if (file.scalaOptions.getSingleFile) + generator.generateSingleScalaFileForFileDescriptor(file) + else generator.generateMultipleScalaFilesForFileDescriptor(file) + b.addAllFile(responseFiles) } - request.getFileToGenerateList.foreach { - name => - val file = filesByName(name) - val responseFiles = generator.generateScalaFilesForFileDescriptor(file) - b.addAllFile(responseFiles) + } catch { + case e: GeneratorException => + b.setError(e.message) } case Left(error) => b.setError(error) diff --git a/e2e/src/main/protobuf/sealed_trait.proto b/e2e/src/main/protobuf/sealed_trait.proto new file mode 100644 index 000000000..98b80ad09 --- /dev/null +++ b/e2e/src/main/protobuf/sealed_trait.proto @@ -0,0 +1,33 @@ +syntax = "proto2"; + +package com.trueaccord.proto.e2e; + +import "scalapb/scalapb.proto"; + +// Demonstrates a sealed trait and two messages extending it. + +option (scalapb.options) = { + // All classes that extend a sealed trait need to be in the same Scala + // file, so we set single_file to true. + single_file: true + + // Generate the base trait. + preamble: [ + "sealed trait MyBaseTrait {", + " def getFoo: Int", + " def getBar: String", + "}" + ]; +}; + +message Type1 { + option (scalapb.message).extends = "MyBaseTrait"; + optional int32 foo = 1; + optional string bar = 2; +} + +message Type2 { + option (scalapb.message).extends = "MyBaseTrait"; + optional int32 foo = 1; + optional string bar = 2; +} diff --git a/proptest/src/test/scala/GeneratedCodeSpec.scala b/proptest/src/test/scala/GeneratedCodeSpec.scala index 6f6b7a019..8325316c6 100644 --- a/proptest/src/test/scala/GeneratedCodeSpec.scala +++ b/proptest/src/test/scala/GeneratedCodeSpec.scala @@ -86,8 +86,8 @@ class GeneratedCodeSpec extends PropSpec with GeneratorDrivenPropertyChecks with javaParse(scalaAscii) should be(javaProto) javaParse(scalaUnicodeAscii) should be(javaProto) - val jsonRep = com.trueaccord.scalapb.json.JsonFormat.toJson(scalaProto) - com.trueaccord.scalapb.json.JsonFormat.fromJson(jsonRep)(companion.asInstanceOf[GeneratedMessageCompanion[T] forSome {type T <: GeneratedMessage with ScalaPBMessage[T] }]) should be(scalaProto) + val jsonRep = com.trueaccord.scalapb.json.JsonFormat.toJsonString(scalaProto) + com.trueaccord.scalapb.json.JsonFormat.fromJsonString(jsonRep)(companion.asInstanceOf[GeneratedMessageCompanion[T] forSome {type T <: GeneratedMessage with ScalaPBMessage[T] }]) should be(scalaProto) } catch { case e: Exception => diff --git a/proptest/src/test/scala/GraphGen.scala b/proptest/src/test/scala/GraphGen.scala index fc154a305..0cdc074d9 100644 --- a/proptest/src/test/scala/GraphGen.scala +++ b/proptest/src/test/scala/GraphGen.scala @@ -130,12 +130,14 @@ object GraphGen { def genScalaOptions(state: State): Gen[(Option[ScalaPbOptions], State)] = for { (scalaPackageName, state) <- GenUtils.listWithStatefulGen(state, minSize = 1, maxSize = 4)(_.generateName) flatPackage <- Gen.oneOf(true, false) + singleFile <- Gen.oneOf(true, false) } yield { val b = ScalaPbOptions.newBuilder if (scalaPackageName.nonEmpty) { b.setPackageName(scalaPackageName.mkString(".")) } b.setFlatPackage(flatPackage) + .setSingleFile(singleFile) (Some(b.build), state) } diff --git a/protobuf/scalapb/scalapb.proto b/protobuf/scalapb/scalapb.proto index b310b6758..e49da7ea5 100644 --- a/protobuf/scalapb/scalapb.proto +++ b/protobuf/scalapb/scalapb.proto @@ -19,6 +19,14 @@ message ScalaPbOptions { // Adds the following imports at the top of the file (this is meant // to provide implicit TypeMappers) repeated string import = 3; + + // If true, all messages and enums (but not services) will be written + // to a single Scala file. + optional bool single_file = 5; + + // Text to add to the generated scala file. This can be used only + // when single_file is true. + repeated string preamble = 4; } extend google.protobuf.FileOptions { diff --git a/scalapb-runtime/jvm/src/main/java/com/trueaccord/scalapb/Scalapb.java b/scalapb-runtime/jvm/src/main/java/com/trueaccord/scalapb/Scalapb.java index be8d79fa4..ef8f6daf6 100644 --- a/scalapb-runtime/jvm/src/main/java/com/trueaccord/scalapb/Scalapb.java +++ b/scalapb-runtime/jvm/src/main/java/com/trueaccord/scalapb/Scalapb.java @@ -102,6 +102,64 @@ public interface ScalaPbOptionsOrBuilder extends */ com.google.protobuf.ByteString getImportBytes(int index); + + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + boolean hasSingleFile(); + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + boolean getSingleFile(); + + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + com.google.protobuf.ProtocolStringList + getPreambleList(); + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + int getPreambleCount(); + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + java.lang.String getPreamble(int index); + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + com.google.protobuf.ByteString + getPreambleBytes(int index); } /** * Protobuf type {@code scalapb.ScalaPbOptions} @@ -118,6 +176,8 @@ private ScalaPbOptions() { packageName_ = ""; flatPackage_ = false; import_ = com.google.protobuf.LazyStringArrayList.EMPTY; + singleFile_ = false; + preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; } @java.lang.Override @@ -167,6 +227,20 @@ private ScalaPbOptions( import_.add(bs); break; } + case 34: { + com.google.protobuf.ByteString bs = input.readBytes(); + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000010; + } + preamble_.add(bs); + break; + } + case 40: { + bitField0_ |= 0x00000004; + singleFile_ = input.readBool(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -179,6 +253,9 @@ private ScalaPbOptions( if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { import_ = import_.getUnmodifiableView(); } + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = preamble_.getUnmodifiableView(); + } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } @@ -328,6 +405,80 @@ public java.lang.String getImport(int index) { return import_.getByteString(index); } + public static final int SINGLE_FILE_FIELD_NUMBER = 5; + private boolean singleFile_; + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + public boolean hasSingleFile() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bool single_file = 5; + * + *
+     * If true, all messages and enums (but not services) will be written
+     * to a single Scala file.
+     * 
+ */ + public boolean getSingleFile() { + return singleFile_; + } + + public static final int PREAMBLE_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList preamble_; + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public com.google.protobuf.ProtocolStringList + getPreambleList() { + return preamble_; + } + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public int getPreambleCount() { + return preamble_.size(); + } + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public java.lang.String getPreamble(int index) { + return preamble_.get(index); + } + /** + * repeated string preamble = 4; + * + *
+     * Text to add to the generated scala file.  This can be used only
+     * when single_file is true.
+     * 
+ */ + public com.google.protobuf.ByteString + getPreambleBytes(int index) { + return preamble_.getByteString(index); + } + private byte memoizedIsInitialized = -1; public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; @@ -349,6 +500,12 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < import_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 3, import_.getRaw(i)); } + for (int i = 0; i < preamble_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, preamble_.getRaw(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBool(5, singleFile_); + } unknownFields.writeTo(output); } @@ -372,6 +529,18 @@ public int getSerializedSize() { size += dataSize; size += 1 * getImportList().size(); } + { + int dataSize = 0; + for (int i = 0; i < preamble_.size(); i++) { + dataSize += computeStringSizeNoTag(preamble_.getRaw(i)); + } + size += dataSize; + size += 1 * getPreambleList().size(); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, singleFile_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -490,6 +659,10 @@ public Builder clear() { bitField0_ = (bitField0_ & ~0x00000002); import_ = com.google.protobuf.LazyStringArrayList.EMPTY; bitField0_ = (bitField0_ & ~0x00000004); + singleFile_ = false; + bitField0_ = (bitField0_ & ~0x00000008); + preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); return this; } @@ -527,6 +700,15 @@ public com.trueaccord.scalapb.Scalapb.ScalaPbOptions buildPartial() { bitField0_ = (bitField0_ & ~0x00000004); } result.import_ = import_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + result.singleFile_ = singleFile_; + if (((bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = preamble_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.preamble_ = preamble_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -561,6 +743,19 @@ public Builder mergeFrom(com.trueaccord.scalapb.Scalapb.ScalaPbOptions other) { } onChanged(); } + if (other.hasSingleFile()) { + setSingleFile(other.getSingleFile()); + } + if (!other.preamble_.isEmpty()) { + if (preamble_.isEmpty()) { + preamble_ = other.preamble_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensurePreambleIsMutable(); + preamble_.addAll(other.preamble_); + } + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -887,6 +1082,196 @@ public Builder addImportBytes( return this; } + private boolean singleFile_ ; + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public boolean hasSingleFile() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public boolean getSingleFile() { + return singleFile_; + } + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public Builder setSingleFile(boolean value) { + bitField0_ |= 0x00000008; + singleFile_ = value; + onChanged(); + return this; + } + /** + * optional bool single_file = 5; + * + *
+       * If true, all messages and enums (but not services) will be written
+       * to a single Scala file.
+       * 
+ */ + public Builder clearSingleFile() { + bitField0_ = (bitField0_ & ~0x00000008); + singleFile_ = false; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringList preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensurePreambleIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + preamble_ = new com.google.protobuf.LazyStringArrayList(preamble_); + bitField0_ |= 0x00000010; + } + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public com.google.protobuf.ProtocolStringList + getPreambleList() { + return preamble_.getUnmodifiableView(); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public int getPreambleCount() { + return preamble_.size(); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public java.lang.String getPreamble(int index) { + return preamble_.get(index); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public com.google.protobuf.ByteString + getPreambleBytes(int index) { + return preamble_.getByteString(index); + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder setPreamble( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreambleIsMutable(); + preamble_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder addPreamble( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreambleIsMutable(); + preamble_.add(value); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder addAllPreamble( + java.lang.Iterable values) { + ensurePreambleIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, preamble_); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder clearPreamble() { + preamble_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + return this; + } + /** + * repeated string preamble = 4; + * + *
+       * Text to add to the generated scala file.  This can be used only
+       * when single_file is true.
+       * 
+ */ + public Builder addPreambleBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreambleIsMutable(); + preamble_.add(value); + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:scalapb.ScalaPbOptions) } @@ -2046,17 +2431,18 @@ public com.trueaccord.scalapb.Scalapb.FieldOptions getDefaultInstanceForType() { static { java.lang.String[] descriptorData = { "\n\025scalapb/scalapb.proto\022\007scalapb\032 google" + - "/protobuf/descriptor.proto\"L\n\016ScalaPbOpt" + + "/protobuf/descriptor.proto\"s\n\016ScalaPbOpt" + "ions\022\024\n\014package_name\030\001 \001(\t\022\024\n\014flat_packa" + - "ge\030\002 \001(\010\022\016\n\006import\030\003 \003(\t\"!\n\016MessageOptio" + - "ns\022\017\n\007extends\030\001 \003(\t\"\034\n\014FieldOptions\022\014\n\004t" + - "ype\030\001 \001(\t:G\n\007options\022\034.google.protobuf.F" + - "ileOptions\030\374\007 \001(\0132\027.scalapb.ScalaPbOptio" + - "ns:J\n\007message\022\037.google.protobuf.MessageO" + - "ptions\030\374\007 \001(\0132\027.scalapb.MessageOptions:D" + - "\n\005field\022\035.google.protobuf.FieldOptions\030\374", - "\007 \001(\0132\025.scalapb.FieldOptionsB\030\n\026com.true" + - "accord.scalapb" + "ge\030\002 \001(\010\022\016\n\006import\030\003 \003(\t\022\023\n\013single_file\030" + + "\005 \001(\010\022\020\n\010preamble\030\004 \003(\t\"!\n\016MessageOption" + + "s\022\017\n\007extends\030\001 \003(\t\"\034\n\014FieldOptions\022\014\n\004ty" + + "pe\030\001 \001(\t:G\n\007options\022\034.google.protobuf.Fi" + + "leOptions\030\374\007 \001(\0132\027.scalapb.ScalaPbOption" + + "s:J\n\007message\022\037.google.protobuf.MessageOp" + + "tions\030\374\007 \001(\0132\027.scalapb.MessageOptions:D\n", + "\005field\022\035.google.protobuf.FieldOptions\030\374\007" + + " \001(\0132\025.scalapb.FieldOptionsB\030\n\026com.truea" + + "ccord.scalapb" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { @@ -2076,7 +2462,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_scalapb_ScalaPbOptions_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_scalapb_ScalaPbOptions_descriptor, - new java.lang.String[] { "PackageName", "FlatPackage", "Import", }); + new java.lang.String[] { "PackageName", "FlatPackage", "Import", "SingleFile", "Preamble", }); internal_static_scalapb_MessageOptions_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_scalapb_MessageOptions_fieldAccessorTable = new