2
* ----------------------------------------------------------------------------
5
* This is used to fix limitations within nmake and the environment.
7
* Copyright (c) 2002 by David Gravereaux.
8
* Copyright (c) 2006 by Pat Thoyts
10
* See the file "license.terms" for information on usage and redistribution of
11
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
* ----------------------------------------------------------------------------
15
#define _CRT_SECURE_NO_DEPRECATE
17
#define NO_SHLWAPI_GDI
18
#define NO_SHLWAPI_STREAM
19
#define NO_SHLWAPI_REG
21
#pragma comment (lib, "user32.lib")
22
#pragma comment (lib, "kernel32.lib")
23
#pragma comment (lib, "shlwapi.lib")
28
* This library is required for x64 builds with _some_ versions of MSVC
30
#if defined(_M_IA64) || defined(_M_AMD64)
31
#if _MSC_VER >= 1400 && _MSC_VER < 1500
32
#pragma comment(lib, "bufferoverflowU")
36
/* ISO hack for dumb VC++ */
38
#define snprintf _snprintf
44
static int CheckForCompilerFeature(const char *option);
45
static int CheckForLinkerFeature(const char **options, int count);
46
static int IsIn(const char *string, const char *substring);
47
static int SubstituteFile(const char *substs, const char *filename);
48
static int QualifyPath(const char *path);
49
static int LocateDependency(const char *keyfile);
50
static const char *GetVersionFromFile(const char *filename, const char *match, int numdots);
51
static DWORD WINAPI ReadFromPipe(LPVOID args);
56
#define STATICBUFFERSIZE 1000
59
char buffer[STATICBUFFERSIZE];
62
pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
63
pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
66
* exitcodes: 0 == no, 1 == yes, 2 == error
80
* Make sure children (cl.exe and link.exe) are kept quiet.
83
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
86
* Make sure the compiler and linker aren't effected by the outside world.
89
SetEnvironmentVariable("CL", "");
90
SetEnvironmentVariable("LINK", "");
92
if (argc > 1 && *argv[1] == '-') {
93
switch (*(argv[1]+1)) {
96
chars = snprintf(msg, sizeof(msg) - 1,
97
"usage: %s -c <compiler option>\n"
98
"Tests for whether cl.exe supports an option\n"
99
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
100
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
104
return CheckForCompilerFeature(argv[2]);
107
chars = snprintf(msg, sizeof(msg) - 1,
108
"usage: %s -l <linker option> ?<mandatory option> ...?\n"
109
"Tests for whether link.exe supports an option\n"
110
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
111
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
115
return CheckForLinkerFeature(&argv[2], argc-2);
118
chars = snprintf(msg, sizeof(msg) - 1,
119
"usage: %s -f <string> <substring>\n"
120
"Find a substring within another\n"
121
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
122
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
125
} else if (argc == 3) {
127
* If the string is blank, there is no match.
132
return IsIn(argv[2], argv[3]);
136
chars = snprintf(msg, sizeof(msg) - 1,
137
"usage: %s -s <substitutions file> <file>\n"
138
"Perform a set of string map type substutitions on a file\n"
141
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
145
return SubstituteFile(argv[2], argv[3]);
148
chars = snprintf(msg, sizeof(msg) - 1,
149
"usage: %s -V filename matchstring\n"
150
"Extract a version from a file:\n"
151
"eg: pkgIndex.tcl \"package ifneeded http\"",
153
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
157
s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0');
162
return 1; /* Version not found. Return non-0 exit code */
166
chars = snprintf(msg, sizeof(msg) - 1,
167
"usage: %s -Q path\n"
168
"Emit the fully qualified path\n"
169
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
170
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
174
return QualifyPath(argv[2]);
178
chars = snprintf(msg, sizeof(msg) - 1,
179
"usage: %s -L keypath\n"
180
"Emit the fully qualified path of directory containing keypath\n"
181
"exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]);
182
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
186
return LocateDependency(argv[2]);
189
chars = snprintf(msg, sizeof(msg) - 1,
190
"usage: %s -c|-f|-l|-Q|-s|-V ...\n"
191
"This is a little helper app to equalize shell differences between WinNT and\n"
192
"Win9x and get nmake.exe to accomplish its job.\n",
194
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
199
CheckForCompilerFeature(
203
PROCESS_INFORMATION pi;
204
SECURITY_ATTRIBUTES sa;
208
HANDLE hProcess, h, pipeThreads[2];
211
hProcess = GetCurrentProcess();
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;
219
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
220
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
221
sa.lpSecurityDescriptor = NULL;
222
sa.bInheritHandle = FALSE;
225
* Create a non-inheritible pipe.
228
CreatePipe(&Out.pipe, &h, &sa, 0);
231
* Dupe the write side, make it inheritible, and close the original.
234
DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
235
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
238
* Same as above, but for the error side.
241
CreatePipe(&Err.pipe, &h, &sa, 0);
242
DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
243
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
249
lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
252
* Append our option for testing
255
lstrcat(cmdline, option);
258
* Filename to compile, which exists, but is nothing and empty.
261
lstrcat(cmdline, " .\\nul");
264
NULL, /* Module name. */
265
cmdline, /* Command line. */
266
NULL, /* Process handle not inheritable. */
267
NULL, /* Thread handle not inheritable. */
268
TRUE, /* yes, inherit handles. */
269
DETACHED_PROCESS, /* No console for you. */
270
NULL, /* Use parent's environment block. */
271
NULL, /* Use parent's starting directory. */
272
&si, /* Pointer to STARTUPINFO structure. */
273
&pi); /* Pointer to PROCESS_INFORMATION structure. */
276
DWORD err = GetLastError();
277
int chars = snprintf(msg, sizeof(msg) - 1,
278
"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
280
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
281
FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
283
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
288
* Close our references to the write handles that have now been inherited.
291
CloseHandle(si.hStdOutput);
292
CloseHandle(si.hStdError);
294
WaitForInputIdle(pi.hProcess, 5000);
295
CloseHandle(pi.hThread);
298
* Start the pipe reader threads.
301
pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
302
pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
305
* Block waiting for the process to end.
308
WaitForSingleObject(pi.hProcess, INFINITE);
309
CloseHandle(pi.hProcess);
312
* Wait for our pipe to get done reading, should it be a little slow.
315
WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
316
CloseHandle(pipeThreads[0]);
317
CloseHandle(pipeThreads[1]);
320
* Look for the commandline warning code in both streams.
321
* - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
324
return !(strstr(Out.buffer, "D4002") != NULL
325
|| strstr(Err.buffer, "D4002") != NULL
326
|| strstr(Out.buffer, "D9002") != NULL
327
|| strstr(Err.buffer, "D9002") != NULL
328
|| strstr(Out.buffer, "D2021") != NULL
329
|| strstr(Err.buffer, "D2021") != NULL);
333
CheckForLinkerFeature(
334
const char **options,
338
PROCESS_INFORMATION pi;
339
SECURITY_ATTRIBUTES sa;
343
HANDLE hProcess, h, pipeThreads[2];
347
hProcess = GetCurrentProcess();
349
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
350
ZeroMemory(&si, sizeof(STARTUPINFO));
351
si.cb = sizeof(STARTUPINFO);
352
si.dwFlags = STARTF_USESTDHANDLES;
353
si.hStdInput = INVALID_HANDLE_VALUE;
355
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
356
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
357
sa.lpSecurityDescriptor = NULL;
358
sa.bInheritHandle = TRUE;
361
* Create a non-inheritible pipe.
364
CreatePipe(&Out.pipe, &h, &sa, 0);
367
* Dupe the write side, make it inheritible, and close the original.
370
DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
371
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
374
* Same as above, but for the error side.
377
CreatePipe(&Err.pipe, &h, &sa, 0);
378
DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
379
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
385
lstrcpy(cmdline, "link.exe -nologo ");
388
* Append our option for testing.
391
for (i = 0; i < count; i++) {
392
lstrcat(cmdline, " \"");
393
lstrcat(cmdline, options[i]);
394
lstrcat(cmdline, "\"");
398
NULL, /* Module name. */
399
cmdline, /* Command line. */
400
NULL, /* Process handle not inheritable. */
401
NULL, /* Thread handle not inheritable. */
402
TRUE, /* yes, inherit handles. */
403
DETACHED_PROCESS, /* No console for you. */
404
NULL, /* Use parent's environment block. */
405
NULL, /* Use parent's starting directory. */
406
&si, /* Pointer to STARTUPINFO structure. */
407
&pi); /* Pointer to PROCESS_INFORMATION structure. */
410
DWORD err = GetLastError();
411
int chars = snprintf(msg, sizeof(msg) - 1,
412
"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
414
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
415
FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
417
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
422
* Close our references to the write handles that have now been inherited.
425
CloseHandle(si.hStdOutput);
426
CloseHandle(si.hStdError);
428
WaitForInputIdle(pi.hProcess, 5000);
429
CloseHandle(pi.hThread);
432
* Start the pipe reader threads.
435
pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
436
pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
439
* Block waiting for the process to end.
442
WaitForSingleObject(pi.hProcess, INFINITE);
443
CloseHandle(pi.hProcess);
446
* Wait for our pipe to get done reading, should it be a little slow.
449
WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
450
CloseHandle(pipeThreads[0]);
451
CloseHandle(pipeThreads[1]);
454
* Look for the commandline warning code in the stderr stream.
457
return !(strstr(Out.buffer, "LNK1117") != NULL ||
458
strstr(Err.buffer, "LNK1117") != NULL ||
459
strstr(Out.buffer, "LNK4044") != NULL ||
460
strstr(Err.buffer, "LNK4044") != NULL ||
461
strstr(Out.buffer, "LNK4224") != NULL ||
462
strstr(Err.buffer, "LNK4224") != NULL);
469
pipeinfo *pi = (pipeinfo *) args;
470
char *lastBuf = pi->buffer;
475
if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
476
CloseHandle(pi->pipe);
479
ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
480
if (!ok || dwRead == 0) {
481
CloseHandle(pi->pipe);
487
return 0; /* makes the compiler happy */
493
const char *substring)
495
return (strstr(string, substring) != NULL);
499
* GetVersionFromFile --
500
* Looks for a match string in a file and then returns the version
501
* following the match where a version is anything acceptable to
502
* package provide or package ifneeded.
507
const char *filename,
511
size_t cbBuffer = 100;
512
static char szBuffer[100];
513
char *szResult = NULL;
514
FILE *fp = fopen(filename, "rt");
518
* Read data until we see our match string.
521
while (fgets(szBuffer, cbBuffer, fp) != NULL) {
524
p = strstr(szBuffer, match);
527
* Skip to first digit after the match.
531
while (*p && !isdigit(*p)) {
536
* Find ending whitespace.
540
while (*q && (strchr("0123456789.ab", *q)) && ((!strchr(".ab", *q)
541
&& (!strchr("ab", q[-1])) || --numdots))) {
545
memcpy(szBuffer, p, q - p);
557
* List helpers for the SubstituteFile function
560
typedef struct list_item_t {
561
struct list_item_t *nextPtr;
566
/* insert a list item into the list (list may be null) */
568
list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
570
list_item_t *itemPtr = malloc(sizeof(list_item_t));
572
itemPtr->key = strdup(key);
573
itemPtr->value = strdup(value);
574
itemPtr->nextPtr = NULL;
577
listPtrPtr = &(*listPtrPtr)->nextPtr;
579
*listPtrPtr = itemPtr;
585
list_free(list_item_t **listPtrPtr)
587
list_item_t *tmpPtr, *listPtr = *listPtrPtr;
590
listPtr = listPtr->nextPtr;
599
* As windows doesn't provide anything useful like sed and it's unreliable
600
* to use the tclsh you are building against (consider x-platform builds -
601
* eg compiling AMD64 target from IX86) we provide a simple substitution
602
* option here to handle autoconf style substitutions.
603
* The substitution file is whitespace and line delimited. The file should
604
* consist of lines matching the regular expression:
607
* Usage is something like:
608
* nmakehlp -S << $** > $@
609
* @PACKAGE_NAME@ $(PACKAGE_NAME)
610
* @PACKAGE_VERSION@ $(PACKAGE_VERSION)
616
const char *substitutions,
617
const char *filename)
619
size_t cbBuffer = 1024;
620
static char szBuffer[1024], szCopy[1024];
621
char *szResult = NULL;
622
list_item_t *substPtr = NULL;
625
fp = fopen(filename, "rt");
629
* Build a list of substutitions from the first filename
632
sp = fopen(substitutions, "rt");
634
while (fgets(szBuffer, cbBuffer, sp) != NULL) {
635
unsigned char *ks, *ke, *vs, *ve;
636
ks = (unsigned char*)szBuffer;
637
while (ks && *ks && isspace(*ks)) ++ks;
639
while (ke && *ke && !isspace(*ke)) ++ke;
641
while (vs && *vs && isspace(*vs)) ++vs;
643
while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
645
list_insert(&substPtr, (char*)ks, (char*)vs);
650
/* debug: dump the list */
654
list_item_t *p = NULL;
655
for (p = substPtr; p != NULL; p = p->nextPtr, ++n) {
656
fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value);
662
* Run the substitutions over each line of the input
665
while (fgets(szBuffer, cbBuffer, fp) != NULL) {
666
list_item_t *p = NULL;
667
for (p = substPtr; p != NULL; p = p->nextPtr) {
668
char *m = strstr(szBuffer, p->key);
673
while (op != m) *cp++ = *op++;
675
while (sp && *sp) *cp++ = *sp++;
676
op += strlen(p->key);
677
while (*op) *cp++ = *op++;
679
memcpy(szBuffer, szCopy, sizeof(szCopy));
685
list_free(&substPtr);
694
* This composes the current working directory with a provided path
695
* and returns the fully qualified and normalized path.
696
* Mostly needed to setup paths for testing.
703
char szCwd[MAX_PATH + 1];
704
char szTmp[MAX_PATH + 1];
706
GetCurrentDirectory(MAX_PATH, szCwd);
707
while ((p = strchr(szPath, '/')) && *p)
709
PathCombine(szTmp, szCwd, szPath);
710
PathCanonicalize(szCwd, szTmp);
711
printf("%s\n", szCwd);
716
* Implements LocateDependency for a single directory. See that command
717
* for an explanation.
718
* Returns 0 if found after printing the directory.
719
* Returns 1 if not found but no errors.
720
* Returns 2 on any kind of error
721
* Basically, these are used as exit codes for the process.
723
static int LocateDependencyHelper(const char *dir, const char *keypath)
726
char path[MAX_PATH+1];
727
int dirlen, keylen, ret;
728
WIN32_FIND_DATA finfo;
730
if (dir == NULL || keypath == NULL)
731
return 2; /* Have no real error reporting mechanism into nmake */
732
dirlen = strlen(dir);
733
if ((dirlen + 3) > sizeof(path))
735
strncpy(path, dir, dirlen);
736
strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */
737
keylen = strlen(keypath);
739
#if 0 /* This function is not available in Visual C++ 6 */
741
* Use numerics 0 -> FindExInfoStandard,
742
* 1 -> FindExSearchLimitToDirectories,
743
* as these are not defined in Visual C++ 6
745
hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0);
747
hSearch = FindFirstFile(path, &finfo);
749
if (hSearch == INVALID_HANDLE_VALUE)
750
return 1; /* Not found */
752
/* Loop through all subdirs checking if the keypath is under there */
753
ret = 1; /* Assume not found */
757
* We need to check it is a directory despite the
758
* FindExSearchLimitToDirectories in the above call. See SDK docs
760
if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
762
sublen = strlen(finfo.cFileName);
763
if ((dirlen+1+sublen+1+keylen+1) > sizeof(path))
764
continue; /* Path does not fit, assume not matched */
765
strncpy(path+dirlen+1, finfo.cFileName, sublen);
766
path[dirlen+1+sublen] = '\\';
767
strncpy(path+dirlen+1+sublen+1, keypath, keylen+1);
768
if (PathFileExists(path)) {
769
/* Found a match, print to stdout */
770
path[dirlen+1+sublen] = '\0';
775
} while (FindNextFile(hSearch, &finfo));
781
* LocateDependency --
783
* Locates a dependency for a package.
784
* keypath - a relative path within the package directory
785
* that is used to confirm it is the correct directory.
786
* The search path for the package directory is currently only
787
* the parent and grandparent of the current working directory.
788
* If found, the command prints
789
* name_DIRPATH=<full path of located directory>
790
* and returns 0. If not found, does not print anything and returns 1.
792
static int LocateDependency(const char *keypath)
795
static char *paths[] = {"..", "..\\..", "..\\..\\.."};
797
for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) {
798
ret = LocateDependencyHelper(paths[i], keypath);
811
* indent-tabs-mode: t