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 */ 22int CheckForCompilerFeature (const char *option); 23int CheckForLinkerFeature (const char *option); 24int IsIn (const char *string, const char *substring); 25int GrepForDefine (const char *file, const char *string); 26DWORD WINAPI ReadFromPipe (LPVOID args); 27 28/* globals */ 29#define CHUNK 25 30#define STATICBUFFERSIZE 1000 31typedef struct { 32 HANDLE pipe; 33 char buffer[STATICBUFFERSIZE]; 34} pipeinfo; 35 36pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; 37pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; 38 39 40 41/* exitcodes: 0 == no, 1 == yes, 2 == error */ 42int 43main (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 108int 109CheckForCompilerFeature (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 199int 200CheckForLinkerFeature (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 288DWORD WINAPI 289ReadFromPipe (LPVOID args) 290{ 291 pipeinfo *pi = (pipeinfo *) args; 292 char *lastBuf = pi->buffer; 293 DWORD dwRead; 294 BOOL ok; 295 296again: 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 312int 313IsIn (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 325int 326GrepForDefine (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