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
18 #pragma comment (lib, "user32.lib")
19 #pragma comment (lib, "kernel32.lib")
25 * This library is required for x64 builds with _some_ versions of MSVC
27 #if defined(_M_IA64) || defined(_M_AMD64)
28 #if _MSC_VER >= 1400 && _MSC_VER < 1500
29 #pragma comment(lib, "bufferoverflowU")
33 /* ISO hack for dumb VC++ */
35 #define snprintf _snprintf
41 static int CheckForCompilerFeature(const char *option
);
42 static int CheckForLinkerFeature(char **options
, int count
);
43 static int IsIn(const char *string
, const char *substring
);
44 static int SubstituteFile(const char *substs
, const char *filename
);
45 static int QualifyPath(const char *path
);
46 static int LocateDependency(const char *keyfile
);
47 static const char *GetVersionFromFile(const char *filename
, const char *match
, int numdots
);
48 static DWORD WINAPI
ReadFromPipe(LPVOID args
);
53 #define STATICBUFFERSIZE 1000
56 char buffer
[STATICBUFFERSIZE
];
59 pipeinfo Out
= {INVALID_HANDLE_VALUE
, ""};
60 pipeinfo Err
= {INVALID_HANDLE_VALUE
, ""};
63 * exitcodes: 0 == no, 1 == yes, 2 == error
77 * Make sure children (cl.exe and link.exe) are kept quiet.
80 SetErrorMode(SEM_FAILCRITICALERRORS
| SEM_NOOPENFILEERRORBOX
);
83 * Make sure the compiler and linker aren't effected by the outside world.
86 SetEnvironmentVariable("CL", "");
87 SetEnvironmentVariable("LINK", "");
89 if (argc
> 1 && *argv
[1] == '-') {
90 switch (*(argv
[1]+1)) {
93 chars
= snprintf(msg
, sizeof(msg
) - 1,
94 "usage: %s -c <compiler option>\n"
95 "Tests for whether cl.exe supports an option\n"
96 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv
[0]);
97 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
101 return CheckForCompilerFeature(argv
[2]);
104 chars
= snprintf(msg
, sizeof(msg
) - 1,
105 "usage: %s -l <linker option> ?<mandatory option> ...?\n"
106 "Tests for whether link.exe supports an option\n"
107 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv
[0]);
108 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
112 return CheckForLinkerFeature(&argv
[2], argc
-2);
115 chars
= snprintf(msg
, sizeof(msg
) - 1,
116 "usage: %s -f <string> <substring>\n"
117 "Find a substring within another\n"
118 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv
[0]);
119 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
122 } else if (argc
== 3) {
124 * If the string is blank, there is no match.
129 return IsIn(argv
[2], argv
[3]);
133 chars
= snprintf(msg
, sizeof(msg
) - 1,
134 "usage: %s -s <substitutions file> <file>\n"
135 "Perform a set of string map type substutitions on a file\n"
138 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
142 return SubstituteFile(argv
[2], argv
[3]);
145 chars
= snprintf(msg
, sizeof(msg
) - 1,
146 "usage: %s -V filename matchstring\n"
147 "Extract a version from a file:\n"
148 "eg: pkgIndex.tcl \"package ifneeded http\"",
150 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
154 s
= GetVersionFromFile(argv
[2], argv
[3], *(argv
[1]+2) - '0');
159 return 1; /* Version not found. Return non-0 exit code */
163 chars
= snprintf(msg
, sizeof(msg
) - 1,
164 "usage: %s -Q path\n"
165 "Emit the fully qualified path\n"
166 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv
[0]);
167 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
171 return QualifyPath(argv
[2]);
175 chars
= snprintf(msg
, sizeof(msg
) - 1,
176 "usage: %s -L keypath\n"
177 "Emit the fully qualified path of directory containing keypath\n"
178 "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv
[0]);
179 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
,
183 return LocateDependency(argv
[2]);
186 chars
= snprintf(msg
, sizeof(msg
) - 1,
187 "usage: %s -c|-f|-l|-Q|-s|-V ...\n"
188 "This is a little helper app to equalize shell differences between WinNT and\n"
189 "Win9x and get nmake.exe to accomplish its job.\n",
191 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, chars
, &dwWritten
, NULL
);
196 CheckForCompilerFeature(
200 PROCESS_INFORMATION pi
;
201 SECURITY_ATTRIBUTES sa
;
205 HANDLE hProcess
, h
, pipeThreads
[2];
208 hProcess
= GetCurrentProcess();
210 ZeroMemory(&pi
, sizeof(PROCESS_INFORMATION
));
211 ZeroMemory(&si
, sizeof(STARTUPINFO
));
212 si
.cb
= sizeof(STARTUPINFO
);
213 si
.dwFlags
= STARTF_USESTDHANDLES
;
214 si
.hStdInput
= INVALID_HANDLE_VALUE
;
216 ZeroMemory(&sa
, sizeof(SECURITY_ATTRIBUTES
));
217 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
218 sa
.lpSecurityDescriptor
= NULL
;
219 sa
.bInheritHandle
= FALSE
;
222 * Create a non-inheritible pipe.
225 CreatePipe(&Out
.pipe
, &h
, &sa
, 0);
228 * Dupe the write side, make it inheritible, and close the original.
231 DuplicateHandle(hProcess
, h
, hProcess
, &si
.hStdOutput
, 0, TRUE
,
232 DUPLICATE_SAME_ACCESS
| DUPLICATE_CLOSE_SOURCE
);
235 * Same as above, but for the error side.
238 CreatePipe(&Err
.pipe
, &h
, &sa
, 0);
239 DuplicateHandle(hProcess
, h
, hProcess
, &si
.hStdError
, 0, TRUE
,
240 DUPLICATE_SAME_ACCESS
| DUPLICATE_CLOSE_SOURCE
);
246 lstrcpy(cmdline
, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
249 * Append our option for testing
252 lstrcat(cmdline
, option
);
255 * Filename to compile, which exists, but is nothing and empty.
258 lstrcat(cmdline
, " .\\nul");
261 NULL
, /* Module name. */
262 cmdline
, /* Command line. */
263 NULL
, /* Process handle not inheritable. */
264 NULL
, /* Thread handle not inheritable. */
265 TRUE
, /* yes, inherit handles. */
266 DETACHED_PROCESS
, /* No console for you. */
267 NULL
, /* Use parent's environment block. */
268 NULL
, /* Use parent's starting directory. */
269 &si
, /* Pointer to STARTUPINFO structure. */
270 &pi
); /* Pointer to PROCESS_INFORMATION structure. */
273 DWORD err
= GetLastError();
274 int chars
= snprintf(msg
, sizeof(msg
) - 1,
275 "Tried to launch: \"%s\", but got error [%u]: ", cmdline
, err
);
277 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_IGNORE_INSERTS
|
278 FORMAT_MESSAGE_MAX_WIDTH_MASK
, 0L, err
, 0, (LPSTR
)&msg
[chars
],
280 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, lstrlen(msg
), &err
,NULL
);
285 * Close our references to the write handles that have now been inherited.
288 CloseHandle(si
.hStdOutput
);
289 CloseHandle(si
.hStdError
);
291 WaitForInputIdle(pi
.hProcess
, 5000);
292 CloseHandle(pi
.hThread
);
295 * Start the pipe reader threads.
298 pipeThreads
[0] = CreateThread(NULL
, 0, ReadFromPipe
, &Out
, 0, &threadID
);
299 pipeThreads
[1] = CreateThread(NULL
, 0, ReadFromPipe
, &Err
, 0, &threadID
);
302 * Block waiting for the process to end.
305 WaitForSingleObject(pi
.hProcess
, INFINITE
);
306 CloseHandle(pi
.hProcess
);
309 * Wait for our pipe to get done reading, should it be a little slow.
312 WaitForMultipleObjects(2, pipeThreads
, TRUE
, 500);
313 CloseHandle(pipeThreads
[0]);
314 CloseHandle(pipeThreads
[1]);
317 * Look for the commandline warning code in both streams.
318 * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
321 return !(strstr(Out
.buffer
, "D4002") != NULL
322 || strstr(Err
.buffer
, "D4002") != NULL
323 || strstr(Out
.buffer
, "D9002") != NULL
324 || strstr(Err
.buffer
, "D9002") != NULL
325 || strstr(Out
.buffer
, "D2021") != NULL
326 || strstr(Err
.buffer
, "D2021") != NULL
);
330 CheckForLinkerFeature(
335 PROCESS_INFORMATION pi
;
336 SECURITY_ATTRIBUTES sa
;
340 HANDLE hProcess
, h
, pipeThreads
[2];
344 hProcess
= GetCurrentProcess();
346 ZeroMemory(&pi
, sizeof(PROCESS_INFORMATION
));
347 ZeroMemory(&si
, sizeof(STARTUPINFO
));
348 si
.cb
= sizeof(STARTUPINFO
);
349 si
.dwFlags
= STARTF_USESTDHANDLES
;
350 si
.hStdInput
= INVALID_HANDLE_VALUE
;
352 ZeroMemory(&sa
, sizeof(SECURITY_ATTRIBUTES
));
353 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
354 sa
.lpSecurityDescriptor
= NULL
;
355 sa
.bInheritHandle
= TRUE
;
358 * Create a non-inheritible pipe.
361 CreatePipe(&Out
.pipe
, &h
, &sa
, 0);
364 * Dupe the write side, make it inheritible, and close the original.
367 DuplicateHandle(hProcess
, h
, hProcess
, &si
.hStdOutput
, 0, TRUE
,
368 DUPLICATE_SAME_ACCESS
| DUPLICATE_CLOSE_SOURCE
);
371 * Same as above, but for the error side.
374 CreatePipe(&Err
.pipe
, &h
, &sa
, 0);
375 DuplicateHandle(hProcess
, h
, hProcess
, &si
.hStdError
, 0, TRUE
,
376 DUPLICATE_SAME_ACCESS
| DUPLICATE_CLOSE_SOURCE
);
382 lstrcpy(cmdline
, "link.exe -nologo ");
385 * Append our option for testing.
388 for (i
= 0; i
< count
; i
++) {
389 lstrcat(cmdline
, " \"");
390 lstrcat(cmdline
, options
[i
]);
391 lstrcat(cmdline
, "\"");
395 NULL
, /* Module name. */
396 cmdline
, /* Command line. */
397 NULL
, /* Process handle not inheritable. */
398 NULL
, /* Thread handle not inheritable. */
399 TRUE
, /* yes, inherit handles. */
400 DETACHED_PROCESS
, /* No console for you. */
401 NULL
, /* Use parent's environment block. */
402 NULL
, /* Use parent's starting directory. */
403 &si
, /* Pointer to STARTUPINFO structure. */
404 &pi
); /* Pointer to PROCESS_INFORMATION structure. */
407 DWORD err
= GetLastError();
408 int chars
= snprintf(msg
, sizeof(msg
) - 1,
409 "Tried to launch: \"%s\", but got error [%u]: ", cmdline
, err
);
411 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_IGNORE_INSERTS
|
412 FORMAT_MESSAGE_MAX_WIDTH_MASK
, 0L, err
, 0, (LPSTR
)&msg
[chars
],
414 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), msg
, lstrlen(msg
), &err
,NULL
);
419 * Close our references to the write handles that have now been inherited.
422 CloseHandle(si
.hStdOutput
);
423 CloseHandle(si
.hStdError
);
425 WaitForInputIdle(pi
.hProcess
, 5000);
426 CloseHandle(pi
.hThread
);
429 * Start the pipe reader threads.
432 pipeThreads
[0] = CreateThread(NULL
, 0, ReadFromPipe
, &Out
, 0, &threadID
);
433 pipeThreads
[1] = CreateThread(NULL
, 0, ReadFromPipe
, &Err
, 0, &threadID
);
436 * Block waiting for the process to end.
439 WaitForSingleObject(pi
.hProcess
, INFINITE
);
440 CloseHandle(pi
.hProcess
);
443 * Wait for our pipe to get done reading, should it be a little slow.
446 WaitForMultipleObjects(2, pipeThreads
, TRUE
, 500);
447 CloseHandle(pipeThreads
[0]);
448 CloseHandle(pipeThreads
[1]);
451 * Look for the commandline warning code in the stderr stream.
454 return !(strstr(Out
.buffer
, "LNK1117") != NULL
||
455 strstr(Err
.buffer
, "LNK1117") != NULL
||
456 strstr(Out
.buffer
, "LNK4044") != NULL
||
457 strstr(Err
.buffer
, "LNK4044") != NULL
||
458 strstr(Out
.buffer
, "LNK4224") != NULL
||
459 strstr(Err
.buffer
, "LNK4224") != NULL
);
466 pipeinfo
*pi
= (pipeinfo
*) args
;
467 char *lastBuf
= pi
->buffer
;
472 if (lastBuf
- pi
->buffer
+ CHUNK
> STATICBUFFERSIZE
) {
473 CloseHandle(pi
->pipe
);
476 ok
= ReadFile(pi
->pipe
, lastBuf
, CHUNK
, &dwRead
, 0L);
477 if (!ok
|| dwRead
== 0) {
478 CloseHandle(pi
->pipe
);
484 return 0; /* makes the compiler happy */
490 const char *substring
)
492 return (strstr(string
, substring
) != NULL
);
496 * GetVersionFromFile --
497 * Looks for a match string in a file and then returns the version
498 * following the match where a version is anything acceptable to
499 * package provide or package ifneeded.
504 const char *filename
,
508 static char szBuffer
[100];
509 char *szResult
= NULL
;
510 FILE *fp
= fopen(filename
, "rt");
514 * Read data until we see our match string.
517 while (fgets(szBuffer
, sizeof(szBuffer
), fp
) != NULL
) {
520 p
= strstr(szBuffer
, match
);
523 * Skip to first digit after the match.
527 while (*p
&& !isdigit((unsigned char)*p
)) {
532 * Find ending whitespace.
536 while (*q
&& (strchr("0123456789.ab", *q
)) && (((!strchr(".ab", *q
)
537 && !strchr("ab", q
[-1])) || --numdots
))) {
552 * List helpers for the SubstituteFile function
555 typedef struct list_item_t
{
556 struct list_item_t
*nextPtr
;
561 /* insert a list item into the list (list may be null) */
563 list_insert(list_item_t
**listPtrPtr
, const char *key
, const char *value
)
565 list_item_t
*itemPtr
= (list_item_t
*)malloc(sizeof(list_item_t
));
567 itemPtr
->key
= strdup(key
);
568 itemPtr
->value
= strdup(value
);
569 itemPtr
->nextPtr
= NULL
;
572 listPtrPtr
= &(*listPtrPtr
)->nextPtr
;
574 *listPtrPtr
= itemPtr
;
580 list_free(list_item_t
**listPtrPtr
)
582 list_item_t
*tmpPtr
, *listPtr
= *listPtrPtr
;
585 listPtr
= listPtr
->nextPtr
;
594 * As windows doesn't provide anything useful like sed and it's unreliable
595 * to use the tclsh you are building against (consider x-platform builds -
596 * eg compiling AMD64 target from IX86) we provide a simple substitution
597 * option here to handle autoconf style substitutions.
598 * The substitution file is whitespace and line delimited. The file should
599 * consist of lines matching the regular expression:
602 * Usage is something like:
603 * nmakehlp -S << $** > $@
604 * @PACKAGE_NAME@ $(PACKAGE_NAME)
605 * @PACKAGE_VERSION@ $(PACKAGE_VERSION)
611 const char *substitutions
,
612 const char *filename
)
614 static char szBuffer
[1024], szCopy
[1024];
615 list_item_t
*substPtr
= NULL
;
618 fp
= fopen(filename
, "rt");
622 * Build a list of substutitions from the first filename
625 sp
= fopen(substitutions
, "rt");
627 while (fgets(szBuffer
, sizeof(szBuffer
), sp
) != NULL
) {
628 unsigned char *ks
, *ke
, *vs
, *ve
;
629 ks
= (unsigned char*)szBuffer
;
630 while (ks
&& *ks
&& isspace(*ks
)) ++ks
;
632 while (ke
&& *ke
&& !isspace(*ke
)) ++ke
;
634 while (vs
&& *vs
&& isspace(*vs
)) ++vs
;
636 while (ve
&& *ve
&& !(*ve
== '\r' || *ve
== '\n')) ++ve
;
638 list_insert(&substPtr
, (char*)ks
, (char*)vs
);
643 /* debug: dump the list */
647 list_item_t
*p
= NULL
;
648 for (p
= substPtr
; p
!= NULL
; p
= p
->nextPtr
, ++n
) {
649 fprintf(stderr
, "% 3d '%s' => '%s'\n", n
, p
->key
, p
->value
);
655 * Run the substitutions over each line of the input
658 while (fgets(szBuffer
, sizeof(szBuffer
), fp
) != NULL
) {
659 list_item_t
*p
= NULL
;
660 for (p
= substPtr
; p
!= NULL
; p
= p
->nextPtr
) {
661 char *m
= strstr(szBuffer
, p
->key
);
666 while (op
!= m
) *cp
++ = *op
++;
668 while (sp
&& *sp
) *cp
++ = *sp
++;
669 op
+= strlen(p
->key
);
670 while (*op
) *cp
++ = *op
++;
672 memcpy(szBuffer
, szCopy
, sizeof(szCopy
));
675 printf("%s", szBuffer
);
678 list_free(&substPtr
);
684 BOOL
FileExists(LPCTSTR szPath
)
686 #ifndef INVALID_FILE_ATTRIBUTES
687 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
689 DWORD pathAttr
= GetFileAttributes(szPath
);
690 return (pathAttr
!= INVALID_FILE_ATTRIBUTES
&&
691 !(pathAttr
& FILE_ATTRIBUTE_DIRECTORY
));
698 * This composes the current working directory with a provided path
699 * and returns the fully qualified and normalized path.
700 * Mostly needed to setup paths for testing.
707 char szCwd
[MAX_PATH
+ 1];
709 GetFullPathName(szPath
, sizeof(szCwd
)-1, szCwd
, NULL
);
710 printf("%s\n", szCwd
);
715 * Implements LocateDependency for a single directory. See that command
716 * for an explanation.
717 * Returns 0 if found after printing the directory.
718 * Returns 1 if not found but no errors.
719 * Returns 2 on any kind of error
720 * Basically, these are used as exit codes for the process.
722 static int LocateDependencyHelper(const char *dir
, const char *keypath
)
725 char path
[MAX_PATH
+1];
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 (FileExists(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
)
796 static const char *paths
[] = {"..", "..\\..", "..\\..\\.."};
798 for (i
= 0; i
< (sizeof(paths
)/sizeof(paths
[0])); ++i
) {
799 ret
= LocateDependencyHelper(paths
[i
], keypath
);
812 * indent-tabs-mode: t