소스 검색

Add protoc plugin.

Original patch by igorgatis@gmail.com, tweaked a little before commit.

Fixes issue 90.
Jon Skeet 11 년 전
부모
커밋
92d6214e0f

+ 3 - 0
build/build.csproj

@@ -189,6 +189,9 @@
     <StaticPackageItem Include="$(ProjectDirectory)\protos\google\protobuf\descriptor.proto">
       <TargetDirectory>\protos\google\protobuf</TargetDirectory>
     </StaticPackageItem>
+    <StaticPackageItem Include="$(ProjectDirectory)\protos\google\protobuf\compiler\plugin.proto">
+      <TargetDirectory>\protos\google\protobuf\compiler</TargetDirectory>
+    </StaticPackageItem>
     <StaticPackageItem Include="$(ProjectDirectory)\protos\google\protobuf\csharp_options.proto">
       <TargetDirectory>\protos\google\protobuf</TargetDirectory>
     </StaticPackageItem>

+ 147 - 0
protos/google/protobuf/compiler/plugin.proto

@@ -0,0 +1,147 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+//
+// WARNING:  The plugin interface is currently EXPERIMENTAL and is subject to
+//   change.
+//
+// protoc (aka the Protocol Compiler) can be extended via plugins.  A plugin is
+// just a program that reads a CodeGeneratorRequest from stdin and writes a
+// CodeGeneratorResponse to stdout.
+//
+// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead
+// of dealing with the raw protocol defined here.
+//
+// A plugin executable needs only to be placed somewhere in the path.  The
+// plugin should be named "protoc-gen-$NAME", and will then be used when the
+// flag "--${NAME}_out" is passed to protoc.
+
+package google.protobuf.compiler;
+option java_package = "com.google.protobuf.compiler";
+option java_outer_classname = "PluginProtos";
+
+import "google/protobuf/descriptor.proto";
+
+// An encoded CodeGeneratorRequest is written to the plugin's stdin.
+message CodeGeneratorRequest {
+  // The .proto files that were explicitly listed on the command-line.  The
+  // code generator should generate code only for these files.  Each file's
+  // descriptor will be included in proto_file, below.
+  repeated string file_to_generate = 1;
+
+  // The generator parameter passed on the command-line.
+  optional string parameter = 2;
+
+  // FileDescriptorProtos for all files in files_to_generate and everything
+  // they import.  The files will appear in topological order, so each file
+  // appears before any file that imports it.
+  //
+  // protoc guarantees that all proto_files will be written after
+  // the fields above, even though this is not technically guaranteed by the
+  // protobuf wire format.  This theoretically could allow a plugin to stream
+  // in the FileDescriptorProtos and handle them one by one rather than read
+  // the entire set into memory at once.  However, as of this writing, this
+  // is not similarly optimized on protoc's end -- it will store all fields in
+  // memory at once before sending them to the plugin.
+  repeated FileDescriptorProto proto_file = 15;
+}
+
+// The plugin writes an encoded CodeGeneratorResponse to stdout.
+message CodeGeneratorResponse {
+  // Error message.  If non-empty, code generation failed.  The plugin process
+  // should exit with status code zero even if it reports an error in this way.
+  //
+  // This should be used to indicate errors in .proto files which prevent the
+  // code generator from generating correct code.  Errors which indicate a
+  // problem in protoc itself -- such as the input CodeGeneratorRequest being
+  // unparseable -- should be reported by writing a message to stderr and
+  // exiting with a non-zero status code.
+  optional string error = 1;
+
+  // Represents a single generated file.
+  message File {
+    // The file name, relative to the output directory.  The name must not
+    // contain "." or ".." components and must be relative, not be absolute (so,
+    // the file cannot lie outside the output directory).  "/" must be used as
+    // the path separator, not "\".
+    //
+    // If the name is omitted, the content will be appended to the previous
+    // file.  This allows the generator to break large files into small chunks,
+    // and allows the generated text to be streamed back to protoc so that large
+    // files need not reside completely in memory at one time.  Note that as of
+    // this writing protoc does not optimize for this -- it will read the entire
+    // CodeGeneratorResponse before writing files to disk.
+    optional string name = 1;
+
+    // If non-empty, indicates that the named file should already exist, and the
+    // content here is to be inserted into that file at a defined insertion
+    // point.  This feature allows a code generator to extend the output
+    // produced by another code generator.  The original generator may provide
+    // insertion points by placing special annotations in the file that look
+    // like:
+    //   @@protoc_insertion_point(NAME)
+    // The annotation can have arbitrary text before and after it on the line,
+    // which allows it to be placed in a comment.  NAME should be replaced with
+    // an identifier naming the point -- this is what other generators will use
+    // as the insertion_point.  Code inserted at this point will be placed
+    // immediately above the line containing the insertion point (thus multiple
+    // insertions to the same point will come out in the order they were added).
+    // The double-@ is intended to make it unlikely that the generated code
+    // could contain things that look like insertion points by accident.
+    //
+    // For example, the C++ code generator places the following line in the
+    // .pb.h files that it generates:
+    //   // @@protoc_insertion_point(namespace_scope)
+    // This line appears within the scope of the file's package namespace, but
+    // outside of any particular class.  Another plugin can then specify the
+    // insertion_point "namespace_scope" to generate additional classes or
+    // other declarations that should be placed in this scope.
+    //
+    // Note that if the line containing the insertion point begins with
+    // whitespace, the same whitespace will be added to every line of the
+    // inserted text.  This is useful for languages like Python, where
+    // indentation matters.  In these languages, the insertion point comment
+    // should be indented the same amount as any inserted code will need to be
+    // in order to work correctly in that context.
+    //
+    // The code generator that generates the initial file and the one which
+    // inserts into it must both run as part of a single invocation of protoc.
+    // Code generators are executed in the order in which they appear on the
+    // command line.
+    //
+    // If |insertion_point| is present, |name| must also be present.
+    optional string insertion_point = 2;
+
+    // The file contents.
+    optional string content = 15;
+  }
+  repeated File file = 15;
+}

+ 6 - 6
src/ProtoGen.Test/DependencyResolutionTest.cs

