Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
protobuf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
protobuf
Commits
64580d09
Commit
64580d09
authored
Nov 22, 2010
by
ArnoldZokas
Browse files
Options
Browse Files
Download
Plain Diff
Fixed merge conflicts
parents
a695dfaa
57599ef1
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1367 additions
and
73 deletions
+1367
-73
.gitignore
.gitignore
+1
-0
csharp_options.proto
protos/google/protobuf/csharp_options.proto
+16
-0
DependencyResolutionTest.cs
src/ProtoGen.Test/DependencyResolutionTest.cs
+6
-6
ProtoGen.Test.csproj
src/ProtoGen.Test/ProtoGen.Test.csproj
+16
-0
TempFile.cs
src/ProtoGen.Test/TempFile.cs
+54
-0
TestPreprocessing.cs
src/ProtoGen.Test/TestPreprocessing.cs
+610
-0
DescriptorUtil.cs
src/ProtoGen/DescriptorUtil.cs
+17
-2
Generator.cs
src/ProtoGen/Generator.cs
+46
-9
GeneratorOptions.cs
src/ProtoGen/GeneratorOptions.cs
+172
-8
Program.cs
src/ProtoGen/Program.cs
+7
-15
ProgramPreprocess.cs
src/ProtoGen/ProgramPreprocess.cs
+153
-0
ProtoGen.csproj
src/ProtoGen/ProtoGen.csproj
+2
-0
ServiceGenerator.cs
src/ProtoGen/ServiceGenerator.cs
+1
-1
UmbrellaClassGenerator.cs
src/ProtoGen/UmbrellaClassGenerator.cs
+13
-0
DynamicMessageTest.cs
src/ProtocolBuffers.Test/DynamicMessageTest.cs
+0
-0
ProtocolBuffers.sln
src/ProtocolBuffers.sln
+8
-0
CSharpOptions.cs
src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs
+178
-8
FileDescriptor.cs
src/ProtocolBuffers/Descriptors/FileDescriptor.cs
+67
-20
DynamicMessage.cs
src/ProtocolBuffers/DynamicMessage.cs
+0
-0
UnknownField.cs
src/ProtocolBuffers/UnknownField.cs
+0
-0
todo.txt
todo.txt
+0
-4
No files found.
.gitignore
View file @
64580d09
...
...
@@ -5,6 +5,7 @@ src/AddressBook/bin
src/AddressBook/obj
src/ProtocolBuffers/bin/
src/ProtocolBuffers/obj/
src/ProtocolBuffers/objCF
src/ProtocolBuffers.Test/bin/
src/ProtocolBuffers.Test/obj/
src/ProtoBench/bin/
...
...
protos/google/protobuf/csharp_options.proto
View file @
64580d09
...
...
@@ -38,6 +38,22 @@ message CSharpFileOptions {
// Generate attributes indicating non-CLS-compliance
optional
bool
cls_compliance
=
8
[
default
=
true
];
// 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
{
...
...
src/ProtoGen.Test/DependencyResolutionTest.cs
View file @
64580d09
...
...
@@ -50,7 +50,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
FileDescriptorProto
second
=
new
FileDescriptorProto
.
Builder
{
Name
=
"Second"
}.
Build
();
FileDescriptorSet
set
=
new
FileDescriptorSet
{
FileList
=
{
first
,
second
}
};
IList
<
FileDescriptor
>
converted
=
Generator
.
ConvertDescriptors
(
set
);
IList
<
FileDescriptor
>
converted
=
Generator
.
ConvertDescriptors
(
CSharpFileOptions
.
DefaultInstance
,
set
);
Assert
.
AreEqual
(
2
,
converted
.
Count
);
Assert
.
AreEqual
(
"First"
,
converted
[
0
].
Name
);
Assert
.
AreEqual
(
0
,
converted
[
0
].
Dependencies
.
Count
);
...
...
@@ -63,7 +63,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
{
FileList
=
{
first
,
second
}
};
IList
<
FileDescriptor
>
converted
=
Generator
.
ConvertDescriptors
(
set
);
IList
<
FileDescriptor
>
converted
=
Generator
.
ConvertDescriptors
(
CSharpFileOptions
.
DefaultInstance
,
set
);
Assert
.
AreEqual
(
2
,
converted
.
Count
);
Assert
.
AreEqual
(
"First"
,
converted
[
0
].
Name
);
Assert
.
AreEqual
(
1
,
converted
[
0
].
Dependencies
.
Count
);
...
...
@@ -77,7 +77,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
{
FileList
=
{
first
,
second
}
};
IList
<
FileDescriptor
>
converted
=
Generator
.
ConvertDescriptors
(
set
);
IList
<
FileDescriptor
>
converted
=
Generator
.
ConvertDescriptors
(
CSharpFileOptions
.
DefaultInstance
,
set
);
Assert
.
AreEqual
(
2
,
converted
.
Count
);
Assert
.
AreEqual
(
"First"
,
converted
[
0
].
Name
);
Assert
.
AreEqual
(
0
,
converted
[
0
].
Dependencies
.
Count
);
...
...
@@ -92,7 +92,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
FileDescriptorProto
second
=
new
FileDescriptorProto
.
Builder
{
Name
=
"Second"
,
DependencyList
=
{
"First"
}
}.
Build
();
FileDescriptorSet
set
=
new
FileDescriptorSet
{
FileList
=
{
first
,
second
}
};
try
{
Generator
.
ConvertDescriptors
(
set
);
Generator
.
ConvertDescriptors
(
CSharpFileOptions
.
DefaultInstance
,
set
);
Assert
.
Fail
(
"Expected exception"
);
}
catch
(
DependencyResolutionException
)
{
// Expected
...
...
@@ -104,7 +104,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
FileDescriptorProto
first
=
new
FileDescriptorProto
.
Builder
{
Name
=
"First"
,
DependencyList
=
{
"Second"
}
}.
Build
();
FileDescriptorSet
set
=
new
FileDescriptorSet
{
FileList
=
{
first
}
};
try
{
Generator
.
ConvertDescriptors
(
set
);
Generator
.
ConvertDescriptors
(
CSharpFileOptions
.
DefaultInstance
,
set
);
Assert
.
Fail
(
"Expected exception"
);
}
catch
(
DependencyResolutionException
)
{
// Expected
...
...
@@ -116,7 +116,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
FileDescriptorProto
first
=
new
FileDescriptorProto
.
Builder
{
Name
=
"First"
,
DependencyList
=
{
"First"
}
}.
Build
();
FileDescriptorSet
set
=
new
FileDescriptorSet
{
FileList
=
{
first
}
};
try
{
Generator
.
ConvertDescriptors
(
set
);
Generator
.
ConvertDescriptors
(
CSharpFileOptions
.
DefaultInstance
,
set
);
Assert
.
Fail
(
"Expected exception"
);
}
catch
(
DependencyResolutionException
)
{
// Expected
...
...
src/ProtoGen.Test/ProtoGen.Test.csproj
View file @
64580d09
...
...
@@ -73,10 +73,14 @@
<HintPath>
..\..\lib\Rhino.Mocks.dll
</HintPath>
</Reference>
<Reference
Include=
"System"
/>
<Reference
Include=
"System.Data"
/>
<Reference
Include=
"System.Xml"
/>
</ItemGroup>
<ItemGroup>
<Compile
Include=
"DependencyResolutionTest.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
<Compile
Include=
"TempFile.cs"
/>
<Compile
Include=
"TestPreprocessing.cs"
/>
</ItemGroup>
<ItemGroup>
<ProjectReference
Include=
"..\ProtocolBuffers\ProtocolBuffers.csproj"
>
...
...
@@ -107,6 +111,18 @@
<ProductName>
Windows Installer 3.1
</ProductName>
<Install>
true
</Install>
</BootstrapperPackage>
<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"
/>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
...
...
src/ProtoGen.Test/TempFile.cs
0 → 100644
View file @
64580d09
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
)
:
base
(
filename
,
contents
)
{
}
}
class
TempFile
:
IDisposable
{
private
string
tempFile
;
public
static
TempFile
Attach
(
string
path
)
{
return
new
TempFile
(
path
,
null
);
}
protected
TempFile
(
string
filename
,
string
contents
)
{
tempFile
=
filename
;
if
(
contents
!=
null
)
{
File
.
WriteAllText
(
tempFile
,
contents
,
new
UTF8Encoding
(
false
));
}
}
public
TempFile
(
string
contents
)
:
this
(
Path
.
GetTempFileName
(),
contents
)
{
}
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
);
}
}
}
}
src/ProtoGen.Test/TestPreprocessing.cs
0 → 100644
View file @
64580d09
#
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
System
;
using
System.Collections.Generic
;
using
System.Diagnostics
;
using
System.IO
;
using
System.Reflection
;
using
NUnit.Framework
;
namespace
Google.ProtocolBuffers.ProtoGen
{
[
TestFixture
]
[
Category
(
"Preprocessor"
)]
public
partial
class
TestPreprocessing
{
private
static
readonly
string
TempPath
=
Path
.
Combine
(
Path
.
GetTempPath
(),
"proto-gen-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
;
[
TestFixtureSetUp
]
public
virtual
void
Setup
()
{
Teardown
();
Directory
.
CreateDirectory
(
TempPath
);
Environment
.
CurrentDirectory
=
TempPath
;
}
[
TestFixtureTearDown
]
public
virtual
void
Teardown
()
{
Environment
.
CurrentDirectory
=
OriginalWorkingDirectory
;
if
(
Directory
.
Exists
(
TempPath
))
{
Directory
.
Delete
(
TempPath
,
true
);
}
}
#
endregion
#
region
Helper
Methods
RunProtoGen
/
RunCsc
void
RunProtoGen
(
int
expect
,
params
string
[]
args
)
{
TextWriter
tout
=
Console
.
Out
,
terr
=
Console
.
Error
;
StringWriter
temp
=
new
StringWriter
();
Console
.
SetOut
(
temp
);
Console
.
SetError
(
temp
);
try
{
Assert
.
AreEqual
(
expect
,
ProgramPreprocess
.
Run
(
args
),
"ProtoGen Failed: {0}"
,
temp
);
}
finally
{
Console
.
SetOut
(
tout
);
Console
.
SetError
(
terr
);
}
}
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
.
CreateNoWindow
=
true
;
psi
.
UseShellExecute
=
false
;
psi
.
RedirectStandardOutput
=
true
;
psi
.
RedirectStandardError
=
true
;
psi
.
Arguments
=
string
.
Join
(
" "
,
args
.
ToArray
());
Process
p
=
Process
.
Start
(
psi
);
p
.
WaitForExit
();
string
errorText
=
p
.
StandardOutput
.
ReadToEnd
()
+
p
.
StandardError
.
ReadToEnd
();
Assert
.
AreEqual
(
expect
,
p
.
ExitCode
,
"CSC.exe Failed: {0}"
,
errorText
);
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
))
{
RunProtoGen
(
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;
} "
))
{
RunProtoGen
(
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
))
{
RunProtoGen
(
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
))
{
RunProtoGen
(
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
))
{
RunProtoGen
(
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
))
{
RunProtoGen
(
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
TestProtoFileDisablingClsComplianceFlags
()
{
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 MyMessage {
optional uint32 name = 1;
} "
))
{
//CS3021: Warning as Error: xx does not need a CLSCompliant attribute because the assembly does not have a CLSCompliant attribute
RunProtoGen
(
0
,
proto
.
TempPath
);
RunCsc
(
1
,
source
.
TempPath
,
"/warnaserror+"
);
//Now we know it fails, make it pass by turning off cls_compliance generation
RunProtoGen
(
0
,
proto
.
TempPath
,
"-cls_compliance:false"
);
Assembly
a
=
RunCsc
(
0
,
source
.
TempPath
,
"/warnaserror+"
);
//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
))
{
RunProtoGen
(
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
))
{
RunProtoGen
(
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
))
{
RunProtoGen
(
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.
RunProtoGen
(
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
))
{
RunProtoGen
(
1
,
proto
.
TempPath
,
"-output_directory:generated-code"
);
Directory
.
CreateDirectory
(
"generated-code"
);
RunProtoGen
(
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
);
RunProtoGen
(
0
,
proto
.
TempPath
,
"-ignore_google_protobuf:true"
);
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
RunProtoGen
(
1
,
proto
.
TempPath
,
"-ignore_google_protobuf:false"
);
}
}
// *******************************************************************
// 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
RunProtoGen
(
0
,
proto
.
TempPath
,
"--include_imports"
);
Assert
.
AreEqual
(
3
,
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
);
}
}
[
Test
]
public
void
TestProtoFileWithIncludeImportsAndIgnoreGoogleProtobuf
()
{
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
);
//Even with --include_imports, if you provide -ignore_google_protobuf:true you only get the one source file
RunProtoGen
(
0
,
proto
.
TempPath
,
"-ignore_google_protobuf:true"
,
"--include_imports"
);
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
);
}
}
[
Test
]
public
void
TestProtoFileKeepingTheProtoBuffer
()
{
string
test
=
new
StackFrame
(
false
).
GetMethod
().
Name
;
Setup
();
using
(
TempFile
protobuf
=
TempFile
.
Attach
(
test
+
".pb"
))
using
(
TempFile
source
=
TempFile
.
Attach
(
test
+
".cs"
))
using
(
ProtoFile
proto
=
new
ProtoFile
(
test
+
".proto"
,
@"
package nunit.simple;
// Test a very simple message.
message MyMessage {
optional string name = 1;
} "
))
{
RunProtoGen
(
0
,
proto
.
TempPath
,
"--descriptor_set_out="
+
protobuf
.
TempPath
);
Assert
.
IsTrue
(
File
.
Exists
(
protobuf
.
TempPath
),
"Missing: "
+
protobuf
.
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
);
}
}
//Seems the --proto_path or -I option is non-functional for me. Maybe others have luck?
[
Test
,
Ignore
(
"http://code.google.com/p/protobuf/issues/detail?id=40"
)]
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
;
RunProtoGen
(
0
,
proto
.
TempPath
,
"--proto_path="
+
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;
}"
))
{
RunProtoGen
(
0
,
proto1
.
TempPath
,
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
(
TempFile
protobuf
=
TempFile
.
Attach
(
"MyMessage.pb"
))
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
RunProtoGen
(
0
,
proto1
.
TempPath
,
"--descriptor_set_out="
+
protobuf
.
TempPath
);
//build the MyMessageList proto-buffer and generate code by including MyMessage.pb
RunProtoGen
(
0
,
proto2
.
TempPath
,
protobuf
.
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
);
}
}
}
}
src/ProtoGen/DescriptorUtil.cs
View file @
64580d09
...
...
@@ -45,11 +45,26 @@ namespace Google.ProtocolBuffers.ProtoGen {
internal
static
string
GetFullUmbrellaClassName
(
IDescriptor
descriptor
)
{
CSharpFileOptions
options
=
descriptor
.
File
.
CSharpOptions
;
string
result
=
options
.
Namespace
;
if
(
result
!=
""
)
result
+=
'.'
;
result
+=
options
.
UmbrellaClassname
;
if
(
result
!=
""
)
{
result
+=
'.'
;
}
result
+=
GetQualifiedUmbrellaClassName
(
options
);
return
"global::"
+
result
;
}
/// <summary>
/// Evaluates the options and returns the qualified name of the umbrella class
/// relative to the descriptor type's namespace. Basically concatenates the
/// UmbrellaNamespace + UmbrellaClassname fields.
/// </summary>
internal
static
string
GetQualifiedUmbrellaClassName
(
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
)
{
switch
(
type
)
{
case
MappedType
.
Int32
:
return
"int"
;
...
...
src/ProtoGen/Generator.cs
View file @
64580d09
...
...
@@ -60,19 +60,30 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
public
void
Generate
()
{
List
<
FileDescriptorSet
>
descriptorProtos
=
new
List
<
FileDescriptorSet
>();
foreach
(
string
inputFile
in
options
.
InputFiles
)
{
FileDescriptorSet
descriptorProtos
;
ExtensionRegistry
extensionRegistry
=
ExtensionRegistry
.
CreateInstance
();
extensionRegistry
.
Add
(
CSharpOptions
.
CSharpFileOptions
);
extensionRegistry
.
Add
(
CSharpOptions
.
CSharpFieldOptions
);
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
());
// Combine with options from command line
foreach
(
FileDescriptor
descriptor
in
descriptors
)
{
Generate
(
descriptor
);
descriptor
.
ConfigureWithDefaultOptions
(
options
.
FileOptions
);
}
foreach
(
FileDescriptor
descriptor
in
descriptors
)
{
// Optionally exclude descriptors in google.protobuf
if
(
descriptor
.
CSharpOptions
.
IgnoreGoogleProtobuf
&&
descriptor
.
Package
==
"google.protobuf"
)
{
continue
;
}
Generate
(
descriptor
);
}
}
...
...
@@ -90,8 +101,10 @@ namespace Google.ProtocolBuffers.ProtoGen {
private
string
GetOutputFile
(
FileDescriptor
descriptor
)
{
CSharpFileOptions
fileOptions
=
descriptor
.
CSharpOptions
;
string
filename
=
descriptor
.
CSharpOptions
.
UmbrellaClassname
+
".cs"
;
string
outputDirectory
=
options
.
OutputDirectory
;
string
filename
=
descriptor
.
CSharpOptions
.
UmbrellaClassname
+
descriptor
.
CSharpOptions
.
FileExtension
;
string
outputDirectory
=
descriptor
.
CSharpOptions
.
OutputDirectory
;
if
(
fileOptions
.
ExpandNamespaceDirectories
)
{
string
package
=
fileOptions
.
Namespace
;
if
(!
string
.
IsNullOrEmpty
(
package
))
{
...
...
@@ -99,9 +112,11 @@ namespace Google.ProtocolBuffers.ProtoGen {
foreach
(
string
bit
in
bits
)
{
outputDirectory
=
Path
.
Combine
(
outputDirectory
,
bit
);
}
Directory
.
CreateDirectory
(
outputDirectory
);
}
}
// As the directory can be explicitly specified in options, we need to make sure it exists
Directory
.
CreateDirectory
(
outputDirectory
);
return
Path
.
Combine
(
outputDirectory
,
filename
);
}
...
...
@@ -111,10 +126,13 @@ namespace Google.ProtocolBuffers.ProtoGen {
/// Note: this method is internal rather than private to allow testing.
/// </summary>
/// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception>
internal
static
IList
<
FileDescriptor
>
ConvertDescriptors
(
FileDescriptorSet
descriptorProtos
)
{
internal
static
IList
<
FileDescriptor
>
ConvertDescriptors
(
CSharpFileOptions
options
,
params
FileDescriptorSet
[]
descriptorProtos
)
{
// 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
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
];
Dictionary
<
string
,
FileDescriptor
>
convertedMap
=
new
Dictionary
<
string
,
FileDescriptor
>();
...
...
@@ -131,9 +149,28 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
FileDescriptorProto
candidate
=
fileList
[
i
];
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
;
for
(
int
j
=
0
;
j
<
dependencies
.
Length
;
j
++)
{
if
(!
convertedMap
.
TryGetValue
(
candidate
.
DependencyList
[
j
],
out
dependencies
[
j
]))
{
// 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
;
break
;
}
...
...
src/ProtoGen/GeneratorOptions.cs
View file @
64580d09
#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/
...
...
@@ -30,13 +31,17 @@
// 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
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Text.RegularExpressions
;
using
Google.ProtocolBuffers.DescriptorProtos
;
using
Google.ProtocolBuffers.Descriptors
;
namespace
Google.ProtocolBuffers.ProtoGen
{
/// <summary>
/// All the configuration required for the generator - where to generate
/// output files, the location of input files etc. While this isn't immutable
...
...
@@ -44,8 +49,6 @@ namespace Google.ProtocolBuffers.ProtoGen {
/// the generator.
/// </summary>
public
sealed
class
GeneratorOptions
{
public
string
OutputDirectory
{
get
;
set
;
}
public
IList
<
string
>
InputFiles
{
get
;
set
;
}
/// <summary>
...
...
@@ -58,19 +61,23 @@ namespace Google.ProtocolBuffers.ProtoGen {
public
bool
TryValidate
(
out
IList
<
string
>
reasons
)
{
List
<
string
>
tmpReasons
=
new
List
<
string
>();
ParseArguments
(
tmpReasons
);
// Output directory validation
if
(
string
.
IsNullOrEmpty
(
OutputDirectory
))
{
if
(
string
.
IsNullOrEmpty
(
FileOptions
.
OutputDirectory
))
{
tmpReasons
.
Add
(
"No output directory specified"
);
}
else
{
if
(!
Directory
.
Exists
(
OutputDirectory
))
{
tmpReasons
.
Add
(
"Specified output directory ("
+
OutputDirectory
+
" doesn't exist."
);
}
else
{
if
(!
Directory
.
Exists
(
FileOptions
.
OutputDirectory
))
{
tmpReasons
.
Add
(
"Specified output directory ("
+
FileOptions
.
OutputDirectory
+
" doesn't exist."
);
}
}
// Input file validation (just in terms of presence)
if
(
InputFiles
==
null
||
InputFiles
.
Count
==
0
)
{
tmpReasons
.
Add
(
"No input files specified"
);
}
else
{
}
else
{
foreach
(
string
input
in
InputFiles
)
{
FileInfo
fi
=
new
FileInfo
(
input
);
if
(!
fi
.
Exists
)
{
...
...
@@ -99,5 +106,161 @@ namespace Google.ProtocolBuffers.ProtoGen {
throw
new
InvalidOptionsException
(
reasons
);
}
}
// Raw arguments, used to provide defaults for proto file options
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>.*)$"
);
private
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
;
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
;
}
}
}
\ No newline at end of file
src/ProtoGen/Program.cs
View file @
64580d09
#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/
...
...
@@ -30,6 +31,7 @@
// 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
System
;
...
...
@@ -40,12 +42,12 @@ namespace Google.ProtocolBuffers.ProtoGen {
/// <summary>
/// Entry point for the Protocol Buffers generator.
/// </summary>
class
Program
{
static
int
Main
(
string
[]
args
)
{
internal
class
Program
{
internal
static
int
Main
(
string
[]
args
)
{
try
{
// Hack to make sure everything's initialized
DescriptorProtoFile
.
Descriptor
.
ToString
();
GeneratorOptions
options
=
ParseCommandLineArguments
(
args
)
;
GeneratorOptions
options
=
new
GeneratorOptions
{
Arguments
=
args
}
;
IList
<
string
>
validationFailures
;
if
(!
options
.
TryValidate
(
out
validationFailures
))
{
...
...
@@ -58,22 +60,13 @@ namespace Google.ProtocolBuffers.ProtoGen {
Generator
generator
=
Generator
.
CreateGenerator
(
options
);
generator
.
Generate
();
return
0
;
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
Console
.
Error
.
WriteLine
(
"Error: {0}"
,
e
.
Message
);
Console
.
Error
.
WriteLine
();
Console
.
Error
.
WriteLine
(
"Detailed exception information: {0}"
,
e
);
return
1
;
}
}
private
static
GeneratorOptions
ParseCommandLineArguments
(
string
[]
args
)
{
GeneratorOptions
options
=
new
GeneratorOptions
();
//string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers";
//options.OutputDirectory = baseDir + "\\tmp";
//options.InputFiles = new[] { baseDir + "\\protos\\nwind-solo.protobin" };
options
.
OutputDirectory
=
"."
;
options
.
InputFiles
=
args
;
return
options
;
}
}
}
\ No newline at end of file
src/ProtoGen/ProgramPreprocess.cs
0 → 100644
View file @
64580d09
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
{
private
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
;
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
;
}
}
}
src/ProtoGen/ProtoGen.csproj
View file @
64580d09
...
...
@@ -34,6 +34,7 @@
<IsWebBootstrapper>
false
</IsWebBootstrapper>
<UseApplicationTrust>
false
</UseApplicationTrust>
<BootstrapperEnabled>
true
</BootstrapperEnabled>
<StartupObject>
Google.ProtocolBuffers.ProtoGen.ProgramPreprocess
</StartupObject>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "
>
<DebugSymbols>
true
</DebugSymbols>
...
...
@@ -79,6 +80,7 @@
<Compile
Include=
"MessageFieldGenerator.cs"
/>
<Compile
Include=
"MessageGenerator.cs"
/>
<Compile
Include=
"PrimitiveFieldGenerator.cs"
/>
<Compile
Include=
"ProgramPreprocess.cs"
/>
<Compile
Include=
"RepeatedEnumFieldGenerator.cs"
/>
<Compile
Include=
"RepeatedMessageFieldGenerator.cs"
/>
<Compile
Include=
"RepeatedPrimitiveFieldGenerator.cs"
/>
...
...
src/ProtoGen/ServiceGenerator.cs
View file @
64580d09
...
...
@@ -61,7 +61,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
writer
.
WriteLine
();
writer
.
WriteLine
(
"{0} static pbd::ServiceDescriptor Descriptor {{"
,
ClassAccessLevel
);
writer
.
WriteLine
(
" get {{ return {0}.Descriptor.Services[{1}]; }}"
,
Descriptor
.
File
.
CSharpOptions
.
UmbrellaClassname
,
Descriptor
.
Index
);
Descriptor
Util
.
GetQualifiedUmbrellaClassName
(
Descriptor
.
File
.
CSharpOptions
)
,
Descriptor
.
Index
);
writer
.
WriteLine
(
"}"
);
writer
.
WriteLine
(
"{0} pbd::ServiceDescriptor DescriptorForType {{"
,
ClassAccessLevel
);
writer
.
WriteLine
(
" get { return Descriptor; }"
);
...
...
src/ProtoGen/UmbrellaClassGenerator.cs
View file @
64580d09
...
...
@@ -92,6 +92,12 @@ namespace Google.ProtocolBuffers.ProtoGen {
if
(!
Descriptor
.
CSharpOptions
.
NestClasses
)
{
writer
.
Outdent
();
writer
.
WriteLine
(
"}"
);
// 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
,
"Messages"
,
Descriptor
.
MessageTypes
);
...
...
@@ -116,6 +122,13 @@ namespace Google.ProtocolBuffers.ProtoGen {
writer
.
Indent
();
writer
.
WriteLine
();
}
// 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
)
{
writer
.
WriteLine
(
"[global::System.Diagnostics.Contracts.ContractVerificationAttribute(false)]"
);
}
...
...
src/ProtocolBuffers.Test/DynamicMessageTest.cs
View file @
64580d09
src/ProtocolBuffers.sln
View file @
64580d09
...
...
@@ -17,6 +17,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoBench", "ProtoBench\Pr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoDump", "ProtoDump\ProtoDump.csproj", "{D7282E99-2DC3-405B-946F-177DB2FD2AE2}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
...
...
src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs
View file @
64580d09
...
...
@@ -37,23 +37,26 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
byte
[]
descriptorData
=
global
::
System
.
Convert
.
FromBase64String
(
"CiRnb29nbGUvcHJvdG9idWYvY3NoYXJwX29wdGlvbnMucHJvdG8SD2dvb2ds"
+
"ZS5wcm90b2J1ZhogZ29vZ2xlL3Byb3RvYnVmL2Rlc2NyaXB0b3IucHJvdG8i"
+
"6
gE
KEUNTaGFycEZpbGVPcHRpb25zEhEKCW5hbWVzcGFjZRgBIAEoCRIaChJ1"
+
"6
wI
KEUNTaGFycEZpbGVPcHRpb25zEhEKCW5hbWVzcGFjZRgBIAEoCRIaChJ1"
+
"bWJyZWxsYV9jbGFzc25hbWUYAiABKAkSHAoOcHVibGljX2NsYXNzZXMYAyAB"
+
"KAg6BHRydWUSFgoObXVsdGlwbGVfZmlsZXMYBCABKAgSFAoMbmVzdF9jbGFz"
+
"c2VzGAUgASgIEhYKDmNvZGVfY29udHJhY3RzGAYgASgIEiQKHGV4cGFuZF9u"
+
"YW1lc3BhY2VfZGlyZWN0b3JpZXMYByABKAgSHAoOY2xzX2NvbXBsaWFuY2UY"
+
"CCABKAg6BHRydWUiKwoSQ1NoYXJwRmllbGRPcHRpb25zEhUKDXByb3BlcnR5"
+
"X25hbWUYASABKAk6XgoTY3NoYXJwX2ZpbGVfb3B0aW9ucxIcLmdvb2dsZS5w"
+
"cm90b2J1Zi5GaWxlT3B0aW9ucxjoByABKAsyIi5nb29nbGUucHJvdG9idWYu"
+
"Q1NoYXJwRmlsZU9wdGlvbnM6YQoUY3NoYXJwX2ZpZWxkX29wdGlvbnMSHS5n"
+
"b29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zGOgHIAEoCzIjLmdvb2dsZS5w"
+
"cm90b2J1Zi5DU2hhcnBGaWVsZE9wdGlvbnM="
);
"CCABKAg6BHRydWUSHAoOZmlsZV9leHRlbnNpb24Y3QEgASgJOgMuY3MSGwoS"
+
"dW1icmVsbGFfbmFtZXNwYWNlGN4BIAEoCRIcChBvdXRwdXRfZGlyZWN0b3J5"
+
"GN8BIAEoCToBLhImChZpZ25vcmVfZ29vZ2xlX3Byb3RvYnVmGOABIAEoCDoF"
+
"ZmFsc2UiKwoSQ1NoYXJwRmllbGRPcHRpb25zEhUKDXByb3BlcnR5X25hbWUY"
+
"ASABKAk6XgoTY3NoYXJwX2ZpbGVfb3B0aW9ucxIcLmdvb2dsZS5wcm90b2J1"
+
"Zi5GaWxlT3B0aW9ucxjoByABKAsyIi5nb29nbGUucHJvdG9idWYuQ1NoYXJw"
+
"RmlsZU9wdGlvbnM6YQoUY3NoYXJwX2ZpZWxkX29wdGlvbnMSHS5nb29nbGUu"
+
"cHJvdG9idWYuRmllbGRPcHRpb25zGOgHIAEoCzIjLmdvb2dsZS5wcm90b2J1"
+
"Zi5DU2hhcnBGaWVsZE9wdGlvbnM="
);
pbd
::
FileDescriptor
.
InternalDescriptorAssigner
assigner
=
delegate
(
pbd
::
FileDescriptor
root
)
{
descriptor
=
root
;
internal__static_google_protobuf_CSharpFileOptions__Descriptor
=
Descriptor
.
MessageTypes
[
0
];
internal__static_google_protobuf_CSharpFileOptions__FieldAccessorTable
=
new
pb
::
FieldAccess
.
FieldAccessorTable
<
global
::
Google
.
ProtocolBuffers
.
DescriptorProtos
.
CSharpFileOptions
,
global
::
Google
.
ProtocolBuffers
.
DescriptorProtos
.
CSharpFileOptions
.
Builder
>(
internal__static_google_protobuf_CSharpFileOptions__Descriptor
,
new
string
[]
{
"Namespace"
,
"UmbrellaClassname"
,
"PublicClasses"
,
"MultipleFiles"
,
"NestClasses"
,
"CodeContracts"
,
"ExpandNamespaceDirectories"
,
"ClsCompliance"
,
});
new
string
[]
{
"Namespace"
,
"UmbrellaClassname"
,
"PublicClasses"
,
"MultipleFiles"
,
"NestClasses"
,
"CodeContracts"
,
"ExpandNamespaceDirectories"
,
"ClsCompliance"
,
"FileExtension"
,
"UmbrellaNamespace"
,
"OutputDirectory"
,
"IgnoreGoogleProtobuf"
,
});
internal__static_google_protobuf_CSharpFieldOptions__Descriptor
=
Descriptor
.
MessageTypes
[
1
];
internal__static_google_protobuf_CSharpFieldOptions__FieldAccessorTable
=
new
pb
::
FieldAccess
.
FieldAccessorTable
<
global
::
Google
.
ProtocolBuffers
.
DescriptorProtos
.
CSharpFieldOptions
,
global
::
Google
.
ProtocolBuffers
.
DescriptorProtos
.
CSharpFieldOptions
.
Builder
>(
internal__static_google_protobuf_CSharpFieldOptions__Descriptor
,
...
...
@@ -173,6 +176,46 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
get
{
return
clsCompliance_
;
}
}
public
const
int
FileExtensionFieldNumber
=
221
;
private
bool
hasFileExtension
;
private
string
fileExtension_
=
".cs"
;
public
bool
HasFileExtension
{
get
{
return
hasFileExtension
;
}
}
public
string
FileExtension
{
get
{
return
fileExtension_
;
}
}
public
const
int
UmbrellaNamespaceFieldNumber
=
222
;
private
bool
hasUmbrellaNamespace
;
private
string
umbrellaNamespace_
=
""
;
public
bool
HasUmbrellaNamespace
{
get
{
return
hasUmbrellaNamespace
;
}
}
public
string
UmbrellaNamespace
{
get
{
return
umbrellaNamespace_
;
}
}
public
const
int
OutputDirectoryFieldNumber
=
223
;
private
bool
hasOutputDirectory
;
private
string
outputDirectory_
=
"."
;
public
bool
HasOutputDirectory
{
get
{
return
hasOutputDirectory
;
}
}
public
string
OutputDirectory
{
get
{
return
outputDirectory_
;
}
}
public
const
int
IgnoreGoogleProtobufFieldNumber
=
224
;
private
bool
hasIgnoreGoogleProtobuf
;
private
bool
ignoreGoogleProtobuf_
=
false
;
public
bool
HasIgnoreGoogleProtobuf
{
get
{
return
hasIgnoreGoogleProtobuf
;
}
}
public
bool
IgnoreGoogleProtobuf
{
get
{
return
ignoreGoogleProtobuf_
;
}
}
public
override
bool
IsInitialized
{
get
{
return
true
;
...
...
@@ -205,6 +248,18 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if
(
HasClsCompliance
)
{
output
.
WriteBool
(
8
,
ClsCompliance
);
}
if
(
HasFileExtension
)
{
output
.
WriteString
(
221
,
FileExtension
);
}
if
(
HasUmbrellaNamespace
)
{
output
.
WriteString
(
222
,
UmbrellaNamespace
);
}
if
(
HasOutputDirectory
)
{
output
.
WriteString
(
223
,
OutputDirectory
);
}
if
(
HasIgnoreGoogleProtobuf
)
{
output
.
WriteBool
(
224
,
IgnoreGoogleProtobuf
);
}
UnknownFields
.
WriteTo
(
output
);
}
...
...
@@ -239,6 +294,18 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if
(
HasClsCompliance
)
{
size
+=
pb
::
CodedOutputStream
.
ComputeBoolSize
(
8
,
ClsCompliance
);
}
if
(
HasFileExtension
)
{
size
+=
pb
::
CodedOutputStream
.
ComputeStringSize
(
221
,
FileExtension
);
}
if
(
HasUmbrellaNamespace
)
{
size
+=
pb
::
CodedOutputStream
.
ComputeStringSize
(
222
,
UmbrellaNamespace
);
}
if
(
HasOutputDirectory
)
{
size
+=
pb
::
CodedOutputStream
.
ComputeStringSize
(
223
,
OutputDirectory
);
}
if
(
HasIgnoreGoogleProtobuf
)
{
size
+=
pb
::
CodedOutputStream
.
ComputeBoolSize
(
224
,
IgnoreGoogleProtobuf
);
}
size
+=
UnknownFields
.
SerializedSize
;
memoizedSerializedSize
=
size
;
return
size
;
...
...
@@ -355,6 +422,18 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if
(
other
.
HasClsCompliance
)
{
ClsCompliance
=
other
.
ClsCompliance
;
}
if
(
other
.
HasFileExtension
)
{
FileExtension
=
other
.
FileExtension
;
}
if
(
other
.
HasUmbrellaNamespace
)
{
UmbrellaNamespace
=
other
.
UmbrellaNamespace
;
}
if
(
other
.
HasOutputDirectory
)
{
OutputDirectory
=
other
.
OutputDirectory
;
}
if
(
other
.
HasIgnoreGoogleProtobuf
)
{
IgnoreGoogleProtobuf
=
other
.
IgnoreGoogleProtobuf
;
}
this
.
MergeUnknownFields
(
other
.
UnknownFields
);
return
this
;
}
...
...
@@ -419,6 +498,22 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
ClsCompliance
=
input
.
ReadBool
();
break
;
}
case
1770
:
{
FileExtension
=
input
.
ReadString
();
break
;
}
case
1778
:
{
UmbrellaNamespace
=
input
.
ReadString
();
break
;
}
case
1786
:
{
OutputDirectory
=
input
.
ReadString
();
break
;
}
case
1792
:
{
IgnoreGoogleProtobuf
=
input
.
ReadBool
();
break
;
}
}
}
}
...
...
@@ -569,6 +664,81 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
result
.
clsCompliance_
=
true
;
return
this
;
}
public
bool
HasFileExtension
{
get
{
return
result
.
HasFileExtension
;
}
}
public
string
FileExtension
{
get
{
return
result
.
FileExtension
;
}
set
{
SetFileExtension
(
value
);
}
}
public
Builder
SetFileExtension
(
string
value
)
{
pb
::
ThrowHelper
.
ThrowIfNull
(
value
,
"value"
);
result
.
hasFileExtension
=
true
;
result
.
fileExtension_
=
value
;
return
this
;
}
public
Builder
ClearFileExtension
()
{
result
.
hasFileExtension
=
false
;
result
.
fileExtension_
=
".cs"
;
return
this
;
}
public
bool
HasUmbrellaNamespace
{
get
{
return
result
.
HasUmbrellaNamespace
;
}
}
public
string
UmbrellaNamespace
{
get
{
return
result
.
UmbrellaNamespace
;
}
set
{
SetUmbrellaNamespace
(
value
);
}
}
public
Builder
SetUmbrellaNamespace
(
string
value
)
{
pb
::
ThrowHelper
.
ThrowIfNull
(
value
,
"value"
);
result
.
hasUmbrellaNamespace
=
true
;
result
.
umbrellaNamespace_
=
value
;
return
this
;
}
public
Builder
ClearUmbrellaNamespace
()
{
result
.
hasUmbrellaNamespace
=
false
;
result
.
umbrellaNamespace_
=
""
;
return
this
;
}
public
bool
HasOutputDirectory
{
get
{
return
result
.
HasOutputDirectory
;
}
}
public
string
OutputDirectory
{
get
{
return
result
.
OutputDirectory
;
}
set
{
SetOutputDirectory
(
value
);
}
}
public
Builder
SetOutputDirectory
(
string
value
)
{
pb
::
ThrowHelper
.
ThrowIfNull
(
value
,
"value"
);
result
.
hasOutputDirectory
=
true
;
result
.
outputDirectory_
=
value
;
return
this
;
}
public
Builder
ClearOutputDirectory
()
{
result
.
hasOutputDirectory
=
false
;
result
.
outputDirectory_
=
"."
;
return
this
;
}
public
bool
HasIgnoreGoogleProtobuf
{
get
{
return
result
.
HasIgnoreGoogleProtobuf
;
}
}
public
bool
IgnoreGoogleProtobuf
{
get
{
return
result
.
IgnoreGoogleProtobuf
;
}
set
{
SetIgnoreGoogleProtobuf
(
value
);
}
}
public
Builder
SetIgnoreGoogleProtobuf
(
bool
value
)
{
result
.
hasIgnoreGoogleProtobuf
=
true
;
result
.
ignoreGoogleProtobuf_
=
value
;
return
this
;
}
public
Builder
ClearIgnoreGoogleProtobuf
()
{
result
.
hasIgnoreGoogleProtobuf
=
false
;
result
.
ignoreGoogleProtobuf_
=
false
;
return
this
;
}
}
static
CSharpFileOptions
()
{
object
.
ReferenceEquals
(
global
::
Google
.
ProtocolBuffers
.
DescriptorProtos
.
CSharpOptions
.
Descriptor
,
null
);
...
...
src/ProtocolBuffers/Descriptors/FileDescriptor.cs
View file @
64580d09
...
...
@@ -32,17 +32,17 @@
using
System
;
using
System.Collections.Generic
;
using
System.Collections.ObjectModel
;
using
System.IO
;
using
Google.ProtocolBuffers.DescriptorProtos
;
using
FileOptions
=
Google
.
ProtocolBuffers
.
DescriptorProtos
.
FileOptions
;
namespace
Google.ProtocolBuffers.Descriptors
{
/// <summary>
/// Describes a .proto file, including everything defined within.
/// IDescriptor is implemented such that the File property returns this descriptor,
/// and the FullName is the same as the Name.
/// </summary>
public
sealed
class
FileDescriptor
:
IDescriptor
<
FileDescriptorProto
>
{
private
FileDescriptorProto
proto
;
private
readonly
IList
<
MessageDescriptor
>
messageTypes
;
private
readonly
IList
<
EnumDescriptor
>
enumTypes
;
...
...
@@ -61,33 +61,59 @@ namespace Google.ProtocolBuffers.Descriptors {
pool
.
AddPackage
(
Package
,
this
);
messageTypes
=
DescriptorUtil
.
ConvertAndMakeReadOnly
(
proto
.
MessageTypeList
,
(
message
,
index
)
=>
new
MessageDescriptor
(
message
,
this
,
null
,
index
));
(
message
,
index
)
=>
new
MessageDescriptor
(
message
,
this
,
null
,
index
));
enumTypes
=
DescriptorUtil
.
ConvertAndMakeReadOnly
(
proto
.
EnumTypeList
,
(
enumType
,
index
)
=>
new
EnumDescriptor
(
enumType
,
this
,
null
,
index
));
(
enumType
,
index
)
=>
new
EnumDescriptor
(
enumType
,
this
,
null
,
index
));
services
=
DescriptorUtil
.
ConvertAndMakeReadOnly
(
proto
.
ServiceList
,
(
service
,
index
)
=>
new
ServiceDescriptor
(
service
,
this
,
index
));
extensions
=
DescriptorUtil
.
ConvertAndMakeReadOnly
(
proto
.
ExtensionList
,
(
field
,
index
)
=>
new
FieldDescriptor
(
field
,
this
,
null
,
index
,
true
));
(
field
,
index
)
=>
new
FieldDescriptor
(
field
,
this
,
null
,
index
,
true
));
}
/// <summary>
/// Allows a file descriptor to be configured with a set of external options, e.g. from the
/// command-line arguments to protogen.
/// </summary>
internal
void
ConfigureWithDefaultOptions
(
CSharpFileOptions
options
)
{
csharpFileOptions
=
BuildOrFakeWithDefaultOptions
(
options
);
}
private
CSharpFileOptions
BuildOrFakeCSharpOptions
()
{
// TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues
if
(
proto
.
Name
==
"google/protobuf/descriptor.proto"
)
{
private
CSharpFileOptions
BuildOrFakeWithDefaultOptions
(
CSharpFileOptions
defaultOptions
)
{
// Fix for being able to relocate these files to any directory structure
if
(
proto
.
Package
==
"google.protobuf"
)
{
string
filename
=
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
{
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
();
}
if
(
proto
.
Name
==
"google/protobuf/
csharp_options.proto"
)
{
if
(
filename
==
"
csharp_options.proto"
)
{
return
new
CSharpFileOptions
.
Builder
{
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
();
}
CSharpFileOptions
.
Builder
builder
=
CSharpFileOptions
.
CreateBuilder
();
}
CSharpFileOptions
.
Builder
builder
=
defaultOptions
.
ToBuilder
();
if
(
proto
.
Options
.
HasExtension
(
DescriptorProtos
.
CSharpOptions
.
CSharpFileOptions
))
{
builder
.
MergeFrom
(
proto
.
Options
.
GetExtension
(
DescriptorProtos
.
CSharpOptions
.
CSharpFileOptions
));
}
...
...
@@ -99,6 +125,25 @@ namespace Google.ProtocolBuffers.Descriptors {
string
baseName
=
Name
.
Substring
(
lastSlash
+
1
);
builder
.
UmbrellaClassname
=
NameHelpers
.
UnderscoresToPascalCase
(
NameHelpers
.
StripProto
(
baseName
));
}
// 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 explicitly anyway.
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
();
}
...
...
@@ -110,7 +155,7 @@ namespace Google.ProtocolBuffers.Descriptors {
}
/// <value>
/// The <see cref="FileOptions" /> defined in <c>descriptor.proto</c>.
/// The <see cref="
DescriptorProtos.
FileOptions" /> defined in <c>descriptor.proto</c>.
/// </value>
public
FileOptions
Options
{
get
{
return
proto
.
Options
;
}
...
...
@@ -124,7 +169,7 @@ namespace Google.ProtocolBuffers.Descriptors {
get
{
lock
(
optionsLock
)
{
if
(
csharpFileOptions
==
null
)
{
csharpFileOptions
=
BuildOrFake
CSharpOptions
(
);
csharpFileOptions
=
BuildOrFake
WithDefaultOptions
(
CSharpFileOptions
.
DefaultInstance
);
}
}
return
csharpFileOptions
;
...
...
@@ -320,19 +365,21 @@ namespace Google.ProtocolBuffers.Descriptors {
public
delegate
ExtensionRegistry
InternalDescriptorAssigner
(
FileDescriptor
descriptor
);
public
static
FileDescriptor
InternalBuildGeneratedFileFrom
(
byte
[]
descriptorData
,
FileDescriptor
[]
dependencies
,
InternalDescriptorAssigner
descriptorAssigner
)
{
FileDescriptor
[]
dependencies
,
InternalDescriptorAssigner
descriptorAssigner
)
{
FileDescriptorProto
proto
;
try
{
proto
=
FileDescriptorProto
.
ParseFrom
(
descriptorData
);
}
catch
(
InvalidProtocolBufferException
e
)
{
}
catch
(
InvalidProtocolBufferException
e
)
{
throw
new
ArgumentException
(
"Failed to parse protocol buffer descriptor for generated code."
,
e
);
}
FileDescriptor
result
;
try
{
result
=
BuildFrom
(
proto
,
dependencies
);
}
catch
(
DescriptorValidationException
e
)
{
}
catch
(
DescriptorValidationException
e
)
{
throw
new
ArgumentException
(
"Invalid embedded descriptor for \""
+
proto
.
Name
+
"\"."
,
e
);
}
...
...
@@ -342,7 +389,8 @@ namespace Google.ProtocolBuffers.Descriptors {
// We must re-parse the proto using the registry.
try
{
proto
=
FileDescriptorProto
.
ParseFrom
(
descriptorData
,
registry
);
}
catch
(
InvalidProtocolBufferException
e
)
{
}
catch
(
InvalidProtocolBufferException
e
)
{
throw
new
ArgumentException
(
"Failed to parse protocol buffer descriptor for generated code."
,
e
);
}
...
...
@@ -351,7 +399,6 @@ namespace Google.ProtocolBuffers.Descriptors {
return
result
;
}
/// <summary>
/// Replace our FileDescriptorProto with the given one, which is
/// identical except that it might contain extensions that weren't present
...
...
src/ProtocolBuffers/DynamicMessage.cs
View file @
64580d09
src/ProtocolBuffers/UnknownField.cs
View file @
64580d09
todo.txt
View file @
64580d09
Current task list (not in order)
- Optionally remove dependencies to csharp options
- Remove multifile support
- Docs
- Clean up protogen code
- Add flags to protogen
- Avoid using reflection for messages which don't need it (is this
possible?)
- Bring service generation into line with Java
...
...
@@ -14,7 +12,5 @@ Current task list (not in order)
- Reformat code
- Change generated format
- Add regions to copyright
- Investigate command line parsing library
- Investigate calling protoc directly
- Build and publish binaries
- Work out why the Compact Framework 3.5 build fails under VS2010
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment