aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/ProtoGen/ProgramPreprocess.cs
diff options
context:
space:
mode:
Diffstat (limited to 'csharp/src/ProtoGen/ProgramPreprocess.cs')
-rw-r--r--csharp/src/ProtoGen/ProgramPreprocess.cs276
1 files changed, 276 insertions, 0 deletions
diff --git a/csharp/src/ProtoGen/ProgramPreprocess.cs b/csharp/src/ProtoGen/ProgramPreprocess.cs
new file mode 100644
index 00000000..343e1f2a
--- /dev/null
+++ b/csharp/src/ProtoGen/ProgramPreprocess.cs
@@ -0,0 +1,276 @@
+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();
+ }
+ }
+} \ No newline at end of file