c# - protobuffers - protocol buffer list
Buffers de protocolo en proyectos C#usando protobuf-net-mejores prácticas para la generación de código (6)
Estoy tratando de usar protobuf en un proyecto de C #, usando protobuf-net, y me pregunto cuál es la mejor manera de organizar esto en una estructura de proyecto de Visual Studio.
Cuando se usa manualmente la herramienta de protogen para generar código en C #, la vida parece fácil pero no se siente bien.
Me gustaría que el archivo .proto se considere el archivo principal de código fuente, generando archivos C # como un subproducto, pero antes de que el compilador C # se involucre.
Las opciones parecen ser:
- Herramienta personalizada para herramientas de proto (aunque no puedo ver por dónde empezar)
- Paso previo a la compilación (llamar a protogen o un archivo por lotes que lo haga)
He luchado con 2) arriba ya que me sigue dando "El sistema no puede encontrar el archivo especificado" a menos que use rutas absolutas (y no me gusta forzar que los proyectos se ubiquen explícitamente).
¿Hay una convención (todavía) para esto?
Editar: Basado en los comentarios de @ jon, volví a intentar el método de paso previo a la creación y usé esto (la ubicación del protocolo está codificada por ahora), usando el ejemplo de la libreta de direcciones de Google:
c:/bin/protobuf/protogen "-i:$(ProjectDir)AddressBook.proto"
"-o:$(ProjectDir)AddressBook.cs" -t:c:/bin/protobuf/csharp.xslt
Edit2: Tomando la recomendación de @ jon para minimizar el tiempo de construcción al no procesar los archivos .proto si no han cambiado, he reunido una herramienta básica para verificarme (esto probablemente podría ampliarse a una herramienta completa de Custom-Build )
using System;
using System.Diagnostics;
using System.IO;
namespace PreBuildChecker
{
public class Checker
{
static int Main(string[] args)
{
try
{
Check(args);
return 0;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return 1;
}
}
public static void Check(string[] args)
{
if (args.Length < 3)
{
throw new ArgumentException(
"Command line must be supplied with source, target and command-line [plus options]");
}
string source = args[0];
string target = args[1];
string executable = args[2];
string arguments = args.Length > 3 ? GetCommandLine(args) : null;
FileInfo targetFileInfo = new FileInfo(target);
FileInfo sourceFileInfo = new FileInfo(source);
if (!sourceFileInfo.Exists)
{
throw new ArgumentException(string.Format(
"Source file {0} not found", source));
}
if (!targetFileInfo.Exists ||
sourceFileInfo.LastWriteTimeUtc > targetFileInfo.LastAccessTimeUtc)
{
Process process = new Process();
process.StartInfo.FileName = executable;
process.StartInfo.Arguments = arguments;
process.StartInfo.ErrorDialog = true;
Console.WriteLine(string.Format(
"Source newer than target, launching tool: {0} {1}",
executable,
arguments));
process.Start();
}
}
private static string GetCommandLine(string[] args)
{
string[] arguments = new string[args.Length - 3];
Array.Copy(args, 3, arguments, 0, arguments.Length);
return String.Join(" ", arguments);
}
}
}
Mi comando pre-build es ahora (todo en una línea):
$(SolutionDir)PreBuildChecker/$(OutDir)PreBuildChecker
$(ProjectDir)AddressBook.proto
$(ProjectDir)AddressBook.cs
c:/bin/protobuf/protogen
"-i:$(ProjectDir)AddressBook.proto"
"-o:$(ProjectDir)AddressBook.cs"
-t:c:/bin/protobuf/csharp.xslt
Llamar a un paso previo a la construcción, pero usando variables de proyecto (por ejemplo, $(ProjectPath)
) para crear nombres de archivo absolutos sin tenerlos realmente en su solución, me parecería una apuesta razonable.
Una cosa que quizás quiera considerar, basada en mi experiencia pasada de generadores de código: es posible que desee escribir un contenedor para protogen que genere código en una ubicación diferente, luego verifique si el código generado recientemente es el mismo que el código anterior, y no lo sobrescribe si es así. De esta forma, Visual Studio se dará cuenta de que nada ha cambiado y no forzará la reconstrucción de ese proyecto; esto ha reducido drásticamente los tiempos de construcción en el pasado.
Alternativamente, podría mantener un hash md5 del archivo .proto la última vez que se ejecutó protogen, y solo ejecutar protogen si el archivo .proto ha cambiado, ¡aún menos para hacer en cada compilación!
Sin embargo, gracias por plantear esto como una pregunta, sugiere claramente que debería encontrar la forma de hacer de este un paso fácil de preconstrucción para mi propio puerto.
He adjuntado un contenedor rápido y sucio de Visual Studio Custom Tool alrededor de ProtoGen.exe a la página de Google Code para este problema ( http://code.google.com/p/protobuf-net/issues/detail?id=39 ). Esto hace que agregar archivos .proto a proyectos C # sea extremadamente fácil.
Consulte el archivo Léame en el archivo adjunto para obtener más información.
bueno, eso me dio una idea (algo sobre reinventar la rueda) ...
- crear Makefile.mak simple, algo así como
.SUFFIXES : .cs .proto .proto.cs: protogen/protogen.exe -i:$? -o:$@ -t:protogen/csharp.xlst
(obviamente, no te olvides de reemplazar las rutas a protogen y csharp.xlst). IMPORTANTE: el comando protogen / protogen.exe a partir del carácter TAB, no 8 espacios
- Si no desea especificar archivos necesarios para compilar todo el tiempo, puede usar algo como
.SUFFIXES : .cs .proto all: mycs1.cs myotherfile.cs .proto.cs: protogen/protogen.exe -i:$? -o:$@ -t:protogen/csharp.xlst
- en el paso previo a la construcción para agregar
cd $(ProjectDir) && "$(DevEnvDir)../../vc/bin/nmake" /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs
o, si tiene nmake en su camino, uno puede usar
cd $(ProjectDir) && nmake /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs
Agregue el siguiente evento de precompilación a la configuración de su proyecto para generar el archivo C # solo cuando el archivo .proto haya cambiado. Simplemente reemplace YourFile
con el nombre del nombre base de su archivo .proto.
cd $(ProjectDir) && powershell -Command if (!(Test-Path YourFile.proto.cs) -or (Get-Item YourFile.proto).LastWriteTimeUtc -gt (Get-Item YourFile.proto.cs).LastWriteTimeUtc) { PathToProtoGen/protogen -i:YourFile.proto -o:YourFile.proto.cs }
Esto funciona en cualquier versión reciente de Visual Studio, a diferencia de la herramienta de creación personalizada de protobuf-net, que no es compatible con Visual Studio 2012 o Visual Studio 2013, según los números 338 y 413 .
Agregue esto al archivo de proyecto relevante.
Ventaja, construcción incremental.
Desventaja, necesita editar manualmente al agregar archivos.
<ItemGroup>
<Proto Include="Person.proto" />
<Compile Include="Person.cs">
<DependentUpon>Person.proto</DependentUpon>
</Compile>
</ItemGroup>
<PropertyGroup>
<CompileDependsOn>ProtobufGenerate;$(CompileDependsOn)</CompileDependsOn>
</PropertyGroup>
<Target Name="ProtobufGenerate" Inputs="@(Proto)" Outputs="@(Proto->''$(ProjectDir)%(Filename).cs'')">
<ItemGroup>
<_protoc Include="../packages/Google.Protobuf.*/tools/protoc.exe" />
</ItemGroup>
<Error Condition="!Exists(@(_protoc))" Text="Could not find protoc.exe" />
<Exec Command=""@(_protoc)" "--csharp_out=$(ProjectDir.TrimEnd(''/'))" @(Proto->''%(Identity)'','' '')" WorkingDirectory="$(ProjectDir)" />
</Target>
Como una extensión del código de Shaun, me complace anunciar que protobuf-net ahora cuenta con la integración de Visual Studio a través de una herramienta personalizada. El instalador msi está disponible desde la página del proyecto . Información más completa aquí: protobuf-net; ahora con orcas añadidas