@@ -52,7 +52,7 @@ namespace Google.ProtocolBuffers.ProtoGen
         {
             FileDescriptorProto first = new FileDescriptorProto.Builder {Name = "First"}.Build();
             FileDescriptorProto second = new FileDescriptorProto.Builder {Name = "Second"}.Build();
-            FileDescriptorSet set = new FileDescriptorSet.Builder { FileList = { first, second } }.Build();
+            var set = new List<FileDescriptorProto> { first, second };
 
             IList<FileDescriptor> converted = Generator.ConvertDescriptors(CSharpFileOptions.DefaultInstance, set);
             Assert.AreEqual(2, converted.Count);
@@ -68,7 +68,7 @@ namespace Google.ProtocolBuffers.ProtoGen
             FileDescriptorProto first =
                 new FileDescriptorProto.Builder {Name = "First", DependencyList = {"Second"}}.Build();
             FileDescriptorProto second = new FileDescriptorProto.Builder {Name = "Second"}.Build();
-            FileDescriptorSet set = new FileDescriptorSet.Builder { FileList = { first, second } }.Build();
+            var set = new List<FileDescriptorProto> { first, second };
             IList<FileDescriptor> converted = Generator.ConvertDescriptors(CSharpFileOptions.DefaultInstance, set);
             Assert.AreEqual(2, converted.Count);
             Assert.AreEqual("First", converted[0].Name);
@@ -84,7 +84,7 @@ namespace Google.ProtocolBuffers.ProtoGen
             FileDescriptorProto first = new FileDescriptorProto.Builder {Name = "First"}.Build();
             FileDescriptorProto second =
                 new FileDescriptorProto.Builder {Name = "Second", DependencyList = {"First"}}.Build();
-            FileDescriptorSet set = new FileDescriptorSet.Builder { FileList = { first, second } }.Build();
+            var set = new List<FileDescriptorProto> { first, second };
             IList<FileDescriptor> converted = Generator.ConvertDescriptors(CSharpFileOptions.DefaultInstance, set);
             Assert.AreEqual(2, converted.Count);
             Assert.AreEqual("First", converted[0].Name);
@@ -101,7 +101,7 @@ namespace Google.ProtocolBuffers.ProtoGen
                 new FileDescriptorProto.Builder {Name = "First", DependencyList = {"Second"}}.Build();
             FileDescriptorProto second =
                 new FileDescriptorProto.Builder {Name = "Second", DependencyList = {"First"}}.Build();
-            FileDescriptorSet set = new FileDescriptorSet.Builder { FileList = { first, second } }.Build();
+            var set = new List<FileDescriptorProto> { first, second };
             try
             {
                 Generator.ConvertDescriptors(CSharpFileOptions.DefaultInstance, set);
@@ -118,7 +118,7 @@ namespace Google.ProtocolBuffers.ProtoGen
         {
             FileDescriptorProto first =
                 new FileDescriptorProto.Builder {Name = "First", DependencyList = {"Second"}}.Build();
-            FileDescriptorSet set = new FileDescriptorSet.Builder { FileList = { first } }.Build();
+            var set = new List<FileDescriptorProto> { first };
             try
             {
                 Generator.ConvertDescriptors(CSharpFileOptions.DefaultInstance, set);
@@ -135,7 +135,7 @@ namespace Google.ProtocolBuffers.ProtoGen
         {
             FileDescriptorProto first =
                 new FileDescriptorProto.Builder {Name = "First", DependencyList = {"First"}}.Build();
-            FileDescriptorSet set = new FileDescriptorSet.Builder { FileList = { first } }.Build();
+            var set = new List<FileDescriptorProto> { first };
             try
             {
                 Generator.ConvertDescriptors(CSharpFileOptions.DefaultInstance, set);

+ 683 - 0
src/ProtoGen.Test/ProtocGenCsUnittests.cs

@@ -0,0 +1,683 @@
+#region Copyright notice and license
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://github.com/jskeet/dotnet-protobufs/
+// Original C++/Java/Python code:
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+namespace Google.ProtocolBuffers.ProtoGen
+{
+    /// <summary>
+    /// Tests protoc-gen-cs plugin.
+    /// </summary>
+    [TestFixture]
+    [Category("Preprocessor")]
+    public partial class ProtocGenCsUnittests
+    {
+        private static readonly string TempPath = Path.Combine(Path.GetTempPath(), "protoc-gen-cs.Test");
+
+        private const string DefaultProto =
+            @"
+package nunit.simple;
+// Test a very simple message.
+message MyMessage {
+  optional string name = 1;
+}";
+
+        #region TestFixture SetUp/TearDown
+
+        private static readonly string OriginalWorkingDirectory = Environment.CurrentDirectory;
+
+        private StringBuilder buffer = new StringBuilder();
+
+        [TestFixtureSetUp]
+        public virtual void Setup()
+        {
+            Teardown();
+            Directory.CreateDirectory(TempPath);
+            Environment.CurrentDirectory = TempPath;
+            this.buffer.Length = 0;
+        }
+
+        [TestFixtureTearDown]
+        public virtual void Teardown()
+        {
+            Environment.CurrentDirectory = OriginalWorkingDirectory;
+            if (Directory.Exists(TempPath))
+            {
+                Directory.Delete(TempPath, true);
+            }
+        }
+
+        #endregion
+
+        #region Helper Methods RunProtoGen / RunCsc
+
+        private void RunProtoc(int expect, string protoFile, params string[] args)
+        {
+            string protoPath = string.Format("-I. -I\"{0}\"", OriginalWorkingDirectory);
+            string plugin = string.Format("--plugin=\"{0}\"", Path.Combine(OriginalWorkingDirectory, "protoc-gen-cs.exe"));
+            string csOut = args.Length == 0 ? "--cs_out=." : string.Format("--cs_out=\"{0}:.\"", string.Join(" ", args));
+            // Start the child process.
+            Process p = new Process();
+            // Redirect the output stream of the child process.
+            p.StartInfo.CreateNoWindow = true;
+            p.StartInfo.UseShellExecute = false;
+            p.StartInfo.RedirectStandardError = true;
+            p.StartInfo.RedirectStandardOutput = true;
+            p.StartInfo.WorkingDirectory = TempPath;
+            p.StartInfo.FileName = Path.Combine(OriginalWorkingDirectory, "protoc.exe");
+            p.StartInfo.Arguments = string.Join(" ", new string[] { plugin, csOut, protoPath, protoFile });
+            p.Start();
+            // Read the output stream first and then wait.
+            buffer.AppendLine(string.Format("{0}> \"{1}\" {2}", p.StartInfo.WorkingDirectory, p.StartInfo.FileName, p.StartInfo.Arguments));
+            buffer.AppendLine(p.StandardError.ReadToEnd());
+            buffer.AppendLine(p.StandardOutput.ReadToEnd());
+            p.WaitForExit();
+            Assert.AreEqual(expect, p.ExitCode, this.buffer.ToString());
+        }
+
+        private Assembly RunCsc(int expect, params string[] sources)
+        {
+            using (TempFile tempDll = new TempFile(String.Empty))
+            {
+                tempDll.ChangeExtension(".dll");
+                List<string> args = new List<string>();
+                args.Add("/nologo");
+                args.Add("/target:library");
+                args.Add("/debug-");
+                args.Add(String.Format(@"""/out:{0}""", tempDll.TempPath));
+                args.Add("/r:System.dll");
+                args.Add(String.Format(@"""/r:{0}""",
+                                       typeof(Google.ProtocolBuffers.DescriptorProtos.DescriptorProto).Assembly.
+                                           Location));
+                args.AddRange(sources);
+
+                string exe = Path.Combine(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(),
+                                          "csc.exe");
+                ProcessStartInfo psi = new ProcessStartInfo(exe);
+                psi.WorkingDirectory = TempPath;
+                psi.CreateNoWindow = true;
+                psi.UseShellExecute = false;
+                psi.RedirectStandardOutput = true;
+                psi.RedirectStandardError = true;
+                psi.Arguments = string.Join(" ", args.ToArray());
+                Process p = Process.Start(psi);
+                buffer.AppendLine(string.Format("{0}> \"{1}\" {2}", p.StartInfo.WorkingDirectory, p.StartInfo.FileName, p.StartInfo.Arguments));
+                buffer.AppendLine(p.StandardError.ReadToEnd());
+                buffer.AppendLine(p.StandardOutput.ReadToEnd());
+                p.WaitForExit();
+                Assert.AreEqual(expect, p.ExitCode, this.buffer.ToString());
+
+                Assembly asm = null;
+                if (p.ExitCode == 0)
+                {
+                    byte[] allbytes = File.ReadAllBytes(tempDll.TempPath);
+                    asm = Assembly.Load(allbytes);
+
+                    foreach (Type t in asm.GetTypes())
+                    {
+                        Debug.WriteLine(t.FullName, asm.FullName);
+                    }
+                }
+                return asm;
+            }
+        }
+
+        #endregion
+
+        // *******************************************************************
+        // The following tests excercise options for protogen.exe
+        // *******************************************************************
+
+        [Test]
+        public void TestProtoFile()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath);
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithConflictingType()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (
+                ProtoFile proto = new ProtoFile(test + ".proto",
+                                                @"
+package nunit.simple;
+// Test a very simple message.
+message " +
+                                                test + @" {
+  optional string name = 1;
+} "))
+            {
+                RunProtoc(0, proto.TempPath);
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple." + test, true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple.Proto." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithNamespace()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "-namespace=MyNewNamespace");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("MyNewNamespace.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("MyNewNamespace." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithUmbrellaClassName()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach("MyUmbrellaClassname.cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "/umbrella_classname=MyUmbrellaClassname");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple.MyUmbrellaClassname", true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithNestedClass()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "-nest_classes=true");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple." + test + "+MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithExpandedNsDirectories()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(@"nunit\simple\" + test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "-expand_namespace_directories=true");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithNewExtension()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".Generated.cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "-file_extension=.Generated.cs");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithUmbrellaNamespace()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "-umbrella_namespace=MyUmbrella.Namespace");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple.MyUmbrella.Namespace." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithIgnoredUmbrellaNamespaceDueToNesting()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(0, proto.TempPath, "-nest_classes=true", "-umbrella_namespace=MyUmbrella.Namespace");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple." + test + "+MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithExplicitEmptyUmbrellaNamespace()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (
+                ProtoFile proto = new ProtoFile(test + ".proto",
+                                                @"
+package nunit.simple;
+// Test a very simple message.
+message " +
+                                                test + @" {
+  optional string name = 1;
+} "))
+            {
+                //Forces the umbrella class to not use a namespace even if a collision with a type is detected.
+                RunProtoc(0, proto.TempPath, "-umbrella_namespace=");
+                //error CS0441: 'nunit.simple.TestProtoFileWithExplicitEmptyUmbrellaNamespace': a class cannot be both static and sealed
+                RunCsc(1, source.TempPath);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithNewOutputFolder()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(@"generated-code\" + test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                RunProtoc(1, proto.TempPath, "-output_directory=generated-code");
+                Directory.CreateDirectory("generated-code");
+                RunProtoc(0, proto.TempPath, "-output_directory=generated-code");
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileAndIgnoreGoogleProtobuf()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (
+                ProtoFile proto = new ProtoFile(test + ".proto",
+                                                @"
+import ""google/protobuf/csharp_options.proto"";
+option (google.protobuf.csharp_file_options).namespace = ""MyNewNamespace"";
+" +
+                                                DefaultProto))
+            {
+                string google = Path.Combine(TempPath, "google\\protobuf");
+                Directory.CreateDirectory(google);
+                foreach (string file in Directory.GetFiles(Path.Combine(OriginalWorkingDirectory, "google\\protobuf")))
+                {
+                    File.Copy(file, Path.Combine(google, Path.GetFileName(file)));
+                }
+
+                Assert.AreEqual(0, Directory.GetFiles(TempPath, "*.cs").Length);
+                RunProtoc(0, proto.TempPath);
+                Assert.AreEqual(1, Directory.GetFiles(TempPath, "*.cs").Length);
+
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("MyNewNamespace.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("MyNewNamespace." + test, true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithoutIgnoreGoogleProtobuf()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (
+                ProtoFile proto = new ProtoFile(test + ".proto",
+                                                @"
+import ""google/protobuf/csharp_options.proto"";
+option (google.protobuf.csharp_file_options).namespace = ""MyNewNamespace"";
+" +
+                                                DefaultProto))
+            {
+                string google = Path.Combine(TempPath, "google\\protobuf");
+                Directory.CreateDirectory(google);
+                foreach (string file in Directory.GetFiles(Path.Combine(OriginalWorkingDirectory, "google\\protobuf")))
+                {
+                    File.Copy(file, Path.Combine(google, Path.GetFileName(file)));
+                }
+
+                Assert.AreEqual(0, Directory.GetFiles(TempPath, "*.cs").Length);
+                //Without the option this fails due to being unable to resolve google/protobuf descriptors
+                RunProtoc(0, proto.TempPath);
+            }
+        }
+
+        // *******************************************************************
+        // The following tests excercise options for protoc.exe
+        // *******************************************************************
+
+        [Test]
+        public void TestProtoFileWithIncludeImports()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (
+                ProtoFile proto = new ProtoFile(test + ".proto",
+                                                @"
+import ""google/protobuf/csharp_options.proto"";
+option (google.protobuf.csharp_file_options).namespace = ""MyNewNamespace"";
+
+package nunit.simple;
+// Test a very simple message.
+message MyMessage {
+  optional string name = 1;
+} ")
+                )
+            {
+                string google = Path.Combine(TempPath, "google\\protobuf");
+                Directory.CreateDirectory(google);
+                foreach (string file in Directory.GetFiles(Path.Combine(OriginalWorkingDirectory, "google\\protobuf")))
+                {
+                    File.Copy(file, Path.Combine(google, Path.GetFileName(file)));
+                }
+
+                Assert.AreEqual(0, Directory.GetFiles(TempPath, "*.cs").Length);
+                //if you specify the protoc option --include_imports this should build three source files
+                RunProtoc(0, proto.TempPath);
+                Assert.AreEqual(1, Directory.GetFiles(TempPath, "*.cs").Length);
+
+                //you can (and should) simply omit the inclusion of the extra source files in your project
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("MyNewNamespace.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("MyNewNamespace." + test, true, true);
+            }
+        }
+
+        //Seems the --proto_path or -I option is non-functional for me.  Maybe others have luck?
+        [Test]
+        public void TestProtoFileInDifferentDirectory()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto", DefaultProto))
+            {
+                Environment.CurrentDirectory = OriginalWorkingDirectory;
+                RunProtoc(0, proto.TempPath);
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple." + test, true, true);
+            }
+        }
+
+        // *******************************************************************
+        // Handling of mutliple input files
+        // *******************************************************************
+
+        [Test]
+        public void TestMultipleProtoFiles()
+        {
+            Setup();
+            using (TempFile source1 = TempFile.Attach("MyMessage.cs"))
+            using (
+                ProtoFile proto1 = new ProtoFile("MyMessage.proto",
+                                                 @"
+package nunit.simple;
+// Test a very simple message.
+message MyMessage {
+  optional string name = 1;
+}")
+                )
+            using (TempFile source2 = TempFile.Attach("MyMessageList.cs"))
+            using (
+                ProtoFile proto2 = new ProtoFile("MyMessageList.proto",
+                                                 @"
+package nunit.simple;
+import ""MyMessage.proto"";
+// Test a very simple message.
+message MyMessageList {
+  repeated MyMessage messages = 1;
+}")
+                )
+            {
+                RunProtoc(0, proto1.TempPath);
+                RunProtoc(0, proto2.TempPath);
+                Assembly a = RunCsc(0, source1.TempPath, source2.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t1 = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t1), "Expect an IMessage");
+                //assert that the message type is in the expected namespace
+                Type t2 = a.GetType("nunit.simple.MyMessageList", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t2), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple.Proto.MyMessage", true, true);
+                a.GetType("nunit.simple.Proto.MyMessageList", true, true);
+            }
+        }
+
+        [Test]
+        public void TestOneProtoFileWithBufferFile()
+        {
+            Setup();
+            using (TempFile source1 = TempFile.Attach("MyMessage.cs"))
+            using (
+                ProtoFile proto1 = new ProtoFile("MyMessage.proto",
+                                                 @"
+package nunit.simple;
+// Test a very simple message.
+message MyMessage {
+  optional string name = 1;
+}")
+                )
+            using (TempFile source2 = TempFile.Attach("MyMessageList.cs"))
+            using (
+                ProtoFile proto2 = new ProtoFile("MyMessageList.proto",
+                                                 @"
+package nunit.simple;
+import ""MyMessage.proto"";
+// Test a very simple message.
+message MyMessageList {
+  repeated MyMessage messages = 1;
+}")
+                )
+            {
+                //build the proto buffer for MyMessage
+                RunProtoc(0, proto1.TempPath);
+                //build the MyMessageList proto-buffer and generate code by including MyMessage.pb
+                RunProtoc(0, proto2.TempPath);
+                Assembly a = RunCsc(0, source1.TempPath, source2.TempPath);
+                //assert that the message type is in the expected namespace
+                Type t1 = a.GetType("nunit.simple.MyMessage", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t1), "Expect an IMessage");
+                //assert that the message type is in the expected namespace
+                Type t2 = a.GetType("nunit.simple.MyMessageList", true, true);
+                Assert.IsTrue(typeof(IMessage).IsAssignableFrom(t2), "Expect an IMessage");
+                //assert that we can find the static descriptor type
+                a.GetType("nunit.simple.Proto.MyMessage", true, true);
+                a.GetType("nunit.simple.Proto.MyMessageList", true, true);
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithService()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto",
+@"
+import ""google/protobuf/csharp_options.proto"";
+option (google.protobuf.csharp_file_options).service_generator_type = GENERIC;
+
+package nunit.simple;
+// Test a very simple message.
+message MyMessage {
+  optional string name = 1;
+}
+// test a very simple service.
+service TestService {
+  rpc Execute (MyMessage) returns (MyMessage);
+}"))
+            {
+                CopyInGoogleProtoFiles();
+
+                RunProtoc(0, proto.TempPath, "-nest_classes=false");
+                Assert.AreEqual(1, Directory.GetFiles(TempPath, "*.cs").Length);
+
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the service type is in the expected namespace
+                Type t1 = a.GetType("nunit.simple.TestService", true, true);
+                Assert.IsTrue(typeof(IService).IsAssignableFrom(t1), "Expect an IService");
+                Assert.IsTrue(t1.IsAbstract, "Expect abstract class");
+                //assert that the Stub subclass type is in the expected namespace
+                Type t2 = a.GetType("nunit.simple.TestService+Stub", true, true);
+                Assert.IsTrue(t1.IsAssignableFrom(t2), "Expect a sub of TestService");
+                Assert.IsFalse(t2.IsAbstract, "Expect concrete class");
+            }
+        }
+
+        [Test]
+        public void TestProtoFileWithServiceInternal()
+        {
+            string test = new StackFrame(false).GetMethod().Name;
+            Setup();
+            using (TempFile source = TempFile.Attach(test + ".cs"))
+            using (ProtoFile proto = new ProtoFile(test + ".proto",
+@"
+import ""google/protobuf/csharp_options.proto"";
+option (google.protobuf.csharp_file_options).service_generator_type = GENERIC;
+
+package nunit.simple;
+// Test a very simple message.
+message MyMessage {
+  optional string name = 1;
+}
+// test a very simple service.
+service TestService {
+  rpc Execute (MyMessage) returns (MyMessage);
+}"))
+            {
+                CopyInGoogleProtoFiles();
+
+                RunProtoc(0, proto.TempPath, "-nest_classes=false", "-public_classes=false");
+                Assert.AreEqual(1, Directory.GetFiles(TempPath, "*.cs").Length);
+
+                Assembly a = RunCsc(0, source.TempPath);
+                //assert that the service type is in the expected namespace
+                Type t1 = a.GetType("nunit.simple.TestService", true, true);
+                Assert.IsTrue(typeof(IService).IsAssignableFrom(t1), "Expect an IService");
+                Assert.IsTrue(t1.IsAbstract, "Expect abstract class");
+                //assert that the Stub subclass type is in the expected namespace
+                Type t2 = a.GetType("nunit.simple.TestService+Stub", true, true);
+                Assert.IsTrue(t1.IsAssignableFrom(t2), "Expect a sub of TestService");
+                Assert.IsFalse(t2.IsAbstract, "Expect concrete class");
+            }
+        }
+
+        private static void CopyInGoogleProtoFiles()
+        {
+            string google = Path.Combine(TempPath, "google\\protobuf");
+            Directory.CreateDirectory(google);
+            foreach (string file in Directory.GetFiles(Path.Combine(OriginalWorkingDirectory, "google\\protobuf")))
+            {
+                File.Copy(file, Path.Combine(google, Path.GetFileName(file)));
+            }
+        }
+    }
+}

+ 101 - 0
src/ProtoGen.Test/protoc-gen-cs.Test.csproj

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <EnvironmentFlavor>CLIENTPROFILE</EnvironmentFlavor>
+    <EnvironmentTemplate>NET35</EnvironmentTemplate>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Google.ProtocolBuffers.ProtoGen</RootNamespace>
+    <AssemblyName>protoc-gen-cs.Test</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\NET35\Debug</OutputPath>
+    <IntermediateOutputPath>obj\NET35\Debug\</IntermediateOutputPath>
+    <DefineConstants>DEBUG;TRACE;$(EnvironmentFlavor);$(EnvironmentTemplate)</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <NoStdLib>true</NoStdLib>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\NET35\Release</OutputPath>
+    <IntermediateOutputPath>obj\NET35\Release\</IntermediateOutputPath>
+    <DefineConstants>TRACE;$(EnvironmentFlavor);$(EnvironmentTemplate)</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <NoStdLib>true</NoStdLib>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="nunit.framework">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\lib\NUnit\lib\nunit.framework.dll</HintPath>
+    </Reference>
+    <Reference Include="mscorlib" />
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ProtocGenCsUnittests.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="TempFile.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
+      <Project>{6908bdce-d925-43f3-94ac-a531e6df2591}</Project>
+      <Name>ProtocolBuffers</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\ProtoGen\protoc-gen-cs.csproj">
+      <Project>{250ade34-82fd-4bae-86d5-985fbe589c4b}</Project>
+      <Name>protoc-gen-cs</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\lib\protoc.exe">
+      <Link>protoc.exe</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\protos\google\protobuf\csharp_options.proto">
+      <Link>google\protobuf\csharp_options.proto</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="..\..\protos\google\protobuf\descriptor.proto">
+      <Link>google\protobuf\descriptor.proto</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <PropertyGroup>
+    <StartAction>Program</StartAction>
+    <StartProgram>$(ProjectDir)..\..\lib\NUnit\tools\nunit-console.exe</StartProgram>
+    <StartArguments>/nologo /noshadow /labels /wait $(AssemblyName).dll</StartArguments>
+    <StartWorkingDirectory>$(ProjectDir)$(OutputPath)</StartWorkingDirectory>
+  </PropertyGroup>
+</Project>

+ 18 - 26
src/ProtoGen/Generator.cs

@@ -39,6 +39,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Text;
 using Google.ProtocolBuffers.Collections;
+using Google.ProtocolBuffers.Compiler.PluginProto;
 using Google.ProtocolBuffers.DescriptorProtos;
 using Google.ProtocolBuffers.Descriptors;
 
@@ -65,20 +66,9 @@ namespace Google.ProtocolBuffers.ProtoGen
             return new Generator(options);
         }
 
-        public void Generate()
+        public void Generate(CodeGeneratorRequest request, CodeGeneratorResponse.Builder response)
         {
-            List<FileDescriptorSet> descriptorProtos = new List<FileDescriptorSet>();
-            foreach (string inputFile in options.InputFiles)
-            {
-                ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
-                CSharpOptions.RegisterAllExtensions(extensionRegistry);
-                using (Stream inputStream = File.OpenRead(inputFile))
-                {
-                    descriptorProtos.Add(FileDescriptorSet.ParseFrom(inputStream, extensionRegistry));
-                }
-            }
-
-            IList<FileDescriptor> descriptors = ConvertDescriptors(options.FileOptions, descriptorProtos.ToArray());
+            IList<FileDescriptor> descriptors = ConvertDescriptors(options.FileOptions, request.ProtoFileList);
 
             // Combine with options from command line
             foreach (FileDescriptor descriptor in descriptors)
@@ -99,6 +89,7 @@ namespace Google.ProtocolBuffers.ProtoGen
                 names.Add(file, true);
             }
 
+            var filesToGenerate = new HashSet<string>(request.FileToGenerateList);
             foreach (FileDescriptor descriptor in descriptors)
             {
                 // Optionally exclude descriptors in google.protobuf
@@ -106,7 +97,10 @@ namespace Google.ProtocolBuffers.ProtoGen
                 {
                     continue;
                 }
-                Generate(descriptor, duplicates);
+                if (filesToGenerate.Contains(descriptor.Name))
+                {
+                    Generate(descriptor, duplicates, response);
+                }
             }
         }
 
@@ -114,14 +108,20 @@ namespace Google.ProtocolBuffers.ProtoGen
         /// Generates code for a particular file. All dependencies must
         /// already have been resolved.
         /// </summary>
-        private void Generate(FileDescriptor descriptor, bool duplicates)
+        private void Generate(FileDescriptor descriptor, bool duplicates, CodeGeneratorResponse.Builder response)
         {
-            UmbrellaClassGenerator ucg = new UmbrellaClassGenerator(descriptor);
-            using (TextWriter textWriter = File.CreateText(GetOutputFile(descriptor, duplicates)))
+            var code = new StringBuilder();
+            var ucg = new UmbrellaClassGenerator(descriptor);
+            using (StringWriter textWriter = new StringWriter(code))
             {
                 TextGenerator writer = new TextGenerator(textWriter, options.LineBreak);
                 ucg.Generate(writer);
             }
+            response.AddFile(new CodeGeneratorResponse.Types.File.Builder
+            {
+                Name = GetOutputFile(descriptor, duplicates),
+                Content = code.ToString(),
+            }.Build());
         }
 
         private string GetOutputFile(FileDescriptor descriptor, bool duplicates)
@@ -164,16 +164,8 @@ namespace Google.ProtocolBuffers.ProtoGen
         /// </summary>
         /// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception>
         public static IList<FileDescriptor> ConvertDescriptors(CSharpFileOptions options,
-                                                               params FileDescriptorSet[] descriptorProtos)
+                                                               IList<FileDescriptorProto> fileList)
         {
-            // Simple strategy: Keep going through the list of protos to convert, only doing ones where
-            // we've already converted all the dependencies, until we get to a stalemate
-            List<FileDescriptorProto> fileList = new List<FileDescriptorProto>();
-            foreach (FileDescriptorSet set in descriptorProtos)
-            {
-                fileList.AddRange(set.FileList);
-            }
-
             FileDescriptor[] converted = new FileDescriptor[fileList.Count];
 
             Dictionary<string, FileDescriptor> convertedMap = new Dictionary<string, FileDescriptor>();

+ 1187 - 0
src/ProtoGen/PluginProtoFile.cs

@@ -0,0 +1,1187 @@
+// Generated by protoc-gen-cs, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48.  DO NOT EDIT!
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.ProtocolBuffers;
+using pbc = global::Google.ProtocolBuffers.Collections;
+using pbd = global::Google.ProtocolBuffers.Descriptors;
+using scg = global::System.Collections.Generic;
+namespace Google.ProtocolBuffers.Compiler.PluginProto {
+
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public static partial class Plugin {
+
+    #region Extension registration
+    public static void RegisterAllExtensions(pb::ExtensionRegistry registry) {
+    }
+    #endregion
+    #region Static variables
+    internal static pbd::MessageDescriptor internal__static_google_protobuf_compiler_CodeGeneratorRequest__Descriptor;
+    internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest.Builder> internal__static_google_protobuf_compiler_CodeGeneratorRequest__FieldAccessorTable;
+    internal static pbd::MessageDescriptor internal__static_google_protobuf_compiler_CodeGeneratorResponse__Descriptor;
+    internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Builder> internal__static_google_protobuf_compiler_CodeGeneratorResponse__FieldAccessorTable;
+    internal static pbd::MessageDescriptor internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__Descriptor;
+    internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.Builder> internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__FieldAccessorTable;
+    #endregion
+    #region Descriptor
+    public static pbd::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbd::FileDescriptor descriptor;
+
+    static Plugin() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "CiVnb29nbGUvcHJvdG9idWYvY29tcGlsZXIvcGx1Z2luLnByb3RvEhhnb29n",
+            "bGUucHJvdG9idWYuY29tcGlsZXIaIGdvb2dsZS9wcm90b2J1Zi9kZXNjcmlw",
+            "dG9yLnByb3RvIn0KFENvZGVHZW5lcmF0b3JSZXF1ZXN0EhgKEGZpbGVfdG9f",
+            "Z2VuZXJhdGUYASADKAkSEQoJcGFyYW1ldGVyGAIgASgJEjgKCnByb3RvX2Zp",
+            "bGUYDyADKAsyJC5nb29nbGUucHJvdG9idWYuRmlsZURlc2NyaXB0b3JQcm90",
+            "byKqAQoVQ29kZUdlbmVyYXRvclJlc3BvbnNlEg0KBWVycm9yGAEgASgJEkIK",
+            "BGZpbGUYDyADKAsyNC5nb29nbGUucHJvdG9idWYuY29tcGlsZXIuQ29kZUdl",
+            "bmVyYXRvclJlc3BvbnNlLkZpbGUaPgoERmlsZRIMCgRuYW1lGAEgASgJEhcK",
+            "D2luc2VydGlvbl9wb2ludBgCIAEoCRIPCgdjb250ZW50GA8gASgJQiwKHGNv",
+          "bS5nb29nbGUucHJvdG9idWYuY29tcGlsZXJCDFBsdWdpblByb3Rvcw=="));
+      pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
+        descriptor = root;
+        internal__static_google_protobuf_compiler_CodeGeneratorRequest__Descriptor = Descriptor.MessageTypes[0];
+        internal__static_google_protobuf_compiler_CodeGeneratorRequest__FieldAccessorTable =
+            new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest.Builder>(internal__static_google_protobuf_compiler_CodeGeneratorRequest__Descriptor,
+                new string[] { "FileToGenerate", "Parameter", "ProtoFile", });
+        internal__static_google_protobuf_compiler_CodeGeneratorResponse__Descriptor = Descriptor.MessageTypes[1];
+        internal__static_google_protobuf_compiler_CodeGeneratorResponse__FieldAccessorTable =
+            new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Builder>(internal__static_google_protobuf_compiler_CodeGeneratorResponse__Descriptor,
+                new string[] { "Error", "File", });
+        internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__Descriptor = internal__static_google_protobuf_compiler_CodeGeneratorResponse__Descriptor.NestedTypes[0];
+        internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__FieldAccessorTable =
+            new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.Builder>(internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__Descriptor,
+                new string[] { "Name", "InsertionPoint", "Content", });
+        return null;
+      };
+      pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+          new pbd::FileDescriptor[] {
+          global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProtoFile.Descriptor,
+          }, assigner);
+    }
+    #endregion
+
+  }
+  #region Messages
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public sealed partial class CodeGeneratorRequest : pb::GeneratedMessage<CodeGeneratorRequest, CodeGeneratorRequest.Builder> {
+    private CodeGeneratorRequest() { }
+    private static readonly CodeGeneratorRequest defaultInstance = new CodeGeneratorRequest().MakeReadOnly();
+    private static readonly string[] _codeGeneratorRequestFieldNames = new string[] { "file_to_generate", "parameter", "proto_file" };
+    private static readonly uint[] _codeGeneratorRequestFieldTags = new uint[] { 10, 18, 122 };
+    public static CodeGeneratorRequest DefaultInstance {
+      get { return defaultInstance; }
+    }
+
+    public override CodeGeneratorRequest DefaultInstanceForType {
+      get { return DefaultInstance; }
+    }
+
+    protected override CodeGeneratorRequest ThisMessage {
+      get { return this; }
+    }
+
+    public static pbd::MessageDescriptor Descriptor {
+      get { return global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.internal__static_google_protobuf_compiler_CodeGeneratorRequest__Descriptor; }
+    }
+
+    protected override pb::FieldAccess.FieldAccessorTable<CodeGeneratorRequest, CodeGeneratorRequest.Builder> InternalFieldAccessors {
+      get { return global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.internal__static_google_protobuf_compiler_CodeGeneratorRequest__FieldAccessorTable; }
+    }
+
+    public const int FileToGenerateFieldNumber = 1;
+    private pbc::PopsicleList<string> fileToGenerate_ = new pbc::PopsicleList<string>();
+    public scg::IList<string> FileToGenerateList {
+      get { return pbc::Lists.AsReadOnly(fileToGenerate_); }
+    }
+    public int FileToGenerateCount {
+      get { return fileToGenerate_.Count; }
+    }
+    public string GetFileToGenerate(int index) {
+      return fileToGenerate_[index];
+    }
+
+    public const int ParameterFieldNumber = 2;
+    private bool hasParameter;
+    private string parameter_ = "";
+    public bool HasParameter {
+      get { return hasParameter; }
+    }
+    public string Parameter {
+      get { return parameter_; }
+    }
+
+    public const int ProtoFileFieldNumber = 15;
+    private pbc::PopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto> protoFile_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto>();
+    public scg::IList<global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto> ProtoFileList {
+      get { return protoFile_; }
+    }
+    public int ProtoFileCount {
+      get { return protoFile_.Count; }
+    }
+    public global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto GetProtoFile(int index) {
+      return protoFile_[index];
+    }
+
+    public override bool IsInitialized {
+      get {
+        foreach (global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto element in ProtoFileList) {
+          if (!element.IsInitialized) return false;
+        }
+        return true;
+      }
+    }
+
+    public override void WriteTo(pb::ICodedOutputStream output) {
+      int size = SerializedSize;
+      string[] field_names = _codeGeneratorRequestFieldNames;
+      if (fileToGenerate_.Count > 0) {
+        output.WriteStringArray(1, field_names[0], fileToGenerate_);
+      }
+      if (hasParameter) {
+        output.WriteString(2, field_names[1], Parameter);
+      }
+      if (protoFile_.Count > 0) {
+        output.WriteMessageArray(15, field_names[2], protoFile_);
+      }
+      UnknownFields.WriteTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public override int SerializedSize {
+      get {
+        int size = memoizedSerializedSize;
+        if (size != -1) return size;
+
+        size = 0;
+        {
+          int dataSize = 0;
+          foreach (string element in FileToGenerateList) {
+            dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element);
+          }
+          size += dataSize;
+          size += 1 * fileToGenerate_.Count;
+        }
+        if (hasParameter) {
+          size += pb::CodedOutputStream.ComputeStringSize(2, Parameter);
+        }
+        foreach (global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto element in ProtoFileList) {
+          size += pb::CodedOutputStream.ComputeMessageSize(15, element);
+        }
+        size += UnknownFields.SerializedSize;
+        memoizedSerializedSize = size;
+        return size;
+      }
+    }
+
+    public static CodeGeneratorRequest ParseFrom(pb::ByteString data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(byte[] data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(global::System.IO.Stream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseDelimitedFrom(global::System.IO.Stream input) {
+      return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(pb::ICodedInputStream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static CodeGeneratorRequest ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    private CodeGeneratorRequest MakeReadOnly() {
+      fileToGenerate_.MakeReadOnly();
+      protoFile_.MakeReadOnly();
+      return this;
+    }
+
+    public static Builder CreateBuilder() { return new Builder(); }
+    public override Builder ToBuilder() { return CreateBuilder(this); }
+    public override Builder CreateBuilderForType() { return new Builder(); }
+    public static Builder CreateBuilder(CodeGeneratorRequest prototype) {
+      return new Builder(prototype);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    public sealed partial class Builder : pb::GeneratedBuilder<CodeGeneratorRequest, Builder> {
+      protected override Builder ThisBuilder {
+        get { return this; }
+      }
+      public Builder() {
+        result = DefaultInstance;
+        resultIsReadOnly = true;
+      }
+      internal Builder(CodeGeneratorRequest cloneFrom) {
+        result = cloneFrom;
+        resultIsReadOnly = true;
+      }
+
+      private bool resultIsReadOnly;
+      private CodeGeneratorRequest result;
+
+      private CodeGeneratorRequest PrepareBuilder() {
+        if (resultIsReadOnly) {
+          CodeGeneratorRequest original = result;
+          result = new CodeGeneratorRequest();
+          resultIsReadOnly = false;
+          MergeFrom(original);
+        }
+        return result;
+      }
+
+      public override bool IsInitialized {
+        get { return result.IsInitialized; }
+      }
+
+      protected override CodeGeneratorRequest MessageBeingBuilt {
+        get { return PrepareBuilder(); }
+      }
+
+      public override Builder Clear() {
+        result = DefaultInstance;
+        resultIsReadOnly = true;
+        return this;
+      }
+
+      public override Builder Clone() {
+        if (resultIsReadOnly) {
+          return new Builder(result);
+        } else {
+          return new Builder().MergeFrom(result);
+        }
+      }
+
+      public override pbd::MessageDescriptor DescriptorForType {
+        get { return global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest.Descriptor; }
+      }
+
+      public override CodeGeneratorRequest DefaultInstanceForType {
+        get { return global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest.DefaultInstance; }
+      }
+
+      public override CodeGeneratorRequest BuildPartial() {
+        if (resultIsReadOnly) {
+          return result;
+        }
+        resultIsReadOnly = true;
+        return result.MakeReadOnly();
+      }
+
+      public override Builder MergeFrom(pb::IMessage other) {
+        if (other is CodeGeneratorRequest) {
+          return MergeFrom((CodeGeneratorRequest) other);
+        } else {
+          base.MergeFrom(other);
+          return this;
+        }
+      }
+
+      public override Builder MergeFrom(CodeGeneratorRequest other) {
+        if (other == global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorRequest.DefaultInstance) return this;
+        PrepareBuilder();
+        if (other.fileToGenerate_.Count != 0) {
+          result.fileToGenerate_.Add(other.fileToGenerate_);
+        }
+        if (other.HasParameter) {
+          Parameter = other.Parameter;
+        }
+        if (other.protoFile_.Count != 0) {
+          result.protoFile_.Add(other.protoFile_);
+        }
+        this.MergeUnknownFields(other.UnknownFields);
+        return this;
+      }
+
+      public override Builder MergeFrom(pb::ICodedInputStream input) {
+        return MergeFrom(input, pb::ExtensionRegistry.Empty);
+      }
+
+      public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+        PrepareBuilder();
+        pb::UnknownFieldSet.Builder unknownFields = null;
+        uint tag;
+        string field_name;
+        while (input.ReadTag(out tag, out field_name)) {
+          if(tag == 0 && field_name != null) {
+            int field_ordinal = global::System.Array.BinarySearch(_codeGeneratorRequestFieldNames, field_name, global::System.StringComparer.Ordinal);
+            if(field_ordinal >= 0)
+              tag = _codeGeneratorRequestFieldTags[field_ordinal];
+            else {
+              if (unknownFields == null) {
+                unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+              }
+              ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+              continue;
+            }
+          }
+          switch (tag) {
+            case 0: {
+              throw pb::InvalidProtocolBufferException.InvalidTag();
+            }
+            default: {
+              if (pb::WireFormat.IsEndGroupTag(tag)) {
+                if (unknownFields != null) {
+                  this.UnknownFields = unknownFields.Build();
+                }
+                return this;
+              }
+              if (unknownFields == null) {
+                unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+              }
+              ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+              break;
+            }
+            case 10: {
+              input.ReadStringArray(tag, field_name, result.fileToGenerate_);
+              break;
+            }
+            case 18: {
+              result.hasParameter = input.ReadString(ref result.parameter_);
+              break;
+            }
+            case 122: {
+              input.ReadMessageArray(tag, field_name, result.protoFile_, global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto.DefaultInstance, extensionRegistry);
+              break;
+            }
+          }
+        }
+
+        if (unknownFields != null) {
+          this.UnknownFields = unknownFields.Build();
+        }
+        return this;
+      }
+
+
+      public pbc::IPopsicleList<string> FileToGenerateList {
+        get { return PrepareBuilder().fileToGenerate_; }
+      }
+      public int FileToGenerateCount {
+        get { return result.FileToGenerateCount; }
+      }
+      public string GetFileToGenerate(int index) {
+        return result.GetFileToGenerate(index);
+      }
+      public Builder SetFileToGenerate(int index, string value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.fileToGenerate_[index] = value;
+        return this;
+      }
+      public Builder AddFileToGenerate(string value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.fileToGenerate_.Add(value);
+        return this;
+      }
+      public Builder AddRangeFileToGenerate(scg::IEnumerable<string> values) {
+        PrepareBuilder();
+        result.fileToGenerate_.Add(values);
+        return this;
+      }
+      public Builder ClearFileToGenerate() {
+        PrepareBuilder();
+        result.fileToGenerate_.Clear();
+        return this;
+      }
+
+      public bool HasParameter {
+        get { return result.hasParameter; }
+      }
+      public string Parameter {
+        get { return result.Parameter; }
+        set { SetParameter(value); }
+      }
+      public Builder SetParameter(string value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.hasParameter = true;
+        result.parameter_ = value;
+        return this;
+      }
+      public Builder ClearParameter() {
+        PrepareBuilder();
+        result.hasParameter = false;
+        result.parameter_ = "";
+        return this;
+      }
+
+      public pbc::IPopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto> ProtoFileList {
+        get { return PrepareBuilder().protoFile_; }
+      }
+      public int ProtoFileCount {
+        get { return result.ProtoFileCount; }
+      }
+      public global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto GetProtoFile(int index) {
+        return result.GetProtoFile(index);
+      }
+      public Builder SetProtoFile(int index, global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.protoFile_[index] = value;
+        return this;
+      }
+      public Builder SetProtoFile(int index, global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto.Builder builderForValue) {
+        pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
+        PrepareBuilder();
+        result.protoFile_[index] = builderForValue.Build();
+        return this;
+      }
+      public Builder AddProtoFile(global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.protoFile_.Add(value);
+        return this;
+      }
+      public Builder AddProtoFile(global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto.Builder builderForValue) {
+        pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
+        PrepareBuilder();
+        result.protoFile_.Add(builderForValue.Build());
+        return this;
+      }
+      public Builder AddRangeProtoFile(scg::IEnumerable<global::Google.ProtocolBuffers.DescriptorProtos.FileDescriptorProto> values) {
+        PrepareBuilder();
+        result.protoFile_.Add(values);
+        return this;
+      }
+      public Builder ClearProtoFile() {
+        PrepareBuilder();
+        result.protoFile_.Clear();
+        return this;
+      }
+    }
+    static CodeGeneratorRequest() {
+      object.ReferenceEquals(global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.Descriptor, null);
+    }
+  }
+
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public sealed partial class CodeGeneratorResponse : pb::GeneratedMessage<CodeGeneratorResponse, CodeGeneratorResponse.Builder> {
+    private CodeGeneratorResponse() { }
+    private static readonly CodeGeneratorResponse defaultInstance = new CodeGeneratorResponse().MakeReadOnly();
+    private static readonly string[] _codeGeneratorResponseFieldNames = new string[] { "error", "file" };
+    private static readonly uint[] _codeGeneratorResponseFieldTags = new uint[] { 10, 122 };
+    public static CodeGeneratorResponse DefaultInstance {
+      get { return defaultInstance; }
+    }
+
+    public override CodeGeneratorResponse DefaultInstanceForType {
+      get { return DefaultInstance; }
+    }
+
+    protected override CodeGeneratorResponse ThisMessage {
+      get { return this; }
+    }
+
+    public static pbd::MessageDescriptor Descriptor {
+      get { return global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.internal__static_google_protobuf_compiler_CodeGeneratorResponse__Descriptor; }
+    }
+
+    protected override pb::FieldAccess.FieldAccessorTable<CodeGeneratorResponse, CodeGeneratorResponse.Builder> InternalFieldAccessors {
+      get { return global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.internal__static_google_protobuf_compiler_CodeGeneratorResponse__FieldAccessorTable; }
+    }
+
+    #region Nested types
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    public static partial class Types {
+      [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+      public sealed partial class File : pb::GeneratedMessage<File, File.Builder> {
+        private File() { }
+        private static readonly File defaultInstance = new File().MakeReadOnly();
+        private static readonly string[] _fileFieldNames = new string[] { "content", "insertion_point", "name" };
+        private static readonly uint[] _fileFieldTags = new uint[] { 122, 18, 10 };
+        public static File DefaultInstance {
+          get { return defaultInstance; }
+        }
+
+        public override File DefaultInstanceForType {
+          get { return DefaultInstance; }
+        }
+
+        protected override File ThisMessage {
+          get { return this; }
+        }
+
+        public static pbd::MessageDescriptor Descriptor {
+          get { return global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__Descriptor; }
+        }
+
+        protected override pb::FieldAccess.FieldAccessorTable<File, File.Builder> InternalFieldAccessors {
+          get { return global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.internal__static_google_protobuf_compiler_CodeGeneratorResponse_File__FieldAccessorTable; }
+        }
+
+        public const int NameFieldNumber = 1;
+        private bool hasName;
+        private string name_ = "";
+        public bool HasName {
+          get { return hasName; }
+        }
+        public string Name {
+          get { return name_; }
+        }
+
+        public const int InsertionPointFieldNumber = 2;
+        private bool hasInsertionPoint;
+        private string insertionPoint_ = "";
+        public bool HasInsertionPoint {
+          get { return hasInsertionPoint; }
+        }
+        public string InsertionPoint {
+          get { return insertionPoint_; }
+        }
+
+        public const int ContentFieldNumber = 15;
+        private bool hasContent;
+        private string content_ = "";
+        public bool HasContent {
+          get { return hasContent; }
+        }
+        public string Content {
+          get { return content_; }
+        }
+
+        public override bool IsInitialized {
+          get {
+            return true;
+          }
+        }
+
+        public override void WriteTo(pb::ICodedOutputStream output) {
+          int size = SerializedSize;
+          string[] field_names = _fileFieldNames;
+          if (hasName) {
+            output.WriteString(1, field_names[2], Name);
+          }
+          if (hasInsertionPoint) {
+            output.WriteString(2, field_names[1], InsertionPoint);
+          }
+          if (hasContent) {
+            output.WriteString(15, field_names[0], Content);
+          }
+          UnknownFields.WriteTo(output);
+        }
+
+        private int memoizedSerializedSize = -1;
+        public override int SerializedSize {
+          get {
+            int size = memoizedSerializedSize;
+            if (size != -1) return size;
+
+            size = 0;
+            if (hasName) {
+              size += pb::CodedOutputStream.ComputeStringSize(1, Name);
+            }
+            if (hasInsertionPoint) {
+              size += pb::CodedOutputStream.ComputeStringSize(2, InsertionPoint);
+            }
+            if (hasContent) {
+              size += pb::CodedOutputStream.ComputeStringSize(15, Content);
+            }
+            size += UnknownFields.SerializedSize;
+            memoizedSerializedSize = size;
+            return size;
+          }
+        }
+
+        public static File ParseFrom(pb::ByteString data) {
+          return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+        }
+        public static File ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+        }
+        public static File ParseFrom(byte[] data) {
+          return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+        }
+        public static File ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+        }
+        public static File ParseFrom(global::System.IO.Stream input) {
+          return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+        }
+        public static File ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+        }
+        public static File ParseDelimitedFrom(global::System.IO.Stream input) {
+          return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+        }
+        public static File ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+          return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+        }
+        public static File ParseFrom(pb::ICodedInputStream input) {
+          return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+        }
+        public static File ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+        }
+        private File MakeReadOnly() {
+          return this;
+        }
+
+        public static Builder CreateBuilder() { return new Builder(); }
+        public override Builder ToBuilder() { return CreateBuilder(this); }
+        public override Builder CreateBuilderForType() { return new Builder(); }
+        public static Builder CreateBuilder(File prototype) {
+          return new Builder(prototype);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        public sealed partial class Builder : pb::GeneratedBuilder<File, Builder> {
+          protected override Builder ThisBuilder {
+            get { return this; }
+          }
+          public Builder() {
+            result = DefaultInstance;
+            resultIsReadOnly = true;
+          }
+          internal Builder(File cloneFrom) {
+            result = cloneFrom;
+            resultIsReadOnly = true;
+          }
+
+          private bool resultIsReadOnly;
+          private File result;
+
+          private File PrepareBuilder() {
+            if (resultIsReadOnly) {
+              File original = result;
+              result = new File();
+              resultIsReadOnly = false;
+              MergeFrom(original);
+            }
+            return result;
+          }
+
+          public override bool IsInitialized {
+            get { return result.IsInitialized; }
+          }
+
+          protected override File MessageBeingBuilt {
+            get { return PrepareBuilder(); }
+          }
+
+          public override Builder Clear() {
+            result = DefaultInstance;
+            resultIsReadOnly = true;
+            return this;
+          }
+
+          public override Builder Clone() {
+            if (resultIsReadOnly) {
+              return new Builder(result);
+            } else {
+              return new Builder().MergeFrom(result);
+            }
+          }
+
+          public override pbd::MessageDescriptor DescriptorForType {
+            get { return global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.Descriptor; }
+          }
+
+          public override File DefaultInstanceForType {
+            get { return global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.DefaultInstance; }
+          }
+
+          public override File BuildPartial() {
+            if (resultIsReadOnly) {
+              return result;
+            }
+            resultIsReadOnly = true;
+            return result.MakeReadOnly();
+          }
+
+          public override Builder MergeFrom(pb::IMessage other) {
+            if (other is File) {
+              return MergeFrom((File) other);
+            } else {
+              base.MergeFrom(other);
+              return this;
+            }
+          }
+
+          public override Builder MergeFrom(File other) {
+            if (other == global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.DefaultInstance) return this;
+            PrepareBuilder();
+            if (other.HasName) {
+              Name = other.Name;
+            }
+            if (other.HasInsertionPoint) {
+              InsertionPoint = other.InsertionPoint;
+            }
+            if (other.HasContent) {
+              Content = other.Content;
+            }
+            this.MergeUnknownFields(other.UnknownFields);
+            return this;
+          }
+
+          public override Builder MergeFrom(pb::ICodedInputStream input) {
+            return MergeFrom(input, pb::ExtensionRegistry.Empty);
+          }
+
+          public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+            PrepareBuilder();
+            pb::UnknownFieldSet.Builder unknownFields = null;
+            uint tag;
+            string field_name;
+            while (input.ReadTag(out tag, out field_name)) {
+              if(tag == 0 && field_name != null) {
+                int field_ordinal = global::System.Array.BinarySearch(_fileFieldNames, field_name, global::System.StringComparer.Ordinal);
+                if(field_ordinal >= 0)
+                  tag = _fileFieldTags[field_ordinal];
+                else {
+                  if (unknownFields == null) {
+                    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                  }
+                  ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+                  continue;
+                }
+              }
+              switch (tag) {
+                case 0: {
+                  throw pb::InvalidProtocolBufferException.InvalidTag();
+                }
+                default: {
+                  if (pb::WireFormat.IsEndGroupTag(tag)) {
+                    if (unknownFields != null) {
+                      this.UnknownFields = unknownFields.Build();
+                    }
+                    return this;
+                  }
+                  if (unknownFields == null) {
+                    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                  }
+                  ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+                  break;
+                }
+                case 10: {
+                  result.hasName = input.ReadString(ref result.name_);
+                  break;
+                }
+                case 18: {
+                  result.hasInsertionPoint = input.ReadString(ref result.insertionPoint_);
+                  break;
+                }
+                case 122: {
+                  result.hasContent = input.ReadString(ref result.content_);
+                  break;
+                }
+              }
+            }
+
+            if (unknownFields != null) {
+              this.UnknownFields = unknownFields.Build();
+            }
+            return this;
+          }
+
+
+          public bool HasName {
+            get { return result.hasName; }
+          }
+          public string Name {
+            get { return result.Name; }
+            set { SetName(value); }
+          }
+          public Builder SetName(string value) {
+            pb::ThrowHelper.ThrowIfNull(value, "value");
+            PrepareBuilder();
+            result.hasName = true;
+            result.name_ = value;
+            return this;
+          }
+          public Builder ClearName() {
+            PrepareBuilder();
+            result.hasName = false;
+            result.name_ = "";
+            return this;
+          }
+
+          public bool HasInsertionPoint {
+            get { return result.hasInsertionPoint; }
+          }
+          public string InsertionPoint {
+            get { return result.InsertionPoint; }
+            set { SetInsertionPoint(value); }
+          }
+          public Builder SetInsertionPoint(string value) {
+            pb::ThrowHelper.ThrowIfNull(value, "value");
+            PrepareBuilder();
+            result.hasInsertionPoint = true;
+            result.insertionPoint_ = value;
+            return this;
+          }
+          public Builder ClearInsertionPoint() {
+            PrepareBuilder();
+            result.hasInsertionPoint = false;
+            result.insertionPoint_ = "";
+            return this;
+          }
+
+          public bool HasContent {
+            get { return result.hasContent; }
+          }
+          public string Content {
+            get { return result.Content; }
+            set { SetContent(value); }
+          }
+          public Builder SetContent(string value) {
+            pb::ThrowHelper.ThrowIfNull(value, "value");
+            PrepareBuilder();
+            result.hasContent = true;
+            result.content_ = value;
+            return this;
+          }
+          public Builder ClearContent() {
+            PrepareBuilder();
+            result.hasContent = false;
+            result.content_ = "";
+            return this;
+          }
+        }
+        static File() {
+          object.ReferenceEquals(global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.Descriptor, null);
+        }
+      }
+
+    }
+    #endregion
+
+    public const int ErrorFieldNumber = 1;
+    private bool hasError;
+    private string error_ = "";
+    public bool HasError {
+      get { return hasError; }
+    }
+    public string Error {
+      get { return error_; }
+    }
+
+    public const int FileFieldNumber = 15;
+    private pbc::PopsicleList<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File> file_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File>();
+    public scg::IList<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File> FileList {
+      get { return file_; }
+    }
+    public int FileCount {
+      get { return file_.Count; }
+    }
+    public global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File GetFile(int index) {
+      return file_[index];
+    }
+
+    public override bool IsInitialized {
+      get {
+        return true;
+      }
+    }
+
+    public override void WriteTo(pb::ICodedOutputStream output) {
+      int size = SerializedSize;
+      string[] field_names = _codeGeneratorResponseFieldNames;
+      if (hasError) {
+        output.WriteString(1, field_names[0], Error);
+      }
+      if (file_.Count > 0) {
+        output.WriteMessageArray(15, field_names[1], file_);
+      }
+      UnknownFields.WriteTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public override int SerializedSize {
+      get {
+        int size = memoizedSerializedSize;
+        if (size != -1) return size;
+
+        size = 0;
+        if (hasError) {
+          size += pb::CodedOutputStream.ComputeStringSize(1, Error);
+        }
+        foreach (global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File element in FileList) {
+          size += pb::CodedOutputStream.ComputeMessageSize(15, element);
+        }
+        size += UnknownFields.SerializedSize;
+        memoizedSerializedSize = size;
+        return size;
+      }
+    }
+
+    public static CodeGeneratorResponse ParseFrom(pb::ByteString data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(byte[] data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(global::System.IO.Stream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseDelimitedFrom(global::System.IO.Stream input) {
+      return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(pb::ICodedInputStream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static CodeGeneratorResponse ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    private CodeGeneratorResponse MakeReadOnly() {
+      file_.MakeReadOnly();
+      return this;
+    }
+
+    public static Builder CreateBuilder() { return new Builder(); }
+    public override Builder ToBuilder() { return CreateBuilder(this); }
+    public override Builder CreateBuilderForType() { return new Builder(); }
+    public static Builder CreateBuilder(CodeGeneratorResponse prototype) {
+      return new Builder(prototype);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    public sealed partial class Builder : pb::GeneratedBuilder<CodeGeneratorResponse, Builder> {
+      protected override Builder ThisBuilder {
+        get { return this; }
+      }
+      public Builder() {
+        result = DefaultInstance;
+        resultIsReadOnly = true;
+      }
+      internal Builder(CodeGeneratorResponse cloneFrom) {
+        result = cloneFrom;
+        resultIsReadOnly = true;
+      }
+
+      private bool resultIsReadOnly;
+      private CodeGeneratorResponse result;
+
+      private CodeGeneratorResponse PrepareBuilder() {
+        if (resultIsReadOnly) {
+          CodeGeneratorResponse original = result;
+          result = new CodeGeneratorResponse();
+          resultIsReadOnly = false;
+          MergeFrom(original);
+        }
+        return result;
+      }
+
+      public override bool IsInitialized {
+        get { return result.IsInitialized; }
+      }
+
+      protected override CodeGeneratorResponse MessageBeingBuilt {
+        get { return PrepareBuilder(); }
+      }
+
+      public override Builder Clear() {
+        result = DefaultInstance;
+        resultIsReadOnly = true;
+        return this;
+      }
+
+      public override Builder Clone() {
+        if (resultIsReadOnly) {
+          return new Builder(result);
+        } else {
+          return new Builder().MergeFrom(result);
+        }
+      }
+
+      public override pbd::MessageDescriptor DescriptorForType {
+        get { return global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Descriptor; }
+      }
+
+      public override CodeGeneratorResponse DefaultInstanceForType {
+        get { return global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.DefaultInstance; }
+      }
+
+      public override CodeGeneratorResponse BuildPartial() {
+        if (resultIsReadOnly) {
+          return result;
+        }
+        resultIsReadOnly = true;
+        return result.MakeReadOnly();
+      }
+
+      public override Builder MergeFrom(pb::IMessage other) {
+        if (other is CodeGeneratorResponse) {
+          return MergeFrom((CodeGeneratorResponse) other);
+        } else {
+          base.MergeFrom(other);
+          return this;
+        }
+      }
+
+      public override Builder MergeFrom(CodeGeneratorResponse other) {
+        if (other == global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.DefaultInstance) return this;
+        PrepareBuilder();
+        if (other.HasError) {
+          Error = other.Error;
+        }
+        if (other.file_.Count != 0) {
+          result.file_.Add(other.file_);
+        }
+        this.MergeUnknownFields(other.UnknownFields);
+        return this;
+      }
+
+      public override Builder MergeFrom(pb::ICodedInputStream input) {
+        return MergeFrom(input, pb::ExtensionRegistry.Empty);
+      }
+
+      public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+        PrepareBuilder();
+        pb::UnknownFieldSet.Builder unknownFields = null;
+        uint tag;
+        string field_name;
+        while (input.ReadTag(out tag, out field_name)) {
+          if(tag == 0 && field_name != null) {
+            int field_ordinal = global::System.Array.BinarySearch(_codeGeneratorResponseFieldNames, field_name, global::System.StringComparer.Ordinal);
+            if(field_ordinal >= 0)
+              tag = _codeGeneratorResponseFieldTags[field_ordinal];
+            else {
+              if (unknownFields == null) {
+                unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+              }
+              ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+              continue;
+            }
+          }
+          switch (tag) {
+            case 0: {
+              throw pb::InvalidProtocolBufferException.InvalidTag();
+            }
+            default: {
+              if (pb::WireFormat.IsEndGroupTag(tag)) {
+                if (unknownFields != null) {
+                  this.UnknownFields = unknownFields.Build();
+                }
+                return this;
+              }
+              if (unknownFields == null) {
+                unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+              }
+              ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+              break;
+            }
+            case 10: {
+              result.hasError = input.ReadString(ref result.error_);
+              break;
+            }
+            case 122: {
+              input.ReadMessageArray(tag, field_name, result.file_, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.DefaultInstance, extensionRegistry);
+              break;
+            }
+          }
+        }
+
+        if (unknownFields != null) {
+          this.UnknownFields = unknownFields.Build();
+        }
+        return this;
+      }
+
+
+      public bool HasError {
+        get { return result.hasError; }
+      }
+      public string Error {
+        get { return result.Error; }
+        set { SetError(value); }
+      }
+      public Builder SetError(string value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.hasError = true;
+        result.error_ = value;
+        return this;
+      }
+      public Builder ClearError() {
+        PrepareBuilder();
+        result.hasError = false;
+        result.error_ = "";
+        return this;
+      }
+
+      public pbc::IPopsicleList<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File> FileList {
+        get { return PrepareBuilder().file_; }
+      }
+      public int FileCount {
+        get { return result.FileCount; }
+      }
+      public global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File GetFile(int index) {
+        return result.GetFile(index);
+      }
+      public Builder SetFile(int index, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.file_[index] = value;
+        return this;
+      }
+      public Builder SetFile(int index, global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.Builder builderForValue) {
+        pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
+        PrepareBuilder();
+        result.file_[index] = builderForValue.Build();
+        return this;
+      }
+      public Builder AddFile(global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File value) {
+        pb::ThrowHelper.ThrowIfNull(value, "value");
+        PrepareBuilder();
+        result.file_.Add(value);
+        return this;
+      }
+      public Builder AddFile(global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File.Builder builderForValue) {
+        pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
+        PrepareBuilder();
+        result.file_.Add(builderForValue.Build());
+        return this;
+      }
+      public Builder AddRangeFile(scg::IEnumerable<global::Google.ProtocolBuffers.Compiler.PluginProto.CodeGeneratorResponse.Types.File> values) {
+        PrepareBuilder();
+        result.file_.Add(values);
+        return this;
+      }
+      public Builder ClearFile() {
+        PrepareBuilder();
+        result.file_.Clear();
+        return this;
+      }
+    }
+    static CodeGeneratorResponse() {
+      object.ReferenceEquals(global::Google.ProtocolBuffers.Compiler.PluginProto.Plugin.Descriptor, null);
+    }
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code

+ 28 - 1
src/ProtoGen/Program.cs

@@ -35,7 +35,9 @@
 #endregion
 
 using System;
+using System.IO;
 using System.Collections.Generic;
+using Google.ProtocolBuffers.Compiler.PluginProto;
 using Google.ProtocolBuffers.DescriptorProtos;
 
 namespace Google.ProtocolBuffers.ProtoGen
@@ -62,8 +64,33 @@ namespace Google.ProtocolBuffers.ProtoGen
                     return 1;
                 }
 
+                var request = new CodeGeneratorRequest.Builder();
+                foreach (string inputFile in options.InputFiles)
+                {
+                    ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
+                    CSharpOptions.RegisterAllExtensions(extensionRegistry);
+                    using (Stream inputStream = File.OpenRead(inputFile))
+                    {
+                        var fileSet = FileDescriptorSet.ParseFrom(inputStream, extensionRegistry);
+                        foreach (var fileProto in fileSet.FileList)
+                        {
+                            request.AddFileToGenerate(fileProto.Name);
+                            request.AddProtoFile(fileProto);
+                        }
+                    }
+                }
+
                 Generator generator = Generator.CreateGenerator(options);
-                generator.Generate();
+                var response = new CodeGeneratorResponse.Builder();
+                generator.Generate(request.Build(), response);
+                if (response.HasError)
+                {
+                    throw new Exception(response.Error);
+                }
+                foreach (var file in response.FileList)
+                {
+                    File.WriteAllText(file.Name, file.Content);
+                }
                 return 0;
             }
             catch (Exception e)

+ 1 - 0
src/ProtoGen/ProtoGen.csproj

@@ -59,6 +59,7 @@
     <Compile Include="ISourceGenerator.cs" />
     <Compile Include="MessageFieldGenerator.cs" />
     <Compile Include="MessageGenerator.cs" />
+    <Compile Include="PluginProtoFile.cs" />
     <Compile Include="PrimitiveFieldGenerator.cs" />
     <Compile Include="ProgramPreprocess.cs" />
     <Compile Include="RepeatedEnumFieldGenerator.cs" />

+ 76 - 0
src/ProtoGen/ProtocGenCs.cs

@@ -0,0 +1,76 @@
+using Google.ProtocolBuffers.Compiler.PluginProto;
+using Google.ProtocolBuffers.DescriptorProtos;
+using System;
+using System.Collections.Generic;
+
+// Usage example:
+//   protoc.exe
+//     --plugin=path\to\protoc-gen-cs.exe
+//     --cs_out="-generated_code_attributes=true umbrella_namespace=TutorialProto :."
+//     --proto_path=.\protos\
+//     protos\tutorial\addressbook.proto
+
+namespace Google.ProtocolBuffers.ProtoGen
+{
+    public static class ProtocGenCs
+    {
+        internal static void Run(CodeGeneratorRequest request, CodeGeneratorResponse.Builder response)
+        {
+            var arguments = new List<string>();
+            foreach (var arg in request.Parameter.Split(' '))
+            {
+                var timmedArg = (arg ?? "").Trim();
+                if (!string.IsNullOrEmpty(timmedArg))
+                {
+                    arguments.Add(timmedArg);
+                }
+            }
+            // Adding fake input file to make TryValidate happy.
+            arguments.Add(System.Reflection.Assembly.GetExecutingAssembly().Location);
+
+            GeneratorOptions options = new GeneratorOptions
+            {
+                Arguments = arguments
+            };
+            IList<string> validationFailures;
+            if (!options.TryValidate(out validationFailures))
+            {
+                response.Error += new InvalidOptionsException(validationFailures).Message;
+                return;
+            }
+
+            Generator generator = Generator.CreateGenerator(options);
+            generator.Generate(request, response);
+        }
+
+        public static int Main(string[] args)
+        {
+            // Hack to make sure everything's initialized
+            DescriptorProtoFile.Descriptor.ToString();
+            ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
+            CSharpOptions.RegisterAllExtensions(extensionRegistry);
+
+            CodeGeneratorRequest request;
+            var response = new CodeGeneratorResponse.Builder();
+            try
+            {
+                using (var input = Console.OpenStandardInput())
+                {
+                    request = CodeGeneratorRequest.ParseFrom(input, extensionRegistry);
+                }
+                Run(request, response);
+            }
+            catch (Exception e)
+            {
+                response.Error += e.ToString();
+            }
+
+            using (var output = Console.OpenStandardOutput())
+            {
+                response.Build().WriteTo(output);
+                output.Flush();
+            }
+            return 0;
+        }
+    }
+}

+ 101 - 0
src/ProtoGen/protoc-gen-cs.csproj

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <EnvironmentFlavor>CLIENTPROFILE</EnvironmentFlavor>
+    <EnvironmentTemplate>NET35</EnvironmentTemplate>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{250ADE34-82FD-4BAE-86D5-985FBE589C4B}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Google.ProtocolBuffers.ProtoGen</RootNamespace>
+    <AssemblyName>protoc-gen-cs</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SignAssembly>true</SignAssembly>
+    <AssemblyOriginatorKeyFile>..\..\keys\Google.ProtocolBuffers.snk</AssemblyOriginatorKeyFile>
+    <StartupObject>Google.ProtocolBuffers.ProtoGen.ProtocGenCs</StartupObject>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\NET35\Debug</OutputPath>
+    <IntermediateOutputPath>obj\NET35\Debug\</IntermediateOutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <NoStdLib>true</NoStdLib>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\NET35\Release</OutputPath>
+    <IntermediateOutputPath>obj\NET35\Release\</IntermediateOutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <NoStdLib>true</NoStdLib>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="mscorlib" />
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="DescriptorUtil.cs" />
+    <Compile Include="EnumFieldGenerator.cs" />
+    <Compile Include="EnumGenerator.cs" />
+    <Compile Include="ExtensionGenerator.cs" />
+    <Compile Include="FieldGeneratorBase.cs" />
+    <Compile Include="IFieldSourceGenerator.cs" />
+    <Compile Include="ISourceGenerator.cs" />
+    <Compile Include="MessageFieldGenerator.cs" />
+    <Compile Include="MessageGenerator.cs" />
+    <Compile Include="PluginProtoFile.cs" />
+    <Compile Include="PrimitiveFieldGenerator.cs" />
+    <Compile Include="ProtocGenCs.cs" />
+    <Compile Include="RepeatedEnumFieldGenerator.cs" />
+    <Compile Include="RepeatedMessageFieldGenerator.cs" />
+    <Compile Include="RepeatedPrimitiveFieldGenerator.cs" />
+    <Compile Include="ServiceGenerator.cs" />
+    <Compile Include="DependencyResolutionException.cs" />
+    <Compile Include="Generator.cs" />
+    <Compile Include="GeneratorOptions.cs" />
+    <Compile Include="Helpers.cs" />
+    <Compile Include="InvalidOptionsException.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ServiceInterfaceGenerator.cs" />
+    <Compile Include="SourceGeneratorBase.cs" />
+    <Compile Include="SourceGenerators.cs" />
+    <Compile Include="UmbrellaClassGenerator.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\ProtocolBuffers.Serialization\ProtocolBuffers.Serialization.csproj">
+      <Project>{231391af-449c-4a39-986c-ad7f270f4750}</Project>
+      <Name>ProtocolBuffers.Serialization</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
+      <Project>{6908BDCE-D925-43F3-94AC-A531E6DF2591}</Project>
+      <Name>ProtocolBuffers</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 22 - 0
src/ProtocolBuffers.sln

@@ -5,6 +5,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "proto", "proto", "{1F896D5C
 		..\protos\tutorial\addressbook.proto = ..\protos\tutorial\addressbook.proto
 		..\protos\google\protobuf\csharp_options.proto = ..\protos\google\protobuf\csharp_options.proto
 		..\protos\google\protobuf\descriptor.proto = ..\protos\google\protobuf\descriptor.proto
+		..\protos\google\protobuf\compiler\plugin.proto = ..\protos\google\protobuf\compiler\plugin.proto
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unittest", "unittest", "{C8D3015A-EA39-4F03-AEEC-3FF1F2087A12}"
@@ -88,6 +89,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks",
 		..\protos\benchmarks\google_speed.proto = ..\protos\benchmarks\google_speed.proto
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "protoc-gen-cs", "ProtoGen\protoc-gen-cs.csproj", "{250ADE34-82FD-4BAE-86D5-985FBE589C4B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "protoc-gen-cs.Test", "ProtoGen.Test\protoc-gen-cs.Test.csproj", "{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug_Silverlight|Any CPU = Debug_Silverlight|Any CPU
@@ -174,6 +179,22 @@ Global
 		{E067A59D-9D0A-4A1F-92B1-38E4457241D1}.Release_Silverlight|Any CPU.ActiveCfg = Release|Any CPU
 		{E067A59D-9D0A-4A1F-92B1-38E4457241D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E067A59D-9D0A-4A1F-92B1-38E4457241D1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Debug_Silverlight|Any CPU.ActiveCfg = Debug|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Debug_Silverlight|Any CPU.Build.0 = Debug|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Release_Silverlight|Any CPU.ActiveCfg = Release|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Release_Silverlight|Any CPU.Build.0 = Release|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{250ADE34-82FD-4BAE-86D5-985FBE589C4B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Debug_Silverlight|Any CPU.ActiveCfg = Debug|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Debug_Silverlight|Any CPU.Build.0 = Debug|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Release_Silverlight|Any CPU.ActiveCfg = Release|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Release_Silverlight|Any CPU.Build.0 = Release|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -185,6 +206,7 @@ Global
 		{EE01ED24-3750-4567-9A23-1DB676A15610} = {C7B69674-7A51-4AC6-8674-0330BA742CE4}
 		{EEFFED24-3750-4567-9A23-1DB676A15610} = {C7B69674-7A51-4AC6-8674-0330BA742CE4}
 		{DD01ED24-3750-4567-9A23-1DB676A15610} = {C7B69674-7A51-4AC6-8674-0330BA742CE4}
+		{C1024C9C-8176-48C3-B547-B9F6DF6B80A6} = {C7B69674-7A51-4AC6-8674-0330BA742CE4}
 		{A31F5FB2-4FF3-432A-B35B-5CD203606311} = {75D5D25A-01A6-4594-957F-5993FB83F450}
 		{C7A4A435-2813-41C8-AA87-BD914BA5223D} = {0D7CDA8F-1BBF-4E0F-8D35-31AEA21A96E6}
 		{D7282E99-2DC3-405B-946F-177DB2FD2AE2} = {0D7CDA8F-1BBF-4E0F-8D35-31AEA21A96E6}