1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "msvc_helper.h"
16 
17 #include <fcntl.h>
18 #include <io.h>
19 #include <stdio.h>
20 #include <windows.h>
21 
22 #include "clparser.h"
23 #include "util.h"
24 
25 #include "getopt.h"
26 
27 using namespace std;
28 
29 namespace {
30 
Usage()31 void Usage() {
32   printf(
33 "usage: ninja -t msvc [options] -- cl.exe /showIncludes /otherArgs\n"
34 "options:\n"
35 "  -e ENVFILE load environment block from ENVFILE as environment\n"
36 "  -o FILE    write output dependency information to FILE.d\n"
37 "  -p STRING  localized prefix of msvc's /showIncludes output\n"
38          );
39 }
40 
PushPathIntoEnvironment(const string & env_block)41 void PushPathIntoEnvironment(const string& env_block) {
42   const char* as_str = env_block.c_str();
43   while (as_str[0]) {
44     if (_strnicmp(as_str, "path=", 5) == 0) {
45       _putenv(as_str);
46       return;
47     } else {
48       as_str = &as_str[strlen(as_str) + 1];
49     }
50   }
51 }
52 
WriteDepFileOrDie(const char * object_path,const CLParser & parse)53 void WriteDepFileOrDie(const char* object_path, const CLParser& parse) {
54   string depfile_path = string(object_path) + ".d";
55   FILE* depfile = fopen(depfile_path.c_str(), "w");
56   if (!depfile) {
57     unlink(object_path);
58     Fatal("opening %s: %s", depfile_path.c_str(),
59           GetLastErrorString().c_str());
60   }
61   if (fprintf(depfile, "%s: ", object_path) < 0) {
62     unlink(object_path);
63     fclose(depfile);
64     unlink(depfile_path.c_str());
65     Fatal("writing %s", depfile_path.c_str());
66   }
67   const set<string>& headers = parse.includes_;
68   for (set<string>::const_iterator i = headers.begin();
69        i != headers.end(); ++i) {
70     if (fprintf(depfile, "%s\n", EscapeForDepfile(*i).c_str()) < 0) {
71       unlink(object_path);
72       fclose(depfile);
73       unlink(depfile_path.c_str());
74       Fatal("writing %s", depfile_path.c_str());
75     }
76   }
77   fclose(depfile);
78 }
79 
80 }  // anonymous namespace
81 
MSVCHelperMain(int argc,char ** argv)82 int MSVCHelperMain(int argc, char** argv) {
83   const char* output_filename = NULL;
84   const char* envfile = NULL;
85 
86   const option kLongOptions[] = {
87     { "help", no_argument, NULL, 'h' },
88     { NULL, 0, NULL, 0 }
89   };
90   int opt;
91   string deps_prefix;
92   while ((opt = getopt_long(argc, argv, "e:o:p:h", kLongOptions, NULL)) != -1) {
93     switch (opt) {
94       case 'e':
95         envfile = optarg;
96         break;
97       case 'o':
98         output_filename = optarg;
99         break;
100       case 'p':
101         deps_prefix = optarg;
102         break;
103       case 'h':
104       default:
105         Usage();
106         return 0;
107     }
108   }
109 
110   string env;
111   if (envfile) {
112     string err;
113     if (ReadFile(envfile, &env, &err) != 0)
114       Fatal("couldn't open %s: %s", envfile, err.c_str());
115     PushPathIntoEnvironment(env);
116   }
117 
118   char* command = GetCommandLineA();
119   command = strstr(command, " -- ");
120   if (!command) {
121     Fatal("expected command line to end with \" -- command args\"");
122   }
123   command += 4;
124 
125   CLWrapper cl;
126   if (!env.empty())
127     cl.SetEnvBlock((void*)env.data());
128   string output;
129   int exit_code = cl.Run(command, &output);
130 
131   if (output_filename) {
132     CLParser parser;
133     string err;
134     if (!parser.Parse(output, deps_prefix, &output, &err))
135       Fatal("%s\n", err.c_str());
136     WriteDepFileOrDie(output_filename, parser);
137   }
138 
139   if (output.empty())
140     return exit_code;
141 
142   // CLWrapper's output already as \r\n line endings, make sure the C runtime
143   // doesn't expand this to \r\r\n.
144   _setmode(_fileno(stdout), _O_BINARY);
145   // Avoid printf and C strings, since the actual output might contain null
146   // bytes like UTF-16 does (yuck).
147   fwrite(&output[0], 1, output.size(), stdout);
148 
149   return exit_code;
150 }
151