Commit c58ce5dc authored by Jon Skeet's avatar Jon Skeet

Merge remote branch 'csharptest/master'

parents f18a5b09 e8e1dab0
...@@ -29,3 +29,5 @@ mono/TestResult.xml ...@@ -29,3 +29,5 @@ mono/TestResult.xml
mono/.libs mono/.libs
mono/*.exe mono/*.exe
mono/*.dll mono/*.dll
src/ProtocolBuffers/objCF
\ No newline at end of file
...@@ -266,10 +266,10 @@ ...@@ -266,10 +266,10 @@
<property name="Configuration" value="Release" /> <property name="Configuration" value="Release" />
<property name="Platform" value="Any CPU" /> <property name="Platform" value="Any CPU" />
</msbuild> </msbuild>
<msbuild project="${src}/ProtocolBuffers.sln"> <!--msbuild project="${src}/ProtocolBuffers.sln">
<property name="Configuration" value="Silverlight2" /> <property name="Configuration" value="Silverlight2" />
<property name="Platform" value="Any CPU" /> <property name="Platform" value="Any CPU" />
</msbuild> </msbuild-->
<!-- Note the deliberate lack of space in the platform name --> <!-- Note the deliberate lack of space in the platform name -->
<msbuild project="${src}/ProtocolBuffers/ProtocolBuffersCF.csproj"> <msbuild project="${src}/ProtocolBuffers/ProtocolBuffersCF.csproj">
<property name="Configuration" value="Release" /> <property name="Configuration" value="Release" />
...@@ -291,7 +291,7 @@ ...@@ -291,7 +291,7 @@
<mkdir dir="${output-dir}/Protoc" /> <mkdir dir="${output-dir}/Protoc" />
<mkdir dir="${output-dir}/Debug" /> <mkdir dir="${output-dir}/Debug" />
<mkdir dir="${output-dir}/Release" /> <mkdir dir="${output-dir}/Release" />
<mkdir dir="${output-dir}/Silverlight2" /> <!--mkdir dir="${output-dir}/Silverlight2" /-->
<mkdir dir="${output-dir}/CompactFramework35" /> <mkdir dir="${output-dir}/CompactFramework35" />
<copy todir="${output-dir}/Protoc"> <copy todir="${output-dir}/Protoc">
<fileset basedir="${project::get-base-directory()}/lib"> <fileset basedir="${project::get-base-directory()}/lib">
...@@ -321,12 +321,12 @@ ...@@ -321,12 +321,12 @@
<exclude name="**/*vshost*" /> <exclude name="**/*vshost*" />
</fileset> </fileset>
</copy> </copy>
<copy todir="${output-dir}/Silverlight2" <!--copy todir="${output-dir}/Silverlight2"
flatten="true"> flatten="true">
<fileset basedir="${src}"> <fileset basedir="${src}">
<include name="ProtocolBuffers/bin/Silverlight2/Google.ProtocolBuffers.dll" /> <include name="ProtocolBuffers/bin/Silverlight2/Google.ProtocolBuffers.dll" />
</fileset> </fileset>
</copy> </copy-->
<copy todir="${output-dir}/CompactFramework35" <copy todir="${output-dir}/CompactFramework35"
flatten="true"> flatten="true">
<fileset basedir="${src}"> <fileset basedir="${src}">
......
...@@ -38,6 +38,23 @@ message CSharpFileOptions { ...@@ -38,6 +38,23 @@ message CSharpFileOptions {
// Generate attributes indicating non-CLS-compliance // Generate attributes indicating non-CLS-compliance
optional bool cls_compliance = 8 [default = true]; optional bool cls_compliance = 8 [default = true];
// ROK - 2010-09-03 additions to csoptions ...
// The extension that should be appended to the umbrella_classname when creating files.
optional string file_extension = 221 [default = ".cs"];
// A nested namespace for the umbrella class. Helpful for name collisions caused by
// umbrella_classname conflicting with an existing type. This will be automatically
// set to 'Proto' if a collision is detected with types being generated. This value
// is ignored when nest_classes == true
optional string umbrella_namespace = 222;
// The output path for the source file(s) generated
optional string output_directory = 223 [default = "."];
// Will ignore the type generations and remove dependencies for the descriptor proto
// files that declare their package to be "google.protobuf"
optional bool ignore_google_protobuf = 224 [default = false];
} }
extend FileOptions { extend FileOptions {
......
...@@ -50,10 +50,14 @@ ...@@ -50,10 +50,14 @@
<HintPath>..\..\lib\Rhino.Mocks.dll</HintPath> <HintPath>..\..\lib\Rhino.Mocks.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DependencyResolutionTest.cs" /> <Compile Include="DependencyResolutionTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TempFile.cs" />
<Compile Include="TestPreprocessing.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj"> <ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
...@@ -68,6 +72,20 @@ ...@@ -68,6 +72,20 @@
<ItemGroup> <ItemGroup>
<None Include="Properties\Google.ProtocolBuffers.ProtoGen.Test.snk" /> <None Include="Properties\Google.ProtocolBuffers.ProtoGen.Test.snk" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\..\lib\protoc.exe">
<Link>protoc.exe</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\..\protos\google\protobuf\csharp_options.proto">
<Link>google\protobuf\csharp_options.proto</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\..\protos\google\protobuf\descriptor.proto">
<Link>google\protobuf\descriptor.proto</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Google.ProtocolBuffers.ProtoGen
{
class ProtoFile : TempFile
{
public ProtoFile (string filename, string contents)
{
_tempFile = filename;
File.WriteAllText(_tempFile, contents);
}
}
class TempFile : IDisposable
{
protected string _tempFile;
public static TempFile Attach(string path)
{
TempFile f = new TempFile();
f._tempFile = path;
return f;
}
protected TempFile() { }
public TempFile(string contents)
{
File.WriteAllText(_tempFile = Path.GetTempFileName(), contents, Encoding.ASCII);
}
public string TempPath { get { return _tempFile; } }
public void ChangeExtension(string ext)
{
string newFile = Path.ChangeExtension(_tempFile, ext);
File.Move(_tempFile, newFile);
_tempFile = newFile;
}
public void Dispose()
{
if(File.Exists(_tempFile))
File.Delete(_tempFile);
}
}
}
This diff is collapsed.
...@@ -46,10 +46,24 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -46,10 +46,24 @@ namespace Google.ProtocolBuffers.ProtoGen {
CSharpFileOptions options = descriptor.File.CSharpOptions; CSharpFileOptions options = descriptor.File.CSharpOptions;
string result = options.Namespace; string result = options.Namespace;
if (result != "") result += '.'; if (result != "") result += '.';
result += options.UmbrellaClassname; result += QualifiedUmbrellaClassName(options);
return "global::" + result; return "global::" + result;
} }
/// <summary>
/// ROK 2010-09-03
/// Evaluates the options and returns the qualified name of the umbrella class
/// relative to the descriptor type's namespace. Basically contacts the
/// UmbrellaNamespace + UmbrellaClassname fields.
/// </summary>
internal static string QualifiedUmbrellaClassName(CSharpFileOptions options)
{
string fullName = options.UmbrellaClassname;
if (!options.NestClasses && options.UmbrellaNamespace != "")
fullName = String.Format("{0}.{1}", options.UmbrellaNamespace, options.UmbrellaClassname);
return fullName;
}
internal static string GetMappedTypeName(MappedType type) { internal static string GetMappedTypeName(MappedType type) {
switch(type) { switch(type) {
case MappedType.Int32: return "int"; case MappedType.Int32: return "int";
......
...@@ -60,19 +60,30 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -60,19 +60,30 @@ namespace Google.ProtocolBuffers.ProtoGen {
} }
public void Generate() { public void Generate() {
List<FileDescriptorSet> descriptorProtos = new List<FileDescriptorSet>();
foreach (string inputFile in options.InputFiles) { foreach (string inputFile in options.InputFiles) {
FileDescriptorSet descriptorProtos;
ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance(); ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
extensionRegistry.Add(CSharpOptions.CSharpFileOptions); extensionRegistry.Add(CSharpOptions.CSharpFileOptions);
extensionRegistry.Add(CSharpOptions.CSharpFieldOptions); extensionRegistry.Add(CSharpOptions.CSharpFieldOptions);
using (Stream inputStream = File.OpenRead(inputFile)) { using (Stream inputStream = File.OpenRead(inputFile)) {
descriptorProtos = FileDescriptorSet.ParseFrom(inputStream, extensionRegistry); descriptorProtos.Add(FileDescriptorSet.ParseFrom(inputStream, extensionRegistry));
}
} }
IList<FileDescriptor> descriptors = ConvertDescriptors(descriptorProtos);
IList<FileDescriptor> descriptors = ConvertDescriptors(options.FileOptions, descriptorProtos.ToArray());
//ROK Combine with Options...
foreach (FileDescriptor descriptor in descriptors)
descriptor.ConfigureWithDefaultOptions(options.FileOptions);
foreach (FileDescriptor descriptor in descriptors) { foreach (FileDescriptor descriptor in descriptors) {
//ROK 2010-09-03 Ignore google protobuf package
if(descriptor.CSharpOptions.IgnoreGoogleProtobuf && descriptor.Package == "google.protobuf")
continue;
Generate(descriptor); Generate(descriptor);
}
} }
} }
...@@ -90,8 +101,14 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -90,8 +101,14 @@ namespace Google.ProtocolBuffers.ProtoGen {
private string GetOutputFile(FileDescriptor descriptor) { private string GetOutputFile(FileDescriptor descriptor) {
CSharpFileOptions fileOptions = descriptor.CSharpOptions; CSharpFileOptions fileOptions = descriptor.CSharpOptions;
string filename = descriptor.CSharpOptions.UmbrellaClassname + ".cs";
string outputDirectory = options.OutputDirectory; //ROK 2010-09-03 - added the ability to sepcify the extension used within the options
//string filename = descriptor.CSharpOptions.UmbrellaClassname + ".cs";
string filename = descriptor.CSharpOptions.UmbrellaClassname + descriptor.CSharpOptions.FileExtension;
//ROK 2010-09-03 - output directory can be specific to a descriptor file
//string outputDirectory = options.OutputDirectory;
string outputDirectory = descriptor.CSharpOptions.OutputDirectory;
if (fileOptions.ExpandNamespaceDirectories) { if (fileOptions.ExpandNamespaceDirectories) {
string package = fileOptions.Namespace; string package = fileOptions.Namespace;
if (!string.IsNullOrEmpty(package)) { if (!string.IsNullOrEmpty(package)) {
...@@ -99,22 +116,31 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -99,22 +116,31 @@ namespace Google.ProtocolBuffers.ProtoGen {
foreach (string bit in bits) { foreach (string bit in bits) {
outputDirectory = Path.Combine(outputDirectory, bit); outputDirectory = Path.Combine(outputDirectory, bit);
} }
Directory.CreateDirectory(outputDirectory);
} }
} }
//ROK 2010-09-03 - Always force output directory exists since they can specify this in .proto options
Directory.CreateDirectory(outputDirectory);
return Path.Combine(outputDirectory, filename); return Path.Combine(outputDirectory, filename);
} }
// ROK 2010-09-03 - used by unit tests, we will continue to allow them to function as-is.
internal static IList<FileDescriptor> ConvertDescriptors(FileDescriptorSet descriptorProtos) {
return ConvertDescriptors(CSharpFileOptions.DefaultInstance, descriptorProtos);
}
/// <summary> /// <summary>
/// Resolves any dependencies and converts FileDescriptorProtos into FileDescriptors. /// Resolves any dependencies and converts FileDescriptorProtos into FileDescriptors.
/// The list returned is in the same order as the protos are listed in the descriptor set. /// The list returned is in the same order as the protos are listed in the descriptor set.
/// Note: this method is internal rather than private to allow testing. /// Note: this method is internal rather than private to allow testing.
/// </summary> /// </summary>
/// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception> /// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception>
internal static IList<FileDescriptor> ConvertDescriptors(FileDescriptorSet descriptorProtos) { private static IList<FileDescriptor> ConvertDescriptors(CSharpFileOptions options, params FileDescriptorSet[] descriptorProtos) {
// Simple strategy: Keep going through the list of protos to convert, only doing ones where // 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 // we've already converted all the dependencies, until we get to a stalemate
IList<FileDescriptorProto> fileList = descriptorProtos.FileList; List<FileDescriptorProto> fileList = new List<FileDescriptorProto>();
foreach (FileDescriptorSet set in descriptorProtos)
fileList.AddRange(set.FileList);
FileDescriptor[] converted = new FileDescriptor[fileList.Count]; FileDescriptor[] converted = new FileDescriptor[fileList.Count];
Dictionary<string, FileDescriptor> convertedMap = new Dictionary<string, FileDescriptor>(); Dictionary<string, FileDescriptor> convertedMap = new Dictionary<string, FileDescriptor>();
...@@ -131,9 +157,31 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -131,9 +157,31 @@ namespace Google.ProtocolBuffers.ProtoGen {
} }
FileDescriptorProto candidate = fileList[i]; FileDescriptorProto candidate = fileList[i];
FileDescriptor[] dependencies = new FileDescriptor[candidate.DependencyList.Count]; FileDescriptor[] dependencies = new FileDescriptor[candidate.DependencyList.Count];
CSharpFileOptions.Builder builder = options.ToBuilder();
if (candidate.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions)) {
builder.MergeFrom(candidate.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions));
}
CSharpFileOptions localOptions = builder.Build();
bool foundAllDependencies = true; bool foundAllDependencies = true;
for (int j = 0; j < dependencies.Length; j++) { for (int j = 0; j < dependencies.Length; j++) {
if (!convertedMap.TryGetValue(candidate.DependencyList[j], out dependencies[j])) { if (!convertedMap.TryGetValue(candidate.DependencyList[j], out dependencies[j])) {
// ROK 2010-09-03 - we can auto-magically resolve these since we already have their description
// This way if the file is only referencing options it does not need to be built with the
// --include_imports definition.
if (localOptions.IgnoreGoogleProtobuf && (candidate.DependencyList[j] == "google/protobuf/csharp_options.proto"))
{
dependencies[j] = CSharpOptions.Descriptor;
continue;
}
if (localOptions.IgnoreGoogleProtobuf && (candidate.DependencyList[j] == "google/protobuf/descriptor.proto"))
{
dependencies[j] = DescriptorProtoFile.Descriptor;
continue;
}
foundAllDependencies = false; foundAllDependencies = false;
break; break;
} }
......
...@@ -32,8 +32,12 @@ ...@@ -32,8 +32,12 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion #endregion
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen { namespace Google.ProtocolBuffers.ProtoGen {
...@@ -44,8 +48,7 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -44,8 +48,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
/// the generator. /// the generator.
/// </summary> /// </summary>
public sealed class GeneratorOptions { public sealed class GeneratorOptions {
//ROK, see below - public string OutputDirectory { get; set; }
public string OutputDirectory { get; set; }
public IList<string> InputFiles { get; set; } public IList<string> InputFiles { get; set; }
/// <summary> /// <summary>
...@@ -58,12 +61,15 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -58,12 +61,15 @@ namespace Google.ProtocolBuffers.ProtoGen {
public bool TryValidate(out IList<string> reasons) { public bool TryValidate(out IList<string> reasons) {
List<string> tmpReasons = new List<string>(); List<string> tmpReasons = new List<string>();
//ROK 2010-09-03 see population of options below
ParseArguments(tmpReasons);
// Output directory validation // Output directory validation
if (string.IsNullOrEmpty(OutputDirectory)) { if (string.IsNullOrEmpty(FileOptions.OutputDirectory)) {
tmpReasons.Add("No output directory specified"); tmpReasons.Add("No output directory specified");
} else { } else {
if (!Directory.Exists(OutputDirectory)) { if (!Directory.Exists(FileOptions.OutputDirectory)) {
tmpReasons.Add("Specified output directory (" + OutputDirectory + " doesn't exist."); tmpReasons.Add("Specified output directory (" + FileOptions.OutputDirectory + " doesn't exist.");
} }
} }
...@@ -99,5 +105,179 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -99,5 +105,179 @@ namespace Google.ProtocolBuffers.ProtoGen {
throw new InvalidOptionsException(reasons); throw new InvalidOptionsException(reasons);
} }
} }
// ROK - added to provide defaults for any of the options
//Raw arguments
public IList<string> Arguments { get; set; }
[Obsolete("Please use GeneratorOptions.FileOptions.OutputDirectory instead")]
public string OutputDirectory
{
get {
return FileOptions.OutputDirectory;
}
set {
CSharpFileOptions.Builder bld = FileOptions.ToBuilder();
bld.OutputDirectory = value;
FileOptions = bld.Build();
}
}
private static readonly Regex ArgMatch = new Regex(@"^[-/](?<name>[\w_]+?)[:=](?<value>.*)$");
CSharpFileOptions _fileOptions;
public CSharpFileOptions FileOptions
{
get { return _fileOptions ?? (_fileOptions = CSharpFileOptions.DefaultInstance); }
set { _fileOptions = value; }
}
private void ParseArguments(IList<string> tmpReasons)
{
bool doHelp = Arguments.Count == 0;
//ROK Parse the raw arguments
InputFiles = new List<string>();
CSharpFileOptions.Builder builder = FileOptions.ToBuilder();
Dictionary<string, FieldDescriptor> fields =
new Dictionary<string, FieldDescriptor>(StringComparer.OrdinalIgnoreCase);
foreach (FieldDescriptor fld in builder.DescriptorForType.Fields)
fields.Add(fld.Name, fld);
foreach (string argument in Arguments)
{
if (StringComparer.OrdinalIgnoreCase.Equals("-help", argument) ||
StringComparer.OrdinalIgnoreCase.Equals("/help", argument) ||
StringComparer.OrdinalIgnoreCase.Equals("-?", argument) ||
StringComparer.OrdinalIgnoreCase.Equals("/?", argument))
{
doHelp = true;
break;
}
Match m = ArgMatch.Match(argument);
if (m.Success)
{
FieldDescriptor fld;
string name = m.Groups["name"].Value;
string value = m.Groups["value"].Value;
if (fields.TryGetValue(name, out fld))
{
object obj;
if (TryCoerceType(value, fld, out obj, tmpReasons))
builder[fld] = obj;
}
else if (!File.Exists(argument))
{
doHelp = true;
tmpReasons.Add("Unknown argument '" + name + "'.");
}
else
InputFiles.Add(argument);
}
else
InputFiles.Add(argument);
}
if (doHelp || InputFiles.Count == 0)
{
tmpReasons.Add("Arguments:");
foreach (KeyValuePair<string, FieldDescriptor> field in fields)
{
tmpReasons.Add(String.Format("-{0}=[{1}]", field.Key, field.Value.FieldType));
}
tmpReasons.Add("followed by one or more file paths.");
}
else
FileOptions = builder.Build();
}
private static bool TryCoerceType(string text, FieldDescriptor field, out object value, IList<string> tmpReasons)
{
value = null;
switch (field.FieldType)
{
case FieldType.Int32:
case FieldType.SInt32:
case FieldType.SFixed32:
value = Int32.Parse(text);
break;
case FieldType.Int64:
case FieldType.SInt64:
case FieldType.SFixed64:
value = Int64.Parse(text);
break;
case FieldType.UInt32:
case FieldType.Fixed32:
value = UInt32.Parse(text);
break;
case FieldType.UInt64:
case FieldType.Fixed64:
value = UInt64.Parse(text);
break;
case FieldType.Float:
value = float.Parse(text);
break;
case FieldType.Double:
value = Double.Parse(text);
break;
case FieldType.Bool:
value = Boolean.Parse(text);
break;
case FieldType.String:
value = text;
break;
case FieldType.Enum:
{
EnumDescriptor enumType = field.EnumType;
int number;
if (int.TryParse(text, out number))
{
value = enumType.FindValueByNumber(number);
if (value == null)
{
tmpReasons.Add(
"Enum type \"" + enumType.FullName +
"\" has no value with number " + number + ".");
return false;
}
}
else
{
value = enumType.FindValueByName(text);
if (value == null)
{
tmpReasons.Add(
"Enum type \"" + enumType.FullName +
"\" has no value named \"" + text + "\".");
return false;
}
}
break;
}
case FieldType.Bytes:
case FieldType.Message:
case FieldType.Group:
tmpReasons.Add("Unhandled field type " + field.FieldType.ToString() + ".");
return false;
}
return true;
}
} }
} }
...@@ -41,7 +41,7 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -41,7 +41,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
/// Entry point for the Protocol Buffers generator. /// Entry point for the Protocol Buffers generator.
/// </summary> /// </summary>
class Program { class Program {
static int Main(string[] args) { internal static int Main(string[] args) {
try { try {
// Hack to make sure everything's initialized // Hack to make sure everything's initialized
DescriptorProtoFile.Descriptor.ToString(); DescriptorProtoFile.Descriptor.ToString();
...@@ -71,8 +71,11 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -71,8 +71,11 @@ namespace Google.ProtocolBuffers.ProtoGen {
//string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers"; //string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers";
//options.OutputDirectory = baseDir + "\\tmp"; //options.OutputDirectory = baseDir + "\\tmp";
//options.InputFiles = new[] { baseDir + "\\protos\\nwind-solo.protobin" }; //options.InputFiles = new[] { baseDir + "\\protos\\nwind-solo.protobin" };
options.OutputDirectory = ".";
options.InputFiles = args; //ROK 2010-09-03 - fixes to allow parsing these options...
//options.OutputDirectory = ".";
//options.InputFiles = args;
options.Arguments = args;
return options; return options;
} }
} }
......
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Google.ProtocolBuffers.ProtoGen
{
/// <summary>
/// Preprocesses any input files with an extension of '.proto' by running protoc.exe. If arguments
/// are supplied with '--' prefix they are provided to protoc.exe, otherwise they are assumed to
/// be used for ProtoGen.exe which is run on the resulting output proto buffer. If the option
/// --descriptor_set_out= is specified the proto buffer file is kept, otherwise it will be removed
/// after code generation.
/// </summary>
internal class ProgramPreprocess
{
static int Main(string[] args)
{
try
{
return Environment.ExitCode = Run(args);
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
return Environment.ExitCode = 2;
}
}
internal static int Run(params string[] args)
{
bool deleteFile = false;
string tempFile = null;
int result = 1;
bool doHelp = args.Length == 0;
try
{
List<string> protocArgs = new List<string>();
List<string> protoGenArgs = new List<string>();
foreach (string arg in args)
{
doHelp |= StringComparer.OrdinalIgnoreCase.Equals(arg, "/?");
doHelp |= StringComparer.OrdinalIgnoreCase.Equals(arg, "/help");
doHelp |= StringComparer.OrdinalIgnoreCase.Equals(arg, "-?");
doHelp |= StringComparer.OrdinalIgnoreCase.Equals(arg, "-help");
if(arg.StartsWith("--descriptor_set_out="))
{
tempFile = arg.Substring("--descriptor_set_out=".Length);
protoGenArgs.Add(tempFile);
}
}
if (doHelp)
{
Console.WriteLine();
Console.WriteLine("PROTOC.exe: Use any of the following options that begin with '--':");
Console.WriteLine();
try { RunProtoc("--help"); }
catch (Exception ex) { Console.Error.WriteLine(ex.Message); }
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("PRTOGEN.exe: The following options are used to specify defaults for code generation.");
Console.WriteLine();
Program.Main(new string[0]);
return 0;
}
foreach (string arg in args)
{
if (arg.StartsWith("--"))
protocArgs.Add(arg);
else if (File.Exists(arg) && StringComparer.OrdinalIgnoreCase.Equals(".proto", Path.GetExtension(arg)))
{
if (tempFile == null)
{
deleteFile = true;
tempFile = Path.GetTempFileName();
protocArgs.Add(String.Format("--descriptor_set_out={0}", tempFile));
protoGenArgs.Add(tempFile);
}
protocArgs.Add(arg);
}
else
protoGenArgs.Add(arg);
}
if (tempFile != null)
{
result = RunProtoc(protocArgs.ToArray());
if (result != 0)
return result;
}
result = Program.Main(protoGenArgs.ToArray());
}
finally
{
if (deleteFile && tempFile != null && File.Exists(tempFile))
File.Delete(tempFile);
}
return result;
}
private static int RunProtoc(params string[] args)
{
const string protoc = "protoc.exe";
string exePath = protoc;
//why oh why is this not in System.IO.Path or Environment...
List<string> searchPath = new List<string>();
searchPath.Add(Environment.CurrentDirectory);
searchPath.Add(AppDomain.CurrentDomain.BaseDirectory);
searchPath.AddRange((Environment.GetEnvironmentVariable("PATH") ?? String.Empty).Split(Path.PathSeparator));
foreach (string path in searchPath)
if (File.Exists(exePath = Path.Combine(path, protoc)))
break;
if (!File.Exists(exePath))
throw new FileNotFoundException("Unable to locate " + protoc + " make sure it is in the PATH, cwd, or exe dir.");
for (int i = 0; i < args.Length; i++)
if (args[i].IndexOf(' ') > 0 && args[i][0] != '"')
args[i] = '"' + args[i] + '"';
ProcessStartInfo psi = new ProcessStartInfo(exePath);
psi.Arguments = String.Join(" ", args);
psi.RedirectStandardError = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardOutput = true;
psi.ErrorDialog = false;
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.WorkingDirectory = Environment.CurrentDirectory;
Process process = Process.Start(psi);
if (process == null) return 1;
process.WaitForExit();
string tmp = process.StandardOutput.ReadToEnd();
if(tmp.Trim().Length > 0) Console.Out.WriteLine(tmp);
tmp = process.StandardError.ReadToEnd();
if (tmp.Trim().Length > 0) Console.Error.WriteLine(tmp);
return process.ExitCode;
}
}
}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
</TargetFrameworkSubset> </TargetFrameworkSubset>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Properties\Google.ProtocolBuffers.ProtoGen.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>Properties\Google.ProtocolBuffers.ProtoGen.snk</AssemblyOriginatorKeyFile>
<StartupObject>Google.ProtocolBuffers.ProtoGen.ProgramPreprocess</StartupObject>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
...@@ -58,6 +59,7 @@ ...@@ -58,6 +59,7 @@
<Compile Include="MessageFieldGenerator.cs" /> <Compile Include="MessageFieldGenerator.cs" />
<Compile Include="MessageGenerator.cs" /> <Compile Include="MessageGenerator.cs" />
<Compile Include="PrimitiveFieldGenerator.cs" /> <Compile Include="PrimitiveFieldGenerator.cs" />
<Compile Include="ProgramPreprocess.cs" />
<Compile Include="RepeatedEnumFieldGenerator.cs" /> <Compile Include="RepeatedEnumFieldGenerator.cs" />
<Compile Include="RepeatedMessageFieldGenerator.cs" /> <Compile Include="RepeatedMessageFieldGenerator.cs" />
<Compile Include="RepeatedPrimitiveFieldGenerator.cs" /> <Compile Include="RepeatedPrimitiveFieldGenerator.cs" />
......
...@@ -61,7 +61,7 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -61,7 +61,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
writer.WriteLine(); writer.WriteLine();
writer.WriteLine("{0} static pbd::ServiceDescriptor Descriptor {{", ClassAccessLevel); writer.WriteLine("{0} static pbd::ServiceDescriptor Descriptor {{", ClassAccessLevel);
writer.WriteLine(" get {{ return {0}.Descriptor.Services[{1}]; }}", writer.WriteLine(" get {{ return {0}.Descriptor.Services[{1}]; }}",
Descriptor.File.CSharpOptions.UmbrellaClassname, Descriptor.Index); DescriptorUtil.QualifiedUmbrellaClassName(Descriptor.File.CSharpOptions), Descriptor.Index);
writer.WriteLine("}"); writer.WriteLine("}");
writer.WriteLine("{0} pbd::ServiceDescriptor DescriptorForType {{", ClassAccessLevel); writer.WriteLine("{0} pbd::ServiceDescriptor DescriptorForType {{", ClassAccessLevel);
writer.WriteLine(" get { return Descriptor; }"); writer.WriteLine(" get { return Descriptor; }");
......
...@@ -92,6 +92,12 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -92,6 +92,12 @@ namespace Google.ProtocolBuffers.ProtoGen {
if (!Descriptor.CSharpOptions.NestClasses) { if (!Descriptor.CSharpOptions.NestClasses) {
writer.Outdent(); writer.Outdent();
writer.WriteLine("}"); writer.WriteLine("}");
//ROK 2010-09-03 - close the namespace around the umbrella class if defined
if (!Descriptor.CSharpOptions.NestClasses && Descriptor.CSharpOptions.UmbrellaNamespace != "") {
writer.Outdent();
writer.WriteLine("}");
}
} }
WriteChildren(writer, "Enums", Descriptor.EnumTypes); WriteChildren(writer, "Enums", Descriptor.EnumTypes);
WriteChildren(writer, "Messages", Descriptor.MessageTypes); WriteChildren(writer, "Messages", Descriptor.MessageTypes);
...@@ -116,6 +122,13 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -116,6 +122,13 @@ namespace Google.ProtocolBuffers.ProtoGen {
writer.Indent(); writer.Indent();
writer.WriteLine(); writer.WriteLine();
} }
//ROK 2010-09-03 - add the namespace around the umbrella class if defined
if(!Descriptor.CSharpOptions.NestClasses && Descriptor.CSharpOptions.UmbrellaNamespace != "") {
writer.WriteLine("namespace {0} {{", Descriptor.CSharpOptions.UmbrellaNamespace);
writer.Indent();
writer.WriteLine();
}
if (Descriptor.CSharpOptions.CodeContracts) { if (Descriptor.CSharpOptions.CodeContracts) {
writer.WriteLine("[global::System.Diagnostics.Contracts.ContractVerificationAttribute(false)]"); writer.WriteLine("[global::System.Diagnostics.Contracts.ContractVerificationAttribute(false)]");
} }
......
...@@ -17,6 +17,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoBench", "ProtoBench\Pr ...@@ -17,6 +17,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoBench", "ProtoBench\Pr
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoDump", "ProtoDump\ProtoDump.csproj", "{D7282E99-2DC3-405B-946F-177DB2FD2AE2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoDump", "ProtoDump\ProtoDump.csproj", "{D7282E99-2DC3-405B-946F-177DB2FD2AE2}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "proto", "proto", "{1F896D5C-5FC2-4671-9216-781CB8187EC7}"
ProjectSection(SolutionItems) = preProject
..\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
..\todo.txt = ..\todo.txt
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
......
...@@ -73,21 +73,36 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -73,21 +73,36 @@ namespace Google.ProtocolBuffers.Descriptors {
(field, index) => new FieldDescriptor(field, this, null, index, true)); (field, index) => new FieldDescriptor(field, this, null, index, true));
} }
private CSharpFileOptions BuildOrFakeCSharpOptions() {
// TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues /// <summary>
if (proto.Name == "google/protobuf/descriptor.proto") { /// ROK - Added to allow the GeneratorOptions to sepcify the default for any or all of these values
/// </summary>
internal void ConfigureWithDefaultOptions(CSharpFileOptions options)
{
csharpFileOptions = BuildOrFakeWithDefaultOptions(options);
}
private CSharpFileOptions BuildOrFakeWithDefaultOptions(CSharpFileOptions defaultOptions)
{
// ROK 2010-09-03 - fix for being able to relocate these files to any directory structure
if(proto.Package == "google.protobuf") {
string filename = System.IO.Path.GetFileName(proto.Name);
// TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues)
if (filename == "descriptor.proto") {
return new CSharpFileOptions.Builder { return new CSharpFileOptions.Builder {
Namespace = "Google.ProtocolBuffers.DescriptorProtos", Namespace = "Google.ProtocolBuffers.DescriptorProtos",
UmbrellaClassname = "DescriptorProtoFile", NestClasses = false, MultipleFiles = false, PublicClasses = true UmbrellaClassname = "DescriptorProtoFile", NestClasses = false, MultipleFiles = false, PublicClasses = true,
OutputDirectory = defaultOptions.OutputDirectory, IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
}.Build(); }.Build();
} }
if (proto.Name == "google/protobuf/csharp_options.proto") { if (filename == "csharp_options.proto") {
return new CSharpFileOptions.Builder { return new CSharpFileOptions.Builder {
Namespace = "Google.ProtocolBuffers.DescriptorProtos", Namespace = "Google.ProtocolBuffers.DescriptorProtos",
UmbrellaClassname = "CSharpOptions", NestClasses = false, MultipleFiles = false, PublicClasses = true UmbrellaClassname = "CSharpOptions", NestClasses = false, MultipleFiles = false, PublicClasses = true,
OutputDirectory = defaultOptions.OutputDirectory, IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
}.Build(); }.Build();
} }
CSharpFileOptions.Builder builder = CSharpFileOptions.CreateBuilder(); }
CSharpFileOptions.Builder builder = defaultOptions.ToBuilder();
if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions)) { if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions)) {
builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions)); builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions));
} }
...@@ -99,6 +114,21 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -99,6 +114,21 @@ namespace Google.ProtocolBuffers.Descriptors {
string baseName = Name.Substring(lastSlash + 1); string baseName = Name.Substring(lastSlash + 1);
builder.UmbrellaClassname = NameHelpers.UnderscoresToPascalCase(NameHelpers.StripProto(baseName)); builder.UmbrellaClassname = NameHelpers.UnderscoresToPascalCase(NameHelpers.StripProto(baseName));
} }
// ROK 2010-09-03 - auto fix for name collision by placing umbrella class into a new namespace. This
// still won't fix the collisions with nesting enabled; however, you have to turn that on so whatever.
if(!builder.NestClasses && !builder.HasUmbrellaNamespace) {
bool collision = false;
foreach (IDescriptor d in MessageTypes)
collision |= d.Name == builder.UmbrellaClassname;
foreach (IDescriptor d in Services)
collision |= d.Name == builder.UmbrellaClassname;
foreach (IDescriptor d in EnumTypes)
collision |= d.Name == builder.UmbrellaClassname;
if (collision)
builder.UmbrellaNamespace = "Proto";
}
return builder.Build(); return builder.Build();
} }
...@@ -124,7 +154,7 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -124,7 +154,7 @@ namespace Google.ProtocolBuffers.Descriptors {
get { get {
lock (optionsLock) { lock (optionsLock) {
if (csharpFileOptions == null) { if (csharpFileOptions == null) {
csharpFileOptions = BuildOrFakeCSharpOptions(); csharpFileOptions = BuildOrFakeWithDefaultOptions(CSharpFileOptions.DefaultInstance);
} }
} }
return csharpFileOptions; return csharpFileOptions;
......
Work complete on csharptest/branch
New Options (in csharp_options.proto):
1 - Add a way to specify the output directory
Added an option to csharp_options.proto called "output_directory", defaults to "."
2 - Added an option "file_extension" to control the suffix for cs files generated, defaults to ".cs"
This enables using ".Generated.cs" to provide easy filtering and cleanup of generated code.
3 - Added the option for "umbrella_namespace" used when nest_classes=false and having name conflicts
4 - Optionally remove dependencies to csharp options
provided option "ignore_google_protobuf" to prevent generation of code for csharp_options.proto and descriptor.proto
option also relaxes the contraint of having either of these compiled into the proto buffer input
5 - Investigate command line parsing library
All proto options for csharp_options.proto can now be provided via the command-line by using the following format:
/option=value or -option:value
i.e. use /namespace=My.Name.Space
6 - Investigate calling protoc directly
ProgramPreprocess.cs - input files with an extension of '.proto' by running protoc.exe. If arguments
are supplied with '--' prefix they are provided to protoc.exe, otherwise they are assumed to
be used for ProtoGen.exe which is run on the resulting output proto buffer. If the option
--descriptor_set_out= is specified the proto buffer file is kept, otherwise it will be removed
after code generation.
7 - Unable to resolve dependencies correctly
Fixed an issue where two or more proto-buffers are provided to protogen.exe that depend on types
defined in one-another. The dependency walker of the generator was not taking into account all
inputs when evaluating the descriptor's existence.
8 - Added several (20) nunits to automate the command-line invocation of each option and ensured the
generated code compiles into a working assembly and contains some of the expected types. Not sure
how to disable this test for mono, I'm certain it will not work; however, I still believe it's worth
having. As a side-benefit from testing the command-line options, it verifies behavior of each
setting in csharp_options (save for multi-file).
Current task list (not in order) Current task list (not in order)
- Optionally remove dependencies to csharp options ? Optionally remove dependencies to csharp options
- Remove multifile support - Remove multifile support
- Docs - Docs
- Clean up protogen code - Clean up protogen code
- Add flags to protogen X Add flags to protogen
- Avoid using reflection for messages which don't need it (is this - Avoid using reflection for messages which don't need it (is this
possible?) possible?)
- Bring service generation into line with Java - Bring service generation into line with Java
...@@ -14,7 +53,7 @@ Current task list (not in order) ...@@ -14,7 +53,7 @@ Current task list (not in order)
- Reformat code - Reformat code
- Change generated format - Change generated format
- Add regions to copyright - Add regions to copyright
- Investigate command line parsing library X Investigate command line parsing library
- Investigate calling protoc directly X Investigate calling protoc directly
- Build and publish binaries - Build and publish binaries
- Work out why the Compact Framework 3.5 build fails under VS2010 - Work out why the Compact Framework 3.5 build fails under VS2010
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment