VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin.c@ 3225

Last change on this file since 3225 was 3225, checked in by bird, 7 years ago

kmk: build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: kmkbuiltin.c 3225 2018-05-29 08:56:36Z bird $ */
2/** @file
3 * kMk Builtin command execution.
4 */
5
6/*
7 * Copyright (c) 2005-2018 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
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.
15 *
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.
20 *
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/>
23 *
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#include <string.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <ctype.h>
34#include <assert.h>
35#include <sys/stat.h>
36#ifdef _MSC_VER
37# include <io.h>
38#endif
39
40#include "makeint.h"
41#include "job.h"
42#include "variable.h"
43#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
44# include "w32/winchildren.h"
45#endif
46#include "kmkbuiltin/err.h"
47#include "kmkbuiltin.h"
48
49#ifndef _MSC_VER
50extern char **environ;
51#endif
52
53
54/*********************************************************************************************************************************
55* Global Variables *
56*********************************************************************************************************************************/
57#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
58extern int print_stats_flag;
59#endif
60
61
62
63int kmk_builtin_command(const char *pszCmd, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
64{
65 int argc;
66 char **argv;
67 int rc;
68 char *pszzCmd;
69 char *pszDst;
70 int fOldStyle = 0;
71
72 /*
73 * Check and skip the prefix.
74 */
75 if (strncmp(pszCmd, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
76 {
77 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
78 return 1;
79 }
80
81 /*
82 * Parse arguments.
83 */
84 rc = 0;
85 argc = 0;
86 argv = NULL;
87 pszzCmd = pszDst = (char *)strdup(pszCmd);
88 if (!pszDst)
89 {
90 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
91 return 1;
92 }
93 do
94 {
95 const char * const pszSrcStart = pszCmd;
96 char ch;
97 char chQuote;
98
99 /*
100 * Start new argument.
101 */
102 if (!(argc % 16))
103 {
104 void *pv = realloc(argv, sizeof(char *) * (argc + 17));
105 if (!pv)
106 {
107 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
108 rc = 1;
109 break;
110 }
111 argv = (char **)pv;
112 }
113 argv[argc++] = pszDst;
114 argv[argc] = NULL;
115
116 if (!fOldStyle)
117 {
118 /*
119 * Process the next argument, bourne style.
120 */
121 chQuote = 0;
122 ch = *pszCmd++;
123 do
124 {
125 /* Unquoted mode? */
126 if (chQuote == 0)
127 {
128 if (ch != '\'' && ch != '"')
129 {
130 if (!isspace(ch))
131 {
132 if (ch != '\\')
133 *pszDst++ = ch;
134 else
135 {
136 ch = *pszCmd++;
137 if (ch)
138 *pszDst++ = ch;
139 else
140 {
141 fprintf(stderr, "kmk_builtin: Incomplete escape sequence in argument %d: %s\n",
142 argc, pszSrcStart);
143 rc = 1;
144 break;
145 }
146 }
147 }
148 else
149 break;
150 }
151 else
152 chQuote = ch;
153 }
154 /* Quoted mode */
155 else if (ch != chQuote)
156 {
157 if ( ch != '\\'
158 || chQuote == '\'')
159 *pszDst++ = ch;
160 else
161 {
162 ch = *pszCmd++;
163 if (ch)
164 {
165 if ( ch != '\\'
166 && ch != '"'
167 && ch != '`'
168 && ch != '$'
169 && ch != '\n')
170 *pszDst++ = '\\';
171 *pszDst++ = ch;
172 }
173 else
174 {
175 fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
176 rc = 1;
177 break;
178 }
179 }
180 }
181 else
182 chQuote = 0;
183 } while ((ch = *pszCmd++) != '\0');
184 }
185 else
186 {
187 /*
188 * Old style in case we ever need it.
189 */
190 ch = *pszCmd++;
191 if (ch != '"' && ch != '\'')
192 {
193 do
194 *pszDst++ = ch;
195 while ((ch = *pszCmd++) != '\0' && !isspace(ch));
196 }
197 else
198 {
199 chQuote = ch;
200 for (;;)
201 {
202 char *pszEnd = strchr(pszCmd, chQuote);
203 if (pszEnd)
204 {
205 fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
206 rc = 1;
207 break;
208 }
209 memcpy(pszDst, pszCmd, pszEnd - pszCmd);
210 pszDst += pszEnd - pszCmd;
211 if (pszEnd[1] != chQuote)
212 break;
213 *pszDst++ = chQuote;
214 }
215 }
216 }
217 *pszDst++ = '\0';
218
219 /*
220 * Skip argument separators (IFS=space() for now). Check for EOS.
221 */
222 if (ch != 0)
223 while ((ch = *pszCmd) && isspace(ch))
224 pszCmd++;
225 if (ch == 0)
226 break;
227 } while (rc == 0);
228
229 /*
230 * Execute the command if parsing was successful.
231 */
232 if (rc == 0)
233 rc = kmk_builtin_command_parsed(argc, argv, pChild, ppapszArgvToSpawn, pPidSpawned);
234
235 /* clean up and return. */
236 free(argv);
237 free(pszzCmd);
238 return rc;
239}
240
241
242/**
243 * kmk built command.
244 */
245static const KMKBUILTINENTRY g_aBuiltIns[] =
246{
247#define BUILTIN_ENTRY(a_fn, a_sz, a_uFnSignature, fMtSafe, fNeedEnv) \
248 { { { sizeof(a_sz) - 1, a_sz, } }, \
249 (uintptr_t)a_fn, a_uFnSignature, fMtSafe, fNeedEnv }
250
251 /* More frequently used commands: */
252 BUILTIN_ENTRY(kmk_builtin_append, "append", FN_SIG_MAIN_SPAWNS, 0, 0),
253 BUILTIN_ENTRY(kmk_builtin_printf, "printf", FN_SIG_MAIN, 0, 0),
254 BUILTIN_ENTRY(kmk_builtin_echo, "echo", FN_SIG_MAIN, 0, 0),
255 BUILTIN_ENTRY(kmk_builtin_install, "install", FN_SIG_MAIN, 1, 0),
256 BUILTIN_ENTRY(kmk_builtin_kDepObj, "kDepObj", FN_SIG_MAIN, 1, 0),
257#ifdef KBUILD_OS_WINDOWS
258 BUILTIN_ENTRY(kmk_builtin_kSubmit, "kSubmit", FN_SIG_MAIN_SPAWNS, 0, 1),
259#endif
260 BUILTIN_ENTRY(kmk_builtin_mkdir, "mkdir", FN_SIG_MAIN, 0, 0),
261 BUILTIN_ENTRY(kmk_builtin_mv, "mv", FN_SIG_MAIN, 0, 0),
262 BUILTIN_ENTRY(kmk_builtin_redirect, "redirect", FN_SIG_MAIN_SPAWNS, 1, 1),
263 BUILTIN_ENTRY(kmk_builtin_rm, "rm", FN_SIG_MAIN, 1, 1),
264 BUILTIN_ENTRY(kmk_builtin_rmdir, "rmdir", FN_SIG_MAIN, 0, 0),
265 BUILTIN_ENTRY(kmk_builtin_test, "test", FN_SIG_MAIN_TO_SPAWN, 0, 0),
266 /* Less frequently used commands: */
267 BUILTIN_ENTRY(kmk_builtin_kDepIDB, "kDepIDB", FN_SIG_MAIN, 0, 0),
268 BUILTIN_ENTRY(kmk_builtin_chmod, "chmod", FN_SIG_MAIN, 0, 0),
269 BUILTIN_ENTRY(kmk_builtin_cp, "cp", FN_SIG_MAIN, 1, 1),
270 BUILTIN_ENTRY(kmk_builtin_expr, "expr", FN_SIG_MAIN, 0, 0),
271 BUILTIN_ENTRY(kmk_builtin_ln, "ln", FN_SIG_MAIN, 0, 0),
272 BUILTIN_ENTRY(kmk_builtin_md5sum, "md5sum", FN_SIG_MAIN, 1, 0),
273 BUILTIN_ENTRY(kmk_builtin_cmp, "cmp", FN_SIG_MAIN, 0, 0),
274 BUILTIN_ENTRY(kmk_builtin_cat, "cat", FN_SIG_MAIN, 0, 0),
275 BUILTIN_ENTRY(kmk_builtin_touch, "touch", FN_SIG_MAIN, 0, 0),
276 BUILTIN_ENTRY(kmk_builtin_sleep, "sleep", FN_SIG_MAIN, 1, 0),
277 BUILTIN_ENTRY(kmk_builtin_dircache, "dircache", FN_SIG_MAIN, 0, 0),
278};
279
280#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
281/** Statistics running in parallel to g_aBuiltIns. */
282struct
283{
284 big_int cNs;
285 unsigned cTimes;
286 unsigned cAsyncTimes;
287} g_aBuiltInStats[sizeof(g_aBuiltIns) / sizeof(g_aBuiltIns[0])];
288#endif
289
290
291int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
292{
293 /*
294 * Check and skip the prefix.
295 */
296 static const char s_szPrefix[] = "kmk_builtin_";
297 const char *pszCmd = argv[0];
298 if (strncmp(pszCmd, s_szPrefix, sizeof(s_szPrefix) - 1) == 0)
299 {
300 struct KMKBUILTINENTRY const *pEntry;
301 size_t cchAndStart;
302 int cLeft;
303
304 pszCmd += sizeof(s_szPrefix) - 1;
305
306 /*
307 * Calc the length and start word to avoid calling memcmp/strcmp on each entry.
308 */
309#if K_ARCH_BITS != 64 && K_ARCH_BITS != 32
310# error "PORT ME!"
311#endif
312 cchAndStart = strlen(pszCmd);
313#if K_ENDIAN == K_ENDIAN_BIG
314 cchAndStart <<= K_ARCH_BITS - 8;
315 switch (cchAndStart)
316 {
317 default: /* fall thru */
318# if K_ARCH_BITS >= 64
319 case 7: cchAndStart |= (size_t)pszCmd[6] << (K_ARCH_BITS - 56); /* fall thru */
320 case 6: cchAndStart |= (size_t)pszCmd[5] << (K_ARCH_BITS - 48); /* fall thru */
321 case 5: cchAndStart |= (size_t)pszCmd[4] << (K_ARCH_BITS - 40); /* fall thru */
322 case 4: cchAndStart |= (size_t)pszCmd[3] << (K_ARCH_BITS - 32); /* fall thru */
323# endif
324 case 3: cchAndStart |= (size_t)pszCmd[2] << (K_ARCH_BITS - 24); /* fall thru */
325 case 2: cchAndStart |= (size_t)pszCmd[1] << (K_ARCH_BITS - 16); /* fall thru */
326 case 1: cchAndStart |= (size_t)pszCmd[0] << (K_ARCH_BITS - 8); /* fall thru */
327 case 0: break;
328 }
329#else
330 switch (cchAndStart)
331 {
332 default: /* fall thru */
333# if K_ARCH_BITS >= 64
334 case 7: cchAndStart |= (size_t)pszCmd[6] << 56; /* fall thru */
335 case 6: cchAndStart |= (size_t)pszCmd[5] << 48; /* fall thru */
336 case 5: cchAndStart |= (size_t)pszCmd[4] << 40; /* fall thru */
337 case 4: cchAndStart |= (size_t)pszCmd[3] << 32; /* fall thru */
338# endif
339 case 3: cchAndStart |= (size_t)pszCmd[2] << 24; /* fall thru */
340 case 2: cchAndStart |= (size_t)pszCmd[1] << 16; /* fall thru */
341 case 1: cchAndStart |= (size_t)pszCmd[0] << 8; /* fall thru */
342 case 0: break;
343 }
344#endif
345
346 /*
347 * Look up the builtin command in the table.
348 */
349 pEntry = &g_aBuiltIns[0];
350 cLeft = sizeof(g_aBuiltIns) / sizeof(g_aBuiltIns[0]);
351 while (cLeft-- > 0)
352 if ( pEntry->uName.cchAndStart != cchAndStart
353 || ( pEntry->uName.s.cch >= sizeof(cchAndStart)
354 && memcmp(pEntry->uName.s.sz, pszCmd, pEntry->uName.s.cch) != 0) )
355 pEntry++;
356 else
357 {
358 /*
359 * That's a match!
360 *
361 * First get the environment if it is actually needed. This is
362 * especially important when we run on a worker thread as it must
363 * not under any circumstances do stuff like target_environment.
364 */
365 int rc;
366 char **papszEnvVars = NULL;
367 if (pEntry->fNeedEnv)
368 {
369 papszEnvVars = pChild->environment;
370 if (!papszEnvVars)
371 pChild->environment = papszEnvVars = target_environment(pChild->file);
372 }
373
374#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
375 /*
376 * If the built-in is multi thread safe, we will run it on a job slot thread.
377 */
378 if (pEntry->fMtSafe)
379 {
380 rc = MkWinChildCreateBuiltIn(pEntry, argc, argv, papszEnvVars, pChild, pPidSpawned);
381# ifdef CONFIG_WITH_KMK_BUILTIN_STATS
382 g_aBuiltInStats[pEntry - &g_aBuiltIns[0]].cAsyncTimes++;
383# endif
384 }
385 else
386#endif
387 {
388 /*
389 * Call the worker function, making sure to preserve umask.
390 */
391#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
392 big_int nsStart = print_stats_flag ? nano_timestamp() : 0;
393#endif
394 KMKBUILTINCTX Ctx;
395 int const iUmask = umask(0); /* save umask */
396 umask(iUmask);
397
398 Ctx.pszProgName = pEntry->uName.s.sz;
399 Ctx.pOut = pChild ? &pChild->output : NULL;
400
401 if (pEntry->uFnSignature == FN_SIG_MAIN)
402 rc = pEntry->u.pfnMain(argc, argv, papszEnvVars, &Ctx);
403 else if (pEntry->uFnSignature == FN_SIG_MAIN_SPAWNS)
404 rc = pEntry->u.pfnMainSpawns(argc, argv, papszEnvVars, &Ctx, pChild, pPidSpawned);
405 else if (pEntry->uFnSignature == FN_SIG_MAIN_TO_SPAWN)
406 {
407 /*
408 * When we got something to execute, check if the child is a kmk_builtin thing.
409 * We recurse here, both because I'm lazy and because it's easier to debug a
410 * problem then (the call stack shows what's been going on).
411 */
412 rc = pEntry->u.pfnMainToSpawn(argc, argv, papszEnvVars, &Ctx, ppapszArgvToSpawn);
413 if ( !rc
414 && *ppapszArgvToSpawn
415 && !strncmp(**ppapszArgvToSpawn, s_szPrefix, sizeof(s_szPrefix) - 1))
416 {
417 char **argv_new = *ppapszArgvToSpawn;
418 int argc_new = 1;
419 while (argv_new[argc_new])
420 argc_new++;
421
422 assert(argv_new[0] != argv[0]);
423 assert(!*pPidSpawned);
424
425 *ppapszArgvToSpawn = NULL;
426 rc = kmk_builtin_command_parsed(argc_new, argv_new, pChild, ppapszArgvToSpawn, pPidSpawned);
427
428 free(argv_new[0]);
429 free(argv_new);
430 }
431 }
432 else
433 rc = 99;
434
435 umask(iUmask); /* restore it */
436
437#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
438 if (print_stats_flag)
439 {
440 uintptr_t iEntry = pEntry - &g_aBuiltIns[0];
441 g_aBuiltInStats[iEntry].cTimes++;
442 g_aBuiltInStats[iEntry].cNs += nano_timestamp() - nsStart;
443 }
444#endif
445 }
446 return rc;
447 }
448
449 /*
450 * No match! :-(
451 */
452 fprintf(stderr, "kmk_builtin: Unknown command '%s%s'!\n", s_szPrefix, pszCmd);
453 }
454 else
455 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
456 return 1;
457}
458
459#ifndef KBUILD_OS_WINDOWS
460/** Dummy. */
461int kmk_builtin_dircache(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
462{
463 (void)argc; (void)argv; (void)envp; (void)pCtx;
464 return 0;
465}
466#endif
467
468#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
469/**
470 * Prints the statistiscs to the given output stream.
471 */
472extern void kmk_builtin_print_stats(FILE *pOutput, const char *pszPrefix)
473{
474 const unsigned cEntries = sizeof(g_aBuiltInStats) / sizeof(g_aBuiltInStats[0]);
475 unsigned i;
476 assert(print_stats_flag);
477 fprintf(pOutput, "\n%skmk built-in command statistics:\n", pszPrefix);
478 for (i = 0; i < cEntries; i++)
479 if (g_aBuiltInStats[i].cTimes > 0)
480 {
481 char szTotal[64];
482 char szAvg[64];
483 format_elapsed_nano(szTotal, sizeof(szTotal), g_aBuiltInStats[i].cNs);
484 format_elapsed_nano(szAvg, sizeof(szAvg), g_aBuiltInStats[i].cNs / g_aBuiltInStats[i].cTimes);
485 fprintf(pOutput, "%s kmk_builtin_%-9s: %4lu times, %9s total, %9s/call\n",
486 pszPrefix, g_aBuiltIns[i].uName.s.sz, g_aBuiltInStats[i].cTimes, szTotal, szAvg);
487 }
488 else if (g_aBuiltInStats[i].cAsyncTimes > 0)
489 fprintf(pOutput, "%s kmk_builtin_%-9s: %4lu times in worker thread\n",
490 pszPrefix, g_aBuiltIns[i].uName.s.sz, g_aBuiltInStats[i].cAsyncTimes);
491}
492#endif
493
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette