| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Text;
- using System.Text.RegularExpressions;
- 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>
- public class ProgramPreprocess
- {
- private const string ProtocExecutable = "protoc.exe";
- private const string ProtocDirectoryArg = "--protoc_dir=";
- private static int Main(string[] args)
- {
- try
- {
- return Environment.ExitCode = Run(args);
- }
- catch (Exception ex)
- {
- Console.Error.WriteLine(ex);
- return Environment.ExitCode = 2;
- }
- }
- public 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>();
- string protocFile = GuessProtocFile(args);
- 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(protocFile, "--help");
- }
- catch (Exception ex)
- {
- Console.Error.WriteLine(ex.Message);
- }
- Console.WriteLine();
- Console.WriteLine();
- Console.WriteLine(
- "PROTOGEN.exe: The following options are used to specify defaults for code generation.");
- Console.WriteLine();
- Program.Main(new string[0]);
- Console.WriteLine();
- Console.WriteLine("The following option enables PROTOGEN.exe to find PROTOC.exe");
- Console.WriteLine("{0}<directory containing protoc.exe>", ProtocDirectoryArg);
- return 0;
- }
- string pathRoot = Environment.CurrentDirectory;
- foreach(string arg in args)
- {
- if (arg.StartsWith("--proto_path=", StringComparison.InvariantCultureIgnoreCase))
- {
- pathRoot = arg.Substring(13);
- }
- }
- foreach (string arg in args)
- {
- if (arg.StartsWith(ProtocDirectoryArg))
- {
- // Handled earlier
- continue;
- }
- if (arg.StartsWith("--"))
- {
- protocArgs.Add(arg);
- }
- else if ((File.Exists(arg) || File.Exists(Path.Combine(pathRoot, 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);
- }
- string patharg = arg;
- if (!File.Exists(patharg))
- {
- patharg = Path.Combine(pathRoot, arg);
- }
- protocArgs.Add(patharg);
- }
- else
- {
- protoGenArgs.Add(arg);
- }
- }
- if (tempFile != null)
- {
- result = RunProtoc(protocFile, 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;
- }
- /// <summary>
- /// Tries to work out where protoc is based on command line arguments, the current
- /// directory, the directory containing protogen, and the path.
- /// </summary>
- /// <returns>The path to protoc.exe, or null if it can't be found.</returns>
- private static string GuessProtocFile(params string[] args)
- {
- // Why oh why is this not in System.IO.Path or Environment...?
- List<string> searchPath = new List<string>();
- foreach (string arg in args)
- {
- if (arg.StartsWith("--protoc_dir="))
- {
- searchPath.Add(arg.Substring(ProtocDirectoryArg.Length));
- }
- }
- searchPath.Add(Environment.CurrentDirectory);
- searchPath.Add(AppDomain.CurrentDomain.BaseDirectory);
- searchPath.AddRange((Environment.GetEnvironmentVariable("PATH") ?? String.Empty).Split(Path.PathSeparator));
- foreach (string path in searchPath)
- {
- string exeFile = Path.Combine(path, ProtocExecutable);
- if (File.Exists(exeFile))
- {
- return exeFile;
- }
- }
- return null;
- }
- private static int RunProtoc(string exeFile, params string[] args)
- {
- if (exeFile == null)
- {
- throw new FileNotFoundException(
- "Unable to locate " + ProtocExecutable +
- " make sure it is in the PATH, cwd, or exe dir, or use --protoc_dir=...");
- }
- ProcessStartInfo psi = new ProcessStartInfo(exeFile);
- psi.Arguments = EscapeArguments(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)
- {
- // Replace protoc output with something more amenable to Visual Studio.
- var regexMsvs = new Regex(@"(.*)\((\d+)\).* column=(\d+)\s*:\s*(.*)");
- tmp = regexMsvs.Replace(tmp, "$1($2,$3): error CS9999: $4");
- var regexGcc = new Regex(@"(.*):(\d+):(\d+):\s*(.*)");
- tmp = regexGcc.Replace(tmp, "$1($2,$3): error CS9999: $4");
- Console.Error.WriteLine(tmp);
- }
- return process.ExitCode;
- }
- /// <summary>
- /// Quotes all arguments that contain whitespace, or begin with a quote and returns a single
- /// argument string for use with Process.Start().
- /// </summary>
- /// <remarks>http://csharptest.net/?p=529</remarks>
- /// <param name="args">A list of strings for arguments, may not contain null, '\0', '\r', or '\n'</param>
- /// <returns>The combined list of escaped/quoted strings</returns>
- /// <exception cref="System.ArgumentNullException">Raised when one of the arguments is null</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">Raised if an argument contains '\0', '\r', or '\n'</exception>
- public static string EscapeArguments(params string[] args)
- {
- StringBuilder arguments = new StringBuilder();
- Regex invalidChar = new Regex("[\x00\x0a\x0d]");// these can not be escaped
- Regex needsQuotes = new Regex(@"\s|""");// contains whitespace or two quote characters
- Regex escapeQuote = new Regex(@"(\\*)(""|$)");// one or more '\' followed with a quote or end of string
- for (int carg = 0; args != null && carg < args.Length; carg++)
- {
- if (args[carg] == null)
- {
- throw new ArgumentNullException("args[" + carg + "]");
- }
- if (invalidChar.IsMatch(args[carg]))
- {
- throw new ArgumentOutOfRangeException("args[" + carg + "]");
- }
- if (args[carg] == String.Empty)
- {
- arguments.Append("\"\"");
- }
- else if (!needsQuotes.IsMatch(args[carg])) { arguments.Append(args[carg]); }
- else
- {
- arguments.Append('"');
- arguments.Append(escapeQuote.Replace(args[carg],
- m =>
- m.Groups[1].Value + m.Groups[1].Value +
- (m.Groups[2].Value == "\"" ? "\\\"" : "")
- ));
- arguments.Append('"');
- }
- if (carg + 1 < args.Length)
- {
- arguments.Append(' ');
- }
- }
- return arguments.ToString();
- }
- }
- }
|