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