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