Buffer di protocollo nei progetti C # usando protobuf-net - best practice per la generazione di codice

StackOverflow https://stackoverflow.com/questions/453820

Domanda

Sto cercando di usare protobuf in un progetto C #, usando protobuf-net, e mi chiedo quale sia il modo migliore per organizzarlo in una struttura di progetto di Visual Studio.

Quando si utilizza manualmente lo strumento protogen per generare codice in C #, la vita sembra facile ma non sembra corretta.

Vorrei che il file .proto fosse considerato il file di codice sorgente primario, generando i file C # come sottoprodotto, ma prima che il compilatore C # venisse coinvolto.

Le opzioni sembrano essere:

  1. Strumento personalizzato per strumenti proto (anche se non riesco a vedere da dove iniziare)
  2. Fase pre-build (chiamata protogen o un file batch che lo fa)

Ho lottato con 2) sopra perché continua a darmi " Il sistema non riesce a trovare il file specificato " a meno che non utilizzi percorsi assoluti (e non mi piace forzare la localizzazione esplicita dei progetti).

C'è una convenzione (ancora) per questo?


Modifica: Sulla base dei commenti di @ jon, ho riprovato il metodo del passo pre-build e ho usato questo (posizione di protogen hardcoded per ora), usando l'esempio della rubrica di Google:

c:\bin\protobuf\protogen "-i:$(ProjectDir)AddressBook.proto" 
       "-o:$(ProjectDir)AddressBook.cs" -t:c:\bin\protobuf\csharp.xslt

Edit2: Seguendo la raccomandazione di @ jon di ridurre al minimo i tempi di costruzione non elaborando i file .proto se non sono stati modificati, ho messo insieme uno strumento di base per verificarlo (questo potrebbe probabilmente essere esteso a uno strumento di creazione personalizzata completo):

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);
        }
    }
}

Il mio comando pre-build è ora (tutto su una riga):

$(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
È stato utile?

Soluzione

Come estensione del codice di Shaun, sono lieto di annunciare che protobuf-net ora ha l'integrazione di Visual Studio tramite uno strumento personalizzato. Il programma di installazione msi è disponibile dalla pagina del progetto . Informazioni più complete qui: protobuf-net; ora con l'aggiunta di Orche .

Studio con net uno strumento personalizzato

Altri suggerimenti

Chiamare una fase di pre-compilazione ma usando variabili di progetto (ad esempio $ (ProjectPath) ) per creare nomi di file assoluti senza averli effettivamente nella tua soluzione, mi sembrerebbe una scommessa ragionevole.

Una cosa che potresti voler considerare, in base alla mia esperienza passata con i generatori di codice: potresti voler scrivere un wrapper per protogen che generi codice in una posizione diversa, quindi controlla se il codice appena generato è uguale al vecchio codice e non lo sovrascrive in tal caso. In questo modo Visual Studio realizzerà che nulla è cambiato e non forzerà la ricostruzione di quel progetto - questo ha ridotto i tempi di costruzione drammaticamente per me in passato.

In alternativa, è possibile mantenere un hash md5 del file .proto l'ultima volta che è stato eseguito protogen ed eseguire protogen solo se il file .proto è cambiato - ancora meno da fare su ogni build!

Grazie per aver sollevato questa domanda come una domanda - suggerisce chiaramente che dovrei trovare un modo per rendere questo un semplice passo pre-build per la mia porta.

Aggiungi il seguente evento pre-build alle impostazioni del tuo progetto per generare il file C # solo quando il file .proto è stato modificato. Sostituisci YourFile con il nome del nome base del tuo file .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 }

Funziona con qualsiasi versione recente di Visual Studio, a differenza dello strumento di creazione personalizzata protobuf-net, che non supporta Visual Studio 2012 o Visual Studio 2013, a seconda dei problemi 338 e 413 .

Aggiungilo al file di progetto pertinente.

Vantaggio, build incrementale.

Svantaggio, è necessario modificare manualmente quando si aggiungono file.

<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="&quot;@(_protoc)&quot; &quot;--csharp_out=$(ProjectDir.TrimEnd('\'))&quot; @(Proto->'%(Identity)',' ')" WorkingDirectory="$(ProjectDir)" />
</Target>

beh, questo mi ha dato un'idea (qualcosa sul reinventare la ruota) ...

  • crea un semplice Makefile.mak, qualcosa come
.SUFFIXES : .cs .proto

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst

(ovviamente, non dimenticare di sostituire i percorsi di protogen e csharp.xlst). IMPORTANTE: comando protogen \ protogen.exe a partire dal carattere TAB, non da 8 spazi

  • Se non si desidera specificare i file necessari per essere compilati continuamente, è possibile utilizzare qualcosa come
.SUFFIXES : .cs .proto

all: mycs1.cs myotherfile.cs

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst
  • nella fase di pre-compilazione da aggiungere
cd $(ProjectDir) && "$(DevEnvDir)..\..\vc\bin\nmake" /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

oppure, se hai nmake nel tuo percorso, puoi usarlo  

cd $(ProjectDir) && nmake /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

Ho collegato un wrapper rapido e sporco di Visual Studio Custom Tool attorno a ProtoGen.exe alla pagina del codice di Google per questo problema ( http://code.google.com/p/protobuf-net/issues/detail?id=39 ). Ciò rende estremamente semplice l'aggiunta di file .proto ai progetti C #.

Vedi il file Leggimi in allegato per maggiori informazioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top