1/* 2 * ---------------------------------------------------------------------------- 3 * nmakehlp.c -- 4 * 5 * This is used to fix limitations within nmake and the environment. 6 * 7 * Copyright (c) 2002 by David Gravereaux. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * ---------------------------------------------------------------------------- 13 * RCS: @(#) $Id: nmakehlp.c,v 1.1.4.4 2006/10/18 08:49:33 patthoyts Exp $ 14 * ---------------------------------------------------------------------------- 15 */ 16 17#define _CRT_SECURE_NO_DEPRECATE 18#include <windows.h> 19#pragma comment (lib, "user32.lib") 20#pragma comment (lib, "kernel32.lib") 21#include <stdio.h> 22#include <math.h> 23#if defined(_M_IA64) || defined(_M_AMD64) 24#pragma comment(lib, "bufferoverflowU") 25#endif 26 27/* ISO hack for dumb VC++ */ 28#ifdef _MSC_VER 29#define snprintf _snprintf 30#endif 31 32 33 34/* protos */ 35 36int CheckForCompilerFeature(const char *option); 37int CheckForLinkerFeature(const char *option); 38int IsIn(const char *string, const char *substring); 39int GrepForDefine(const char *file, const char *string); 40DWORD WINAPI ReadFromPipe(LPVOID args); 41 42/* globals */ 43 44#define CHUNK 25 45#define STATICBUFFERSIZE 1000 46typedef struct { 47 HANDLE pipe; 48 char buffer[STATICBUFFERSIZE]; 49} pipeinfo; 50 51pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; 52pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; 53 54/* 55 * exitcodes: 0 == no, 1 == yes, 2 == error 56 */ 57 58int 59main( 60 int argc, 61 char *argv[]) 62{ 63 char msg[300]; 64 DWORD dwWritten; 65 int chars; 66 67 /* 68 * Make sure children (cl.exe and link.exe) are kept quiet. 69 */ 70 71 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); 72 73 /* 74 * Make sure the compiler and linker aren't effected by the outside world. 75 */ 76 77 SetEnvironmentVariable("CL", ""); 78 SetEnvironmentVariable("LINK", ""); 79 80 if (argc > 1 && *argv[1] == '-') { 81 switch (*(argv[1]+1)) { 82 case 'c': 83 if (argc != 3) { 84 chars = snprintf(msg, sizeof(msg) - 1, 85 "usage: %s -c <compiler option>\n" 86 "Tests for whether cl.exe supports an option\n" 87 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); 88 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, 89 &dwWritten, NULL); 90 return 2; 91 } 92 return CheckForCompilerFeature(argv[2]); 93 case 'l': 94 if (argc != 3) { 95 chars = snprintf(msg, sizeof(msg) - 1, 96 "usage: %s -l <linker option>\n" 97 "Tests for whether link.exe supports an option\n" 98 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); 99 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, 100 &dwWritten, NULL); 101 return 2; 102 } 103 return CheckForLinkerFeature(argv[2]); 104 case 'f': 105 if (argc == 2) { 106 chars = snprintf(msg, sizeof(msg) - 1, 107 "usage: %s -f <string> <substring>\n" 108 "Find a substring within another\n" 109 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); 110 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, 111 &dwWritten, NULL); 112 return 2; 113 } else if (argc == 3) { 114 /* 115 * If the string is blank, there is no match. 116 */ 117 118 return 0; 119 } else { 120 return IsIn(argv[2], argv[3]); 121 } 122 case 'g': 123 if (argc == 2) { 124 chars = snprintf(msg, sizeof(msg) - 1, 125 "usage: %s -g <file> <string>\n" 126 "grep for a #define\n" 127 "exitcodes: integer of the found string (no decimals)\n", 128 argv[0]); 129 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, 130 &dwWritten, NULL); 131 return 2; 132 } 133 return GrepForDefine(argv[2], argv[3]); 134 } 135 } 136 chars = snprintf(msg, sizeof(msg) - 1, 137 "usage: %s -c|-l|-f ...\n" 138 "This is a little helper app to equalize shell differences between WinNT and\n" 139 "Win9x and get nmake.exe to accomplish its job.\n", 140 argv[0]); 141 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); 142 return 2; 143} 144 145int 146CheckForCompilerFeature( 147 const char *option) 148{ 149 STARTUPINFO si; 150 PROCESS_INFORMATION pi; 151 SECURITY_ATTRIBUTES sa; 152 DWORD threadID; 153 char msg[300]; 154 BOOL ok; 155 HANDLE hProcess, h, pipeThreads[2]; 156 char cmdline[100]; 157 158 hProcess = GetCurrentProcess(); 159 160 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); 161 ZeroMemory(&si, sizeof(STARTUPINFO)); 162 si.cb = sizeof(STARTUPINFO); 163 si.dwFlags = STARTF_USESTDHANDLES; 164 si.hStdInput = INVALID_HANDLE_VALUE; 165 166 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); 167 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 168 sa.lpSecurityDescriptor = NULL; 169 sa.bInheritHandle = FALSE; 170 171 /* 172 * Create a non-inheritible pipe. 173 */ 174 175 CreatePipe(&Out.pipe, &h, &sa, 0); 176 177 /* 178 * Dupe the write side, make it inheritible, and close the original. 179 */ 180 181 DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, 182 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); 183 184 /* 185 * Same as above, but for the error side. 186 */ 187 188 CreatePipe(&Err.pipe, &h, &sa, 0); 189 DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, 190 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); 191 192 /* 193 * Base command line. 194 */ 195 196 lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X "); 197 198 /* 199 * Append our option for testing 200 */ 201 202 lstrcat(cmdline, option); 203 204 /* 205 * Filename to compile, which exists, but is nothing and empty. 206 */ 207 208 lstrcat(cmdline, " .\\nul"); 209 210 ok = CreateProcess( 211 NULL, /* Module name. */ 212 cmdline, /* Command line. */ 213 NULL, /* Process handle not inheritable. */ 214 NULL, /* Thread handle not inheritable. */ 215 TRUE, /* yes, inherit handles. */ 216 DETACHED_PROCESS, /* No console for you. */ 217 NULL, /* Use parent's environment block. */ 218 NULL, /* Use parent's starting directory. */ 219 &si, /* Pointer to STARTUPINFO structure. */ 220 &pi); /* Pointer to PROCESS_INFORMATION structure. */ 221 222 if (!ok) { 223 DWORD err = GetLastError(); 224 int chars = snprintf(msg, sizeof(msg) - 1, 225 "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); 226 227 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| 228 FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], 229 (300-chars), 0); 230 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); 231 return 2; 232 } 233 234 /* 235 * Close our references to the write handles that have now been inherited. 236 */ 237 238 CloseHandle(si.hStdOutput); 239 CloseHandle(si.hStdError); 240 241 WaitForInputIdle(pi.hProcess, 5000); 242 CloseHandle(pi.hThread); 243 244 /* 245 * Start the pipe reader threads. 246 */ 247 248 pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); 249 pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); 250 251 /* 252 * Block waiting for the process to end. 253 */ 254 255 WaitForSingleObject(pi.hProcess, INFINITE); 256 CloseHandle(pi.hProcess); 257 258 /* 259 * Wait for our pipe to get done reading, should it be a little slow. 260 */ 261 262 WaitForMultipleObjects(2, pipeThreads, TRUE, 500); 263 CloseHandle(pipeThreads[0]); 264 CloseHandle(pipeThreads[1]); 265 266 /* 267 * Look for the commandline warning code in both streams. 268 * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002. 269 */ 270 271 return !(strstr(Out.buffer, "D4002") != NULL 272 || strstr(Err.buffer, "D4002") != NULL 273 || strstr(Out.buffer, "D9002") != NULL 274 || strstr(Err.buffer, "D9002") != NULL); 275} 276 277int 278CheckForLinkerFeature( 279 const char *option) 280{ 281 STARTUPINFO si; 282 PROCESS_INFORMATION pi; 283 SECURITY_ATTRIBUTES sa; 284 DWORD threadID; 285 char msg[300]; 286 BOOL ok; 287 HANDLE hProcess, h, pipeThreads[2]; 288 char cmdline[100]; 289 290 hProcess = GetCurrentProcess(); 291 292 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); 293 ZeroMemory(&si, sizeof(STARTUPINFO)); 294 si.cb = sizeof(STARTUPINFO); 295 si.dwFlags = STARTF_USESTDHANDLES; 296 si.hStdInput = INVALID_HANDLE_VALUE; 297 298 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); 299 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 300 sa.lpSecurityDescriptor = NULL; 301 sa.bInheritHandle = TRUE; 302 303 /* 304 * Create a non-inheritible pipe. 305 */ 306 307 CreatePipe(&Out.pipe, &h, &sa, 0); 308 309 /* 310 * Dupe the write side, make it inheritible, and close the original. 311 */ 312 313 DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, 314 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); 315 316 /* 317 * Same as above, but for the error side. 318 */ 319 320 CreatePipe(&Err.pipe, &h, &sa, 0); 321 DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, 322 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); 323 324 /* 325 * Base command line. 326 */ 327 328 lstrcpy(cmdline, "link.exe -nologo "); 329 330 /* 331 * Append our option for testing. 332 */ 333 334 lstrcat(cmdline, option); 335 336 ok = CreateProcess( 337 NULL, /* Module name. */ 338 cmdline, /* Command line. */ 339 NULL, /* Process handle not inheritable. */ 340 NULL, /* Thread handle not inheritable. */ 341 TRUE, /* yes, inherit handles. */ 342 DETACHED_PROCESS, /* No console for you. */ 343 NULL, /* Use parent's environment block. */ 344 NULL, /* Use parent's starting directory. */ 345 &si, /* Pointer to STARTUPINFO structure. */ 346 &pi); /* Pointer to PROCESS_INFORMATION structure. */ 347 348 if (!ok) { 349 DWORD err = GetLastError(); 350 int chars = snprintf(msg, sizeof(msg) - 1, 351 "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); 352 353 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| 354 FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], 355 (300-chars), 0); 356 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); 357 return 2; 358 } 359 360 /* 361 * Close our references to the write handles that have now been inherited. 362 */ 363 364 CloseHandle(si.hStdOutput); 365 CloseHandle(si.hStdError); 366 367 WaitForInputIdle(pi.hProcess, 5000); 368 CloseHandle(pi.hThread); 369 370 /* 371 * Start the pipe reader threads. 372 */ 373 374 pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); 375 pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); 376 377 /* 378 * Block waiting for the process to end. 379 */ 380 381 WaitForSingleObject(pi.hProcess, INFINITE); 382 CloseHandle(pi.hProcess); 383 384 /* 385 * Wait for our pipe to get done reading, should it be a little slow. 386 */ 387 388 WaitForMultipleObjects(2, pipeThreads, TRUE, 500); 389 CloseHandle(pipeThreads[0]); 390 CloseHandle(pipeThreads[1]); 391 392 /* 393 * Look for the commandline warning code in the stderr stream. 394 */ 395 396 return !(strstr(Out.buffer, "LNK1117") != NULL || 397 strstr(Err.buffer, "LNK1117") != NULL || 398 strstr(Out.buffer, "LNK4044") != NULL || 399 strstr(Err.buffer, "LNK4044") != NULL); 400} 401 402DWORD WINAPI 403ReadFromPipe( 404 LPVOID args) 405{ 406 pipeinfo *pi = (pipeinfo *) args; 407 char *lastBuf = pi->buffer; 408 DWORD dwRead; 409 BOOL ok; 410 411 again: 412 if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) { 413 CloseHandle(pi->pipe); 414 return (DWORD)-1; 415 } 416 ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L); 417 if (!ok || dwRead == 0) { 418 CloseHandle(pi->pipe); 419 return 0; 420 } 421 lastBuf += dwRead; 422 goto again; 423 424 return 0; /* makes the compiler happy */ 425} 426 427int 428IsIn( 429 const char *string, 430 const char *substring) 431{ 432 return (strstr(string, substring) != NULL); 433} 434 435/* 436 * Find a specified #define by name. 437 * 438 * If the line is '#define TCL_VERSION "8.5"', it returns 85 as the result. 439 */ 440 441int 442GrepForDefine( 443 const char *file, 444 const char *string) 445{ 446 FILE *f; 447 char s1[51], s2[51], s3[51]; 448 int r = 0; 449 double d1; 450 451 f = fopen(file, "rt"); 452 if (f == NULL) { 453 return 0; 454 } 455 456 do { 457 r = fscanf(f, "%50s", s1); 458 if (r == 1 && !strcmp(s1, "#define")) { 459 /* 460 * Get next two words. 461 */ 462 463 r = fscanf(f, "%50s %50s", s2, s3); 464 if (r != 2) { 465 continue; 466 } 467 468 /* 469 * Is the first word what we're looking for? 470 */ 471 472 if (!strcmp(s2, string)) { 473 fclose(f); 474 475 /* 476 * Add 1 past first double quote char. "8.5" 477 */ 478 479 d1 = atof(s3 + 1); /* 8.5 */ 480 while (floor(d1) != d1) { 481 d1 *= 10.0; 482 } 483 return ((int) d1); /* 85 */ 484 } 485 } 486 } while (!feof(f)); 487 488 fclose(f); 489 return 0; 490} 491