1 /* ----------------------------------------------------------------------------
2 * nmakehlp.c --
3 *
4 * This is used to fix limitations within nmake and the environment.
5 *
6 * Copyright (c) 2002 by David Gravereaux.
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 * ----------------------------------------------------------------------------
11 */
12 #include <windows.h>
13 #pragma comment (lib, "user32.lib")
14 #pragma comment (lib, "kernel32.lib")
15 #include <stdio.h>
16 #include <math.h>
17
18 /* protos */
19 int CheckForCompilerFeature (const char *option);
20 int CheckForLinkerFeature (const char *option);
21 int IsIn (const char *string, const char *substring);
22 int GrepForDefine (const char *file, const char *string);
23 DWORD WINAPI ReadFromPipe (LPVOID args);
24
25 /* globals */
26 #define CHUNK 25
27 #define STATICBUFFERSIZE 1000
28 typedef struct {
29 HANDLE pipe;
30 char buffer[STATICBUFFERSIZE];
31 } pipeinfo;
32
33 pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
34 pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
35
36
37
38 /* exitcodes: 0 == no, 1 == yes, 2 == error */
39 int
main(int argc,char * argv[])40 main (int argc, char *argv[])
41 {
42 char msg[300];
43 DWORD dwWritten;
44 int chars;
45
46 /* make sure children (cl.exe and link.exe) are kept quiet. */
47 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
48
49 /* Make sure the compiler and linker aren't effected by the outside world. */
50 SetEnvironmentVariable("CL", "");
51 SetEnvironmentVariable("LINK", "");
52
53 if (argc > 1 && *argv[1] == '-') {
54 switch (*(argv[1]+1)) {
55 case 'c':
56 if (argc != 3) {
57 chars = wsprintf(msg, "usage: %s -c <compiler option>\n"
58 "Tests for whether cl.exe supports an option\n"
59 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
60 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
61 return 2;
62 }
63 return CheckForCompilerFeature(argv[2]);
64 case 'l':
65 if (argc != 3) {
66 chars = wsprintf(msg, "usage: %s -l <linker option>\n"
67 "Tests for whether link.exe supports an option\n"
68 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
69 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
70 return 2;
71 }
72 return CheckForLinkerFeature(argv[2]);
73 case 'f':
74 if (argc == 2) {
75 chars = wsprintf(msg, "usage: %s -f <string> <substring>\n"
76 "Find a substring within another\n"
77 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
78 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
79 return 2;
80 } else if (argc == 3) {
81 /* if the string is blank, there is no match */
82 return 0;
83 } else {
84 return IsIn(argv[2], argv[3]);
85 }
86 case 'g':
87 if (argc == 2) {
88 chars = wsprintf(msg, "usage: %s -g <file> <string>\n"
89 "grep for a #define\n"
90 "exitcodes: integer of the found string (no decimals)\n", argv[0]);
91 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
92 return 2;
93 }
94 return GrepForDefine(argv[2], argv[3]);
95 }
96 }
97 chars = wsprintf(msg, "usage: %s -c|-l|-f ...\n"
98 "This is a little helper app to equalize shell differences between WinNT and\n"
99 "Win9x and get nmake.exe to accomplish its job.\n",
100 argv[0]);
101 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
102 return 2;
103 }
104
105 int
CheckForCompilerFeature(const char * option)106 CheckForCompilerFeature (const char *option)
107 {
108 STARTUPINFO si;
109 PROCESS_INFORMATION pi;
110 SECURITY_ATTRIBUTES sa;
111 DWORD threadID;
112 char msg[300];
113 BOOL ok;
114 HANDLE hProcess, h, pipeThreads[2];
115 char cmdline[100];
116
117 hProcess = GetCurrentProcess();
118
119 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
120 ZeroMemory(&si, sizeof(STARTUPINFO));
121 si.cb = sizeof(STARTUPINFO);
122 si.dwFlags = STARTF_USESTDHANDLES;
123 si.hStdInput = INVALID_HANDLE_VALUE;
124
125 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
126 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
127 sa.lpSecurityDescriptor = NULL;
128 sa.bInheritHandle = FALSE;
129
130 /* create a non-inheritible pipe. */
131 CreatePipe(&Out.pipe, &h, &sa, 0);
132
133 /* dupe the write side, make it inheritible, and close the original. */
134 DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput,
135 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
136
137 /* Same as above, but for the error side. */
138 CreatePipe(&Err.pipe, &h, &sa, 0);
139 DuplicateHandle(hProcess, h, hProcess, &si.hStdError,
140 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
141
142 /* base command line */
143 strcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X ");
144 /* append our option for testing */
145 strcat(cmdline, option);
146 /* filename to compile, which exists, but is nothing and empty. */
147 strcat(cmdline, " .\\nul");
148
149 ok = CreateProcess(
150 NULL, /* Module name. */
151 cmdline, /* Command line. */
152 NULL, /* Process handle not inheritable. */
153 NULL, /* Thread handle not inheritable. */
154 TRUE, /* yes, inherit handles. */
155 DETACHED_PROCESS, /* No console for you. */
156 NULL, /* Use parent's environment block. */
157 NULL, /* Use parent's starting directory. */
158 &si, /* Pointer to STARTUPINFO structure. */
159 &pi); /* Pointer to PROCESS_INFORMATION structure. */
160
161 if (!ok) {
162 DWORD err = GetLastError();
163 int chars = wsprintf(msg, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
164
165 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
166 FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID) &msg[chars],
167 (300-chars), 0);
168 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, strlen(msg), &err, NULL);
169 return 2;
170 }
171
172 /* close our references to the write handles that have now been inherited. */
173 CloseHandle(si.hStdOutput);
174 CloseHandle(si.hStdError);
175
176 WaitForInputIdle(pi.hProcess, 5000);
177 CloseHandle(pi.hThread);
178
179 /* start the pipe reader threads. */
180 pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
181 pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
182
183 /* block waiting for the process to end. */
184 WaitForSingleObject(pi.hProcess, INFINITE);
185 CloseHandle(pi.hProcess);
186
187 /* wait for our pipe to get done reading, should it be a little slow. */
188 WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
189 CloseHandle(pipeThreads[0]);
190 CloseHandle(pipeThreads[1]);
191
192 /* look for the commandline warning code in both streams. */
193 return !(strstr(Out.buffer, "D4002") != NULL || strstr(Err.buffer, "D4002") != NULL);
194 }
195
196 int
CheckForLinkerFeature(const char * option)197 CheckForLinkerFeature (const char *option)
198 {
199 STARTUPINFO si;
200 PROCESS_INFORMATION pi;
201 SECURITY_ATTRIBUTES sa;
202 DWORD threadID;
203 char msg[300];
204 BOOL ok;
205 HANDLE hProcess, h, pipeThreads[2];
206 char cmdline[100];
207
208 hProcess = GetCurrentProcess();
209
210 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
211 ZeroMemory(&si, sizeof(STARTUPINFO));
212 si.cb = sizeof(STARTUPINFO);
213 si.dwFlags = STARTF_USESTDHANDLES;
214 si.hStdInput = INVALID_HANDLE_VALUE;
215
216 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
217 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
218 sa.lpSecurityDescriptor = NULL;
219 sa.bInheritHandle = TRUE;
220
221 /* create a non-inheritible pipe. */
222 CreatePipe(&Out.pipe, &h, &sa, 0);
223
224 /* dupe the write side, make it inheritible, and close the original. */
225 DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput,
226 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
227
228 /* Same as above, but for the error side. */
229 CreatePipe(&Err.pipe, &h, &sa, 0);
230 DuplicateHandle(hProcess, h, hProcess, &si.hStdError,
231 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
232
233 /* base command line */
234 strcpy(cmdline, "link.exe -nologo ");
235 /* append our option for testing */
236 strcat(cmdline, option);
237
238 ok = CreateProcess(
239 NULL, /* Module name. */
240 cmdline, /* Command line. */
241 NULL, /* Process handle not inheritable. */
242 NULL, /* Thread handle not inheritable. */
243 TRUE, /* yes, inherit handles. */
244 DETACHED_PROCESS, /* No console for you. */
245 NULL, /* Use parent's environment block. */
246 NULL, /* Use parent's starting directory. */
247 &si, /* Pointer to STARTUPINFO structure. */
248 &pi); /* Pointer to PROCESS_INFORMATION structure. */
249
250 if (!ok) {
251 DWORD err = GetLastError();
252 int chars = wsprintf(msg, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
253
254 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
255 FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID) &msg[chars],
256 (300-chars), 0);
257 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, strlen(msg), &err, NULL);
258 return 2;
259 }
260
261 /* close our references to the write handles that have now been inherited. */
262 CloseHandle(si.hStdOutput);
263 CloseHandle(si.hStdError);
264
265 WaitForInputIdle(pi.hProcess, 5000);
266 CloseHandle(pi.hThread);
267
268 /* start the pipe reader threads. */
269 pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
270 pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
271
272 /* block waiting for the process to end. */
273 WaitForSingleObject(pi.hProcess, INFINITE);
274 CloseHandle(pi.hProcess);
275
276 /* wait for our pipe to get done reading, should it be a little slow. */
277 WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
278 CloseHandle(pipeThreads[0]);
279 CloseHandle(pipeThreads[1]);
280
281 /* look for the commandline warning code in the stderr stream. */
282 return !(strstr(Out.buffer, "LNK1117") != NULL || strstr(Err.buffer, "LNK1117") != NULL);
283 }
284
285 DWORD WINAPI
ReadFromPipe(LPVOID args)286 ReadFromPipe (LPVOID args)
287 {
288 pipeinfo *pi = (pipeinfo *) args;
289 char *lastBuf = pi->buffer;
290 DWORD dwRead;
291 BOOL ok;
292
293 again:
294 if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
295 CloseHandle(pi->pipe);
296 return -1;
297 }
298 ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
299 if (!ok || dwRead == 0) {
300 CloseHandle(pi->pipe);
301 return 0;
302 }
303 lastBuf += dwRead;
304 goto again;
305
306 return 0; /* makes the compiler happy */
307 }
308
309 int
IsIn(const char * string,const char * substring)310 IsIn (const char *string, const char *substring)
311 {
312 return (strstr(string, substring) != NULL);
313 }
314
315 /*
316 * Find a specified #define by name.
317 *
318 * If the line is '#define TCL_VERSION "8.5"', it returns
319 * 85 as the result.
320 */
321
322 int
GrepForDefine(const char * file,const char * string)323 GrepForDefine (const char *file, const char *string)
324 {
325 FILE *f;
326 char s1[51], s2[51], s3[51];
327 int r = 0;
328 double d1;
329
330 f = fopen(file, "rt");
331 if (f == NULL) {
332 return 0;
333 }
334
335 do {
336 r = fscanf(f, "%50s", s1);
337 if (r == 1 && !strcmp(s1, "#define")) {
338 /* get next two words */
339 r = fscanf(f, "%50s %50s", s2, s3);
340 if (r != 2) continue;
341 /* is the first word what we're looking for? */
342 if (!strcmp(s2, string)) {
343 fclose(f);
344 /* add 1 past first double quote char. "8.5" */
345 d1 = atof(s3 + 1); /* 8.5 */
346 while (floor(d1) != d1) {
347 d1 *= 10.0;
348 }
349 return ((int) d1); /* 85 */
350 }
351 }
352 } while (!feof(f));
353
354 fclose(f);
355 return 0;
356 }
357