Tampons de protocole dans les projets C # utilisant protobuf-net - Meilleures pratiques pour la génération de code

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

Question

J'essaie d'utiliser protobuf dans un projet C # à l'aide de protobuf-net et je me demande quel est le meilleur moyen de l'organiser dans une structure de projet Visual Studio.

Lorsque vous utilisez manuellement l'outil protogen pour générer du code en C #, la vie semble simple, mais vous ne vous sentez pas bien.

J'aimerais que le fichier .proto soit considéré comme le fichier de code source principal, générant des fichiers C # en tant que sous-produit, mais avant que le compilateur C # n'intervienne.

Les options semblent être:

  1. Outil personnalisé pour les outils proto (bien que je ne sache pas par où commencer)
  2. Étape de pré-construction (appel de protogen ou d'un fichier de commandes qui le fait)

J'ai eu des problèmes avec 2) ci-dessus, car il continue à me donner "Le système ne peut pas trouver le fichier spécifié". sauf si j'utilise des chemins absolus (et que je n'aime pas forcer les projets à être explicitement situés).

Existe-t-il (encore) une convention pour cela?

Modifier: Sur la base des commentaires de @ jon, j'ai réessayé la méthode de l'étape de pré-construction et l'ai utilisée (l'emplacement de protogen en code dur pour l'instant), en utilisant l'exemple du carnet d'adresses de Google:

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

Modifier2: En suivant la recommandation de @ jon de minimiser le temps de compilation en ne traitant pas les fichiers .proto si ceux-ci n'ont pas été modifiés, j'ai créé un outil de base pour vérifier pour moi (cet outil pourrait probablement être étendu à un outil complet de génération personnalisée):

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

Ma commande de pré-construction est maintenant (sur une seule ligne):

$(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
Était-ce utile?

La solution

En tant qu'extension du code de Shaun, j'ai le plaisir d'annoncer que protobuf-net intègre désormais Visual Studio par le biais d'un outil personnalisé. Le programme d’installation msi est disponible à partir de la page de projet . Des informations plus complètes ici: protobuf-net; maintenant avec les orques ajoutées .

Studio de cr&eacute;ation avec protob&egrave;ne

Autres conseils

Appeler une étape de pré-génération mais utiliser des variables de projet (par exemple, $ (ProjectPath) ) pour créer des noms de fichiers absolus sans les avoir réellement dans votre solution me semble être un pari raisonnable.

Une chose que vous voudrez peut-être prendre en compte, selon mon expérience passée des générateurs de code: vous voudrez peut-être écrire un wrapper pour protogen qui génère du code dans un emplacement différent, puis vérifie si le code nouvellement généré est identique à l'ancien. code, et ne l'écrase pas si oui. De cette façon, Visual Studio réalisera que rien n’a changé et ne forcera pas la reconstruction de ce projet, ce qui a permis de réduire considérablement le temps de construction par le passé .

Sinon, vous pouvez conserver un hachage md5 du fichier .proto lors de la dernière exécution de protogen et n'exécuter que protogen si le fichier .proto a été modifié - encore moins à faire pour chaque construction!

Merci d'avoir soulevé cette question en tant que question car elle suggère clairement que je devrais trouver un moyen d'en faire une étape de pré-construction facile pour mon propre port.

Ajoutez l'événement de pré-génération suivant aux paramètres de votre projet pour générer le fichier C # uniquement lorsque le fichier .proto a été modifié. Il suffit de remplacer VotreFichier par le nom du nom de base de votre fichier .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 }

Cela fonctionne dans toutes les versions récentes de Visual Studio, à la différence de l'outil protobuf-net Custom-Build, qui ne prend pas en charge Visual Studio 2012 ni Visual Studio 2013, en fonction des problèmes 338 et 413 .

Ajoutez ceci au fichier de projet approprié.

Avantage, construction incrémentielle.

Inconvénient, vous devez modifier manuellement lorsque vous ajoutez des fichiers.

<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>

eh bien, ça m’a donné une idée (de réinventer la roue) ...

  • créer un Makefile.mak simple, quelque chose comme
.SUFFIXES : .cs .proto

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

(évidemment, n'oubliez pas de remplacer les chemins d'accès à protogen et csharp.xlst). IMPORTANT - Commande protogen \ protogen.exe à partir du caractère de tabulation (TAB) et non de 8 espaces

  • Si vous ne voulez pas spécifier que les fichiers doivent être construits tout le temps, vous pouvez utiliser quelque chose comme
.SUFFIXES : .cs .proto

all: mycs1.cs myotherfile.cs

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst
  • en phase de pré-construction pour ajouter
cd $(ProjectDir) && "$(DevEnvDir)..\..\vc\bin\nmake" /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

ou, si vous avez nmake sur votre chemin, on peut utiliser  

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

J'ai attaché un wrapper rapide et personnalisé de Visual Studio Custom Tool autour de ProtoGen.exe à la page de codes Google de ce problème ( http://code.google.com/p/protobuf-net/issues/detail?id=39 ). Cela facilite l'ajout de fichiers .proto aux projets C #.

Voir le fichier readme en pièce jointe pour plus d'informations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top