aboutsummaryrefslogtreecommitdiff
path: root/src/ProtoGen/ProgramPreprocess.cs
blob: 3cc0826dcecabd7b37bcb46888417440f4da2b4b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

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

        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("--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]);
          return 0;
        }

        foreach (string arg in args) {
          if (arg.StartsWith("--")) {
            protocArgs.Add(arg);
          }
          else if (File.Exists(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);
            }
            protocArgs.Add(arg);
          }
          else {
            protoGenArgs.Add(arg);
          }
        }

        if (tempFile != null) {
          result = RunProtoc(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;
    }

    private static int RunProtoc(params string[] args) {
      const string protoc = "protoc.exe";
      string exePath = protoc;

      // Why oh why is this not in System.IO.Path or Environment...?
      List<string> searchPath = new List<string>();
      searchPath.Add(Environment.CurrentDirectory);
      searchPath.Add(AppDomain.CurrentDomain.BaseDirectory);
      searchPath.AddRange((Environment.GetEnvironmentVariable("PATH") ?? String.Empty).Split(Path.PathSeparator));

      foreach (string path in searchPath) {
        if (File.Exists(exePath = Path.Combine(path, protoc))) {
          break;
        }
      }

      if (!File.Exists(exePath)) {
        throw new FileNotFoundException("Unable to locate " + protoc + " make sure it is in the PATH, cwd, or exe dir.");
      }

      for (int i = 0; i < args.Length; i++) {
        if (args[i].IndexOf(' ') > 0 && args[i][0] != '"') {
          args[i] = '"' + args[i] + '"';
        }
      }

      ProcessStartInfo psi = new ProcessStartInfo(exePath);
      psi.Arguments = String.Join(" ", 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) {
        Console.Error.WriteLine(tmp);
      }
      return process.ExitCode;
    }
  }
}