sl@0: /* sl@0: * ---------------------------------------------------------------------------- sl@0: * nmakehlp.c -- sl@0: * sl@0: * This is used to fix limitations within nmake and the environment. sl@0: * sl@0: * Copyright (c) 2002 by David Gravereaux. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * ---------------------------------------------------------------------------- sl@0: * RCS: @(#) $Id: nmakehlp.c,v 1.1.4.4 2006/10/18 08:49:33 patthoyts Exp $ sl@0: * ---------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: #define _CRT_SECURE_NO_DEPRECATE sl@0: #include sl@0: #pragma comment (lib, "user32.lib") sl@0: #pragma comment (lib, "kernel32.lib") sl@0: #include sl@0: #include sl@0: #if defined(_M_IA64) || defined(_M_AMD64) sl@0: #pragma comment(lib, "bufferoverflowU") sl@0: #endif sl@0: sl@0: /* ISO hack for dumb VC++ */ sl@0: #ifdef _MSC_VER sl@0: #define snprintf _snprintf sl@0: #endif sl@0: sl@0: sl@0: sl@0: /* protos */ sl@0: sl@0: int CheckForCompilerFeature(const char *option); sl@0: int CheckForLinkerFeature(const char *option); sl@0: int IsIn(const char *string, const char *substring); sl@0: int GrepForDefine(const char *file, const char *string); sl@0: DWORD WINAPI ReadFromPipe(LPVOID args); sl@0: sl@0: /* globals */ sl@0: sl@0: #define CHUNK 25 sl@0: #define STATICBUFFERSIZE 1000 sl@0: typedef struct { sl@0: HANDLE pipe; sl@0: char buffer[STATICBUFFERSIZE]; sl@0: } pipeinfo; sl@0: sl@0: pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; sl@0: pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; sl@0: sl@0: /* sl@0: * exitcodes: 0 == no, 1 == yes, 2 == error sl@0: */ sl@0: sl@0: int sl@0: main( sl@0: int argc, sl@0: char *argv[]) sl@0: { sl@0: char msg[300]; sl@0: DWORD dwWritten; sl@0: int chars; sl@0: sl@0: /* sl@0: * Make sure children (cl.exe and link.exe) are kept quiet. sl@0: */ sl@0: sl@0: SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); sl@0: sl@0: /* sl@0: * Make sure the compiler and linker aren't effected by the outside world. sl@0: */ sl@0: sl@0: SetEnvironmentVariable("CL", ""); sl@0: SetEnvironmentVariable("LINK", ""); sl@0: sl@0: if (argc > 1 && *argv[1] == '-') { sl@0: switch (*(argv[1]+1)) { sl@0: case 'c': sl@0: if (argc != 3) { sl@0: chars = snprintf(msg, sizeof(msg) - 1, sl@0: "usage: %s -c \n" sl@0: "Tests for whether cl.exe supports an option\n" sl@0: "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, sl@0: &dwWritten, NULL); sl@0: return 2; sl@0: } sl@0: return CheckForCompilerFeature(argv[2]); sl@0: case 'l': sl@0: if (argc != 3) { sl@0: chars = snprintf(msg, sizeof(msg) - 1, sl@0: "usage: %s -l \n" sl@0: "Tests for whether link.exe supports an option\n" sl@0: "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, sl@0: &dwWritten, NULL); sl@0: return 2; sl@0: } sl@0: return CheckForLinkerFeature(argv[2]); sl@0: case 'f': sl@0: if (argc == 2) { sl@0: chars = snprintf(msg, sizeof(msg) - 1, sl@0: "usage: %s -f \n" sl@0: "Find a substring within another\n" sl@0: "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, sl@0: &dwWritten, NULL); sl@0: return 2; sl@0: } else if (argc == 3) { sl@0: /* sl@0: * If the string is blank, there is no match. sl@0: */ sl@0: sl@0: return 0; sl@0: } else { sl@0: return IsIn(argv[2], argv[3]); sl@0: } sl@0: case 'g': sl@0: if (argc == 2) { sl@0: chars = snprintf(msg, sizeof(msg) - 1, sl@0: "usage: %s -g \n" sl@0: "grep for a #define\n" sl@0: "exitcodes: integer of the found string (no decimals)\n", sl@0: argv[0]); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, sl@0: &dwWritten, NULL); sl@0: return 2; sl@0: } sl@0: return GrepForDefine(argv[2], argv[3]); sl@0: } sl@0: } sl@0: chars = snprintf(msg, sizeof(msg) - 1, sl@0: "usage: %s -c|-l|-f ...\n" sl@0: "This is a little helper app to equalize shell differences between WinNT and\n" sl@0: "Win9x and get nmake.exe to accomplish its job.\n", sl@0: argv[0]); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); sl@0: return 2; sl@0: } sl@0: sl@0: int sl@0: CheckForCompilerFeature( sl@0: const char *option) sl@0: { sl@0: STARTUPINFO si; sl@0: PROCESS_INFORMATION pi; sl@0: SECURITY_ATTRIBUTES sa; sl@0: DWORD threadID; sl@0: char msg[300]; sl@0: BOOL ok; sl@0: HANDLE hProcess, h, pipeThreads[2]; sl@0: char cmdline[100]; sl@0: sl@0: hProcess = GetCurrentProcess(); sl@0: sl@0: ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); sl@0: ZeroMemory(&si, sizeof(STARTUPINFO)); sl@0: si.cb = sizeof(STARTUPINFO); sl@0: si.dwFlags = STARTF_USESTDHANDLES; sl@0: si.hStdInput = INVALID_HANDLE_VALUE; sl@0: sl@0: ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sl@0: sa.nLength = sizeof(SECURITY_ATTRIBUTES); sl@0: sa.lpSecurityDescriptor = NULL; sl@0: sa.bInheritHandle = FALSE; sl@0: sl@0: /* sl@0: * Create a non-inheritible pipe. sl@0: */ sl@0: sl@0: CreatePipe(&Out.pipe, &h, &sa, 0); sl@0: sl@0: /* sl@0: * Dupe the write side, make it inheritible, and close the original. sl@0: */ sl@0: sl@0: DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, sl@0: DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); sl@0: sl@0: /* sl@0: * Same as above, but for the error side. sl@0: */ sl@0: sl@0: CreatePipe(&Err.pipe, &h, &sa, 0); sl@0: DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, sl@0: DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); sl@0: sl@0: /* sl@0: * Base command line. sl@0: */ sl@0: sl@0: lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X "); sl@0: sl@0: /* sl@0: * Append our option for testing sl@0: */ sl@0: sl@0: lstrcat(cmdline, option); sl@0: sl@0: /* sl@0: * Filename to compile, which exists, but is nothing and empty. sl@0: */ sl@0: sl@0: lstrcat(cmdline, " .\\nul"); sl@0: sl@0: ok = CreateProcess( sl@0: NULL, /* Module name. */ sl@0: cmdline, /* Command line. */ sl@0: NULL, /* Process handle not inheritable. */ sl@0: NULL, /* Thread handle not inheritable. */ sl@0: TRUE, /* yes, inherit handles. */ sl@0: DETACHED_PROCESS, /* No console for you. */ sl@0: NULL, /* Use parent's environment block. */ sl@0: NULL, /* Use parent's starting directory. */ sl@0: &si, /* Pointer to STARTUPINFO structure. */ sl@0: &pi); /* Pointer to PROCESS_INFORMATION structure. */ sl@0: sl@0: if (!ok) { sl@0: DWORD err = GetLastError(); sl@0: int chars = snprintf(msg, sizeof(msg) - 1, sl@0: "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); sl@0: sl@0: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| sl@0: FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], sl@0: (300-chars), 0); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); sl@0: return 2; sl@0: } sl@0: sl@0: /* sl@0: * Close our references to the write handles that have now been inherited. sl@0: */ sl@0: sl@0: CloseHandle(si.hStdOutput); sl@0: CloseHandle(si.hStdError); sl@0: sl@0: WaitForInputIdle(pi.hProcess, 5000); sl@0: CloseHandle(pi.hThread); sl@0: sl@0: /* sl@0: * Start the pipe reader threads. sl@0: */ sl@0: sl@0: pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); sl@0: pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); sl@0: sl@0: /* sl@0: * Block waiting for the process to end. sl@0: */ sl@0: sl@0: WaitForSingleObject(pi.hProcess, INFINITE); sl@0: CloseHandle(pi.hProcess); sl@0: sl@0: /* sl@0: * Wait for our pipe to get done reading, should it be a little slow. sl@0: */ sl@0: sl@0: WaitForMultipleObjects(2, pipeThreads, TRUE, 500); sl@0: CloseHandle(pipeThreads[0]); sl@0: CloseHandle(pipeThreads[1]); sl@0: sl@0: /* sl@0: * Look for the commandline warning code in both streams. sl@0: * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002. sl@0: */ sl@0: sl@0: return !(strstr(Out.buffer, "D4002") != NULL sl@0: || strstr(Err.buffer, "D4002") != NULL sl@0: || strstr(Out.buffer, "D9002") != NULL sl@0: || strstr(Err.buffer, "D9002") != NULL); sl@0: } sl@0: sl@0: int sl@0: CheckForLinkerFeature( sl@0: const char *option) sl@0: { sl@0: STARTUPINFO si; sl@0: PROCESS_INFORMATION pi; sl@0: SECURITY_ATTRIBUTES sa; sl@0: DWORD threadID; sl@0: char msg[300]; sl@0: BOOL ok; sl@0: HANDLE hProcess, h, pipeThreads[2]; sl@0: char cmdline[100]; sl@0: sl@0: hProcess = GetCurrentProcess(); sl@0: sl@0: ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); sl@0: ZeroMemory(&si, sizeof(STARTUPINFO)); sl@0: si.cb = sizeof(STARTUPINFO); sl@0: si.dwFlags = STARTF_USESTDHANDLES; sl@0: si.hStdInput = INVALID_HANDLE_VALUE; sl@0: sl@0: ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sl@0: sa.nLength = sizeof(SECURITY_ATTRIBUTES); sl@0: sa.lpSecurityDescriptor = NULL; sl@0: sa.bInheritHandle = TRUE; sl@0: sl@0: /* sl@0: * Create a non-inheritible pipe. sl@0: */ sl@0: sl@0: CreatePipe(&Out.pipe, &h, &sa, 0); sl@0: sl@0: /* sl@0: * Dupe the write side, make it inheritible, and close the original. sl@0: */ sl@0: sl@0: DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, sl@0: DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); sl@0: sl@0: /* sl@0: * Same as above, but for the error side. sl@0: */ sl@0: sl@0: CreatePipe(&Err.pipe, &h, &sa, 0); sl@0: DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, sl@0: DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); sl@0: sl@0: /* sl@0: * Base command line. sl@0: */ sl@0: sl@0: lstrcpy(cmdline, "link.exe -nologo "); sl@0: sl@0: /* sl@0: * Append our option for testing. sl@0: */ sl@0: sl@0: lstrcat(cmdline, option); sl@0: sl@0: ok = CreateProcess( sl@0: NULL, /* Module name. */ sl@0: cmdline, /* Command line. */ sl@0: NULL, /* Process handle not inheritable. */ sl@0: NULL, /* Thread handle not inheritable. */ sl@0: TRUE, /* yes, inherit handles. */ sl@0: DETACHED_PROCESS, /* No console for you. */ sl@0: NULL, /* Use parent's environment block. */ sl@0: NULL, /* Use parent's starting directory. */ sl@0: &si, /* Pointer to STARTUPINFO structure. */ sl@0: &pi); /* Pointer to PROCESS_INFORMATION structure. */ sl@0: sl@0: if (!ok) { sl@0: DWORD err = GetLastError(); sl@0: int chars = snprintf(msg, sizeof(msg) - 1, sl@0: "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); sl@0: sl@0: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| sl@0: FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], sl@0: (300-chars), 0); sl@0: WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); sl@0: return 2; sl@0: } sl@0: sl@0: /* sl@0: * Close our references to the write handles that have now been inherited. sl@0: */ sl@0: sl@0: CloseHandle(si.hStdOutput); sl@0: CloseHandle(si.hStdError); sl@0: sl@0: WaitForInputIdle(pi.hProcess, 5000); sl@0: CloseHandle(pi.hThread); sl@0: sl@0: /* sl@0: * Start the pipe reader threads. sl@0: */ sl@0: sl@0: pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); sl@0: pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); sl@0: sl@0: /* sl@0: * Block waiting for the process to end. sl@0: */ sl@0: sl@0: WaitForSingleObject(pi.hProcess, INFINITE); sl@0: CloseHandle(pi.hProcess); sl@0: sl@0: /* sl@0: * Wait for our pipe to get done reading, should it be a little slow. sl@0: */ sl@0: sl@0: WaitForMultipleObjects(2, pipeThreads, TRUE, 500); sl@0: CloseHandle(pipeThreads[0]); sl@0: CloseHandle(pipeThreads[1]); sl@0: sl@0: /* sl@0: * Look for the commandline warning code in the stderr stream. sl@0: */ sl@0: sl@0: return !(strstr(Out.buffer, "LNK1117") != NULL || sl@0: strstr(Err.buffer, "LNK1117") != NULL || sl@0: strstr(Out.buffer, "LNK4044") != NULL || sl@0: strstr(Err.buffer, "LNK4044") != NULL); sl@0: } sl@0: sl@0: DWORD WINAPI sl@0: ReadFromPipe( sl@0: LPVOID args) sl@0: { sl@0: pipeinfo *pi = (pipeinfo *) args; sl@0: char *lastBuf = pi->buffer; sl@0: DWORD dwRead; sl@0: BOOL ok; sl@0: sl@0: again: sl@0: if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) { sl@0: CloseHandle(pi->pipe); sl@0: return (DWORD)-1; sl@0: } sl@0: ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L); sl@0: if (!ok || dwRead == 0) { sl@0: CloseHandle(pi->pipe); sl@0: return 0; sl@0: } sl@0: lastBuf += dwRead; sl@0: goto again; sl@0: sl@0: return 0; /* makes the compiler happy */ sl@0: } sl@0: sl@0: int sl@0: IsIn( sl@0: const char *string, sl@0: const char *substring) sl@0: { sl@0: return (strstr(string, substring) != NULL); sl@0: } sl@0: sl@0: /* sl@0: * Find a specified #define by name. sl@0: * sl@0: * If the line is '#define TCL_VERSION "8.5"', it returns 85 as the result. sl@0: */ sl@0: sl@0: int sl@0: GrepForDefine( sl@0: const char *file, sl@0: const char *string) sl@0: { sl@0: FILE *f; sl@0: char s1[51], s2[51], s3[51]; sl@0: int r = 0; sl@0: double d1; sl@0: sl@0: f = fopen(file, "rt"); sl@0: if (f == NULL) { sl@0: return 0; sl@0: } sl@0: sl@0: do { sl@0: r = fscanf(f, "%50s", s1); sl@0: if (r == 1 && !strcmp(s1, "#define")) { sl@0: /* sl@0: * Get next two words. sl@0: */ sl@0: sl@0: r = fscanf(f, "%50s %50s", s2, s3); sl@0: if (r != 2) { sl@0: continue; sl@0: } sl@0: sl@0: /* sl@0: * Is the first word what we're looking for? sl@0: */ sl@0: sl@0: if (!strcmp(s2, string)) { sl@0: fclose(f); sl@0: sl@0: /* sl@0: * Add 1 past first double quote char. "8.5" sl@0: */ sl@0: sl@0: d1 = atof(s3 + 1); /* 8.5 */ sl@0: while (floor(d1) != d1) { sl@0: d1 *= 10.0; sl@0: } sl@0: return ((int) d1); /* 85 */ sl@0: } sl@0: } sl@0: } while (!feof(f)); sl@0: sl@0: fclose(f); sl@0: return 0; sl@0: }