Built win.arm64 against r3658
[kbuild-mirror.git] / src / kDepPre / kDepPre.c
blob41d1381b87ceb61dcebef8765baf9feeaec1e83b
1 /* $Id$ */
2 /** @file
3 * kDepPre - Dependency Generator using Precompiler output.
4 */
6 /*
7 * Copyright (c) 2005-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
9 * This file is part of kBuild.
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 /*******************************************************************************
27 * Header Files *
28 *******************************************************************************/
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #ifdef _MSC_VER
34 # include <io.h>
35 #else
36 # include <unistd.h>
37 #endif
38 #include "kDep.h"
40 #ifdef HAVE_FGETC_UNLOCKED
41 # define FGETC(s) getc_unlocked(s)
42 #else
43 # define FGETC(s) fgetc(s)
44 #endif
46 #ifdef NEED_ISBLANK
47 # define isblank(ch) ( (unsigned char)(ch) == ' ' || (unsigned char)(ch) == '\t' )
48 #endif
53 /**
54 * Parses the output from a preprocessor of a C-style language.
56 * @returns 0 on success.
57 * @returns 1 or other approriate exit code on failure.
58 * @param pThis Pointer to the 'dep' instance.
59 * @param pInput Input stream. (probably not seekable)
61 static int ParseCPrecompiler(PDEPGLOBALS pThis, FILE *pInput)
63 enum
65 C_DISCOVER = 0,
66 C_SKIP_LINE,
67 C_PARSE_FILENAME,
68 C_EOF
69 } enmMode = C_DISCOVER;
70 PDEP pDep = NULL;
71 int ch = 0;
72 char szBuf[8192];
74 for (;;)
76 switch (enmMode)
79 * Start of line, need to look for '#[[:space]]*line <num> "file"' and '# <num> "file"'.
81 case C_DISCOVER:
82 /* first find '#' */
83 while ((ch = FGETC(pInput)) != EOF)
84 if (!isblank(ch))
85 break;
86 if (ch == '#')
88 /* skip spaces */
89 while ((ch = FGETC(pInput)) != EOF)
90 if (!isblank(ch))
91 break;
93 /* check for "line" */
94 if (ch == 'l')
96 if ( (ch = FGETC(pInput)) == 'i'
97 && (ch = FGETC(pInput)) == 'n'
98 && (ch = FGETC(pInput)) == 'e')
100 ch = FGETC(pInput);
101 if (isblank(ch))
103 /* skip spaces */
104 while ((ch = FGETC(pInput)) != EOF)
105 if (!isblank(ch))
106 break;
108 else
109 ch = 'x';
111 else
112 ch = 'x';
115 /* line number */
116 if (ch >= '0' && ch <= '9')
118 /* skip the number following spaces */
119 while ((ch = FGETC(pInput)) != EOF)
120 if (!isxdigit(ch))
121 break;
122 if (isblank(ch))
124 while ((ch = FGETC(pInput)) != EOF)
125 if (!isblank(ch))
126 break;
127 /* quoted filename */
128 if (ch == '"')
130 enmMode = C_PARSE_FILENAME;
131 break;
136 enmMode = C_SKIP_LINE;
137 break;
140 * Skip past the end of the current line.
142 case C_SKIP_LINE:
145 if ( ch == '\r'
146 || ch == '\n')
147 break;
148 } while ((ch = FGETC(pInput)) != EOF);
149 enmMode = C_DISCOVER;
150 break;
153 * Parse the filename.
155 case C_PARSE_FILENAME:
157 /* retreive and unescape the filename. */
158 char *psz = &szBuf[0];
159 while ( (ch = FGETC(pInput)) != EOF
160 && psz < &szBuf[sizeof(szBuf) - 1])
162 if (ch == '\\')
164 ch = FGETC(pInput);
165 switch (ch)
167 case '\\': ch = '/'; break;
168 case 't': ch = '\t'; break;
169 case 'r': ch = '\r'; break;
170 case 'n': ch = '\n'; break;
171 case 'b': ch = '\b'; break;
172 default:
173 fprintf(stderr, "warning: unknown escape char '%c'\n", ch);
174 continue;
177 *psz++ = ch == '\\' ? '/' : ch;
179 else if (ch != '"')
180 *psz++ = ch;
181 else
183 size_t cchFilename = psz - &szBuf[0];
184 *psz = '\0';
185 /* compare with current dep, add & switch on mismatch. */
186 if ( !pDep
187 || pDep->cchFilename != cchFilename
188 || memcmp(pDep->szFilename, szBuf, cchFilename))
189 pDep = depAdd(pThis, szBuf, cchFilename);
190 break;
193 enmMode = C_SKIP_LINE;
194 break;
198 * Handle EOF.
200 case C_EOF:
201 if (feof(pInput))
202 return 0;
203 enmMode = C_DISCOVER;
204 break;
206 if (ch == EOF)
207 enmMode = C_EOF;
210 return 0;
214 static int usage(FILE *pOut, const char *argv0)
216 fprintf(pOut,
217 "usage: %s [-l=c] -o <output> -t <target> [-f] [-s] < - | <filename> | -e <cmdline> >\n"
218 " or: %s --help\n"
219 " or: %s --version\n",
220 argv0, argv0, argv0);
221 return 1;
225 int main(int argc, char *argv[])
227 int i;
228 DEPGLOBALS This;
230 /* Arguments. */
231 int iExec = 0;
232 FILE *pOutput = NULL;
233 const char *pszOutput = NULL;
234 FILE *pInput = NULL;
235 const char *pszTarget = NULL;
236 int fStubs = 0;
237 int fFixCase = 0;
238 /* Argument parsing. */
239 int fInput = 0; /* set when we've found input argument. */
242 * Parse arguments.
244 if (argc <= 1)
245 return usage(stderr, argv[0]);
246 for (i = 1; i < argc; i++)
248 if (argv[i][0] == '-')
250 const char *psz = &argv[i][1];
251 if (*psz == '-')
253 if (!strcmp(psz, "-help"))
254 psz = "h";
255 else if (!strcmp(psz, "-version"))
256 psz = "V";
259 switch (*psz)
262 * Output file.
264 case 'o':
266 pszOutput = &argv[i][2];
267 if (pOutput)
269 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
270 return 1;
272 if (!*pszOutput)
274 if (++i >= argc)
276 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
277 return 1;
279 pszOutput = argv[i];
281 if (pszOutput[0] == '-' && !pszOutput[1])
282 pOutput = stdout;
283 else
284 pOutput = fopen(pszOutput, "w");
285 if (!pOutput)
287 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
288 return 1;
290 break;
294 * Language spec.
296 case 'l':
298 const char *pszValue = &argv[i][2];
299 if (*pszValue == '=')
300 pszValue++;
301 if (!strcmp(pszValue, "c"))
303 else
305 fprintf(stderr, "%s: error: The '%s' language is not supported.\n", argv[0], pszValue);
306 return 1;
308 break;
312 * Target name.
314 case 't':
316 if (pszTarget)
318 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
319 return 1;
321 pszTarget = &argv[i][2];
322 if (!*pszTarget)
324 if (++i >= argc)
326 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
327 return 1;
329 pszTarget = argv[i];
331 break;
335 * Exec.
337 case 'e':
339 if (++i >= argc)
341 fprintf(stderr, "%s: syntax error: The '-e' argument is missing the command.\n", argv[0]);
342 return 1;
344 iExec = i;
345 i = argc - 1;
346 break;
350 * Pipe input.
352 case '\0':
354 pInput = stdin;
355 fInput = 1;
356 break;
360 * Fix case.
362 case 'f':
364 fFixCase = 1;
365 break;
369 * Generate stubs.
371 case 's':
373 fStubs = 1;
374 break;
378 * The obligatory help and version.
380 case 'h':
381 usage(stdout, argv[0]);
382 return 0;
384 case 'V':
385 printf("kDepPre - kBuild version %d.%d.%d\n"
386 "Copyright (C) 2005-2008 knut st. osmundsen\n",
387 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
388 return 0;
391 * Invalid argument.
393 default:
394 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
395 return usage(stderr, argv[0]);
398 else
400 pInput = fopen(argv[i], "r");
401 if (!pInput)
403 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
404 return 1;
406 fInput = 1;
410 * End of the line?
412 if (fInput)
414 if (++i < argc)
416 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
417 return 1;
419 break;
424 * Got all we require?
426 if (!pInput && iExec <= 0)
428 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
429 return 1;
431 if (!pOutput)
433 fprintf(stderr, "%s: syntax error: No output!\n", argv[0]);
434 return 1;
436 if (!pszTarget)
438 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
439 return 1;
443 * Spawn process?
445 if (iExec > 0)
447 fprintf(stderr, "%s: -e is not yet implemented!\n", argv[0]);
448 return 1;
452 * Do the parsing.
454 depInit(&This);
455 i = ParseCPrecompiler(&This, pInput);
458 * Reap child.
460 if (iExec > 0)
462 /* later */
466 * Write the dependecy file.
468 if (!i)
470 depOptimize(&This, fFixCase, 0 /* fQuiet */, NULL /*pszIgnoredExt*/);
471 depPrintTargetWithDeps(&This, pOutput, pszTarget, 1 /*fEscapeTarget*/);
472 if (fStubs)
473 depPrintStubs(&This, pOutput);
477 * Close the output, delete output on failure.
479 if (!i && ferror(pOutput))
481 i = 1;
482 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
484 fclose(pOutput);
485 if (i)
487 if (unlink(pszOutput))
488 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
491 depCleanup(&This);
493 return i;