VirtualBox

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

Last change on this file since 3291 was 3291, checked in by bird, 6 years ago

kmk_builtin_command_parsed: big endian fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: kmkbuiltin.c 3291 2019-01-08 15:06:54Z 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, } }, { (uintptr_t)a_fn }, a_uFnSignature, fMtSafe, fNeedEnv }
249
250 /* More frequently used commands: */
251 BUILTIN_ENTRY(kmk_builtin_append, "append", FN_SIG_MAIN_SPAWNS, 0, 0),
252 BUILTIN_ENTRY(kmk_builtin_printf, "printf", FN_SIG_MAIN, 0, 0),
253 BUILTIN_ENTRY(kmk_builtin_echo, "echo", FN_SIG_MAIN, 0, 0),
254 BUILTIN_ENTRY(kmk_builtin_install, "install", FN_SIG_MAIN, 1, 0),
255 BUILTIN_ENTRY(kmk_builtin_kDepObj, "kDepObj", FN_SIG_MAIN, 1, 0),
256#ifdef KBUILD_OS_WINDOWS
257 BUILTIN_ENTRY(kmk_builtin_kSubmit, "kSubmit", FN_SIG_MAIN_SPAWNS, 0, 1),
258#endif
259 BUILTIN_ENTRY(kmk_builtin_mkdir, "mkdir", FN_SIG_MAIN, 0, 0),
260 BUILTIN_ENTRY(kmk_builtin_mv, "mv", FN_SIG_MAIN, 0, 0),
261 BUILTIN_ENTRY(kmk_builtin_redirect, "redirect", FN_SIG_MAIN_SPAWNS, 1, 1),
262 BUILTIN_ENTRY(kmk_builtin_rm, "rm", FN_SIG_MAIN, 1, 1),
263 BUILTIN_ENTRY(kmk_builtin_rmdir, "rmdir", FN_SIG_MAIN, 0, 0),
264 BUILTIN_ENTRY(kmk_builtin_test, "test", FN_SIG_MAIN_TO_SPAWN, 0, 0),
265 /* Less frequently used commands: */
266 BUILTIN_ENTRY(kmk_builtin_kDepIDB, "kDepIDB", FN_SIG_MAIN, 0, 0),
267 BUILTIN_ENTRY(kmk_builtin_chmod, "chmod", FN_SIG_MAIN, 0, 0),
268 BUILTIN_ENTRY(kmk_builtin_cp, "cp", FN_SIG_MAIN, 1, 1),
269 BUILTIN_ENTRY(kmk_builtin_expr, "expr", FN_SIG_MAIN, 0, 0),
270 BUILTIN_ENTRY(kmk_builtin_ln, "ln", FN_SIG_MAIN, 0, 0),
271 BUILTIN_ENTRY(kmk_builtin_md5sum, "md5sum", FN_SIG_MAIN, 1, 0),
272 BUILTIN_ENTRY(kmk_builtin_cmp, "cmp", FN_SIG_MAIN, 0, 0),
273 BUILTIN_ENTRY(kmk_builtin_cat, "cat", FN_SIG_MAIN, 0, 0),
274 BUILTIN_ENTRY(kmk_builtin_touch, "touch", FN_SIG_MAIN, 0, 0),
275 BUILTIN_ENTRY(kmk_builtin_sleep, "sleep", FN_SIG_MAIN, 1, 0),
276 BUILTIN_ENTRY(kmk_builtin_dircache, "dircache", FN_SIG_MAIN, 0, 0),
277};
278
279#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
280/** Statistics running in parallel to g_aBuiltIns. */
281struct
282{
283 big_int cNs;
284 unsigned cTimes;
285 unsigned cAsyncTimes;
286} g_aBuiltInStats[sizeof(g_aBuiltIns) / sizeof(g_aBuiltIns[0])];
287#endif
288
289
290int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
291{
292 /*
293 * Check and skip the prefix.
294 */
295 static const char s_szPrefix[] = "kmk_builtin_";
296 const char *pszCmd = argv[0];
297 if (strncmp(pszCmd, s_szPrefix, sizeof(s_szPrefix) - 1) == 0)
298 {
299 struct KMKBUILTINENTRY const *pEntry;
300 size_t cchAndStart;
301 int cLeft;
302
303 pszCmd += sizeof(s_szPrefix) - 1;
304
305 /*
306 * Calc the length and start word to avoid calling memcmp/strcmp on each entry.
307 */
308#if K_ARCH_BITS != 64 && K_ARCH_BITS != 32
309# error "PORT ME!"
310#endif
311 cchAndStart = strlen(pszCmd);
312#if K_ENDIAN == K_ENDIAN_BIG
313 cchAndStart <<= K_ARCH_BITS - 8;
314 switch (cchAndStart)
315 {
316 default: /* fall thru */
317# if K_ARCH_BITS >= 64
318 case 7: cchAndStart |= (size_t)pszCmd[6]; /* fall thru */
319 case 6: cchAndStart |= (size_t)pszCmd[5] << (K_ARCH_BITS - 56); /* fall thru */
320 case 5: cchAndStart |= (size_t)pszCmd[4] << (K_ARCH_BITS - 48); /* fall thru */
321 case 4: cchAndStart |= (size_t)pszCmd[3] << (K_ARCH_BITS - 40); /* fall thru */
322# endif
323 /* fall thru - gcc 8.2.0 is confused by # endif */
324 case 3: cchAndStart |= (size_t)pszCmd[2] << (K_ARCH_BITS - 32); /* fall thru */
325 case 2: cchAndStart |= (size_t)pszCmd[1] << (K_ARCH_BITS - 24); /* fall thru */
326 case 1: cchAndStart |= (size_t)pszCmd[0] << (K_ARCH_BITS - 16); /* 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 /* fall thru - gcc 8.2.0 is confused by # endif */
340 case 3: cchAndStart |= (size_t)pszCmd[2] << 24; /* fall thru */
341 case 2: cchAndStart |= (size_t)pszCmd[1] << 16; /* fall thru */
342 case 1: cchAndStart |= (size_t)pszCmd[0] << 8; /* fall thru */
343 case 0: break;
344 }
345#endif
346
347 /*
348 * Look up the builtin command in the table.
349 */
350 pEntry = &g_aBuiltIns[0];
351 cLeft = sizeof(g_aBuiltIns) / sizeof(g_aBuiltIns[0]);
352 while (cLeft-- > 0)
353 if ( pEntry->uName.cchAndStart != cchAndStart
354 || ( pEntry->uName.s.cch >= sizeof(cchAndStart)
355 && memcmp(pEntry->uName.s.sz, pszCmd, pEntry->uName.s.cch) != 0) )
356 pEntry++;
357 else
358 {
359 /*
360 * That's a match!
361 *
362 * First get the environment if it is actually needed. This is
363 * especially important when we run on a worker thread as it must
364 * not under any circumstances do stuff like target_environment.
365 */
366 int rc;
367 char **papszEnvVars = NULL;
368 if (pEntry->fNeedEnv)
369 {
370 papszEnvVars = pChild->environment;
371 if (!papszEnvVars)
372 pChild->environment = papszEnvVars = target_environment(pChild->file);
373 }
374
375#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
376 /*
377 * If the built-in is multi thread safe, we will run it on a job slot thread.
378 */
379 if (pEntry->fMtSafe)
380 {
381 rc = MkWinChildCreateBuiltIn(pEntry, argc, argv, papszEnvVars, pChild, pPidSpawned);
382# ifdef CONFIG_WITH_KMK_BUILTIN_STATS
383 g_aBuiltInStats[pEntry - &g_aBuiltIns[0]].cAsyncTimes++;
384# endif
385 }
386 else
387#endif
388 {
389 /*
390 * Call the worker function, making sure to preserve umask.
391 */
392#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
393 big_int nsStart = print_stats_flag ? nano_timestamp() : 0;
394#endif
395 KMKBUILTINCTX Ctx;
396 int const iUmask = umask(0); /* save umask */
397 umask(iUmask);
398
399 Ctx.pszProgName = pEntry->uName.s.sz;
400 Ctx.pOut = pChild ? &pChild->output : NULL;
401
402 if (pEntry->uFnSignature == FN_SIG_MAIN)
403 rc = pEntry->u.pfnMain(argc, argv, papszEnvVars, &Ctx);
404 else if (pEntry->uFnSignature == FN_SIG_MAIN_SPAWNS)
405 rc = pEntry->u.pfnMainSpawns(argc, argv, papszEnvVars, &Ctx, pChild, pPidSpawned);
406 else if (pEntry->uFnSignature == FN_SIG_MAIN_TO_SPAWN)
407 {
408 /*
409 * When we got something to execute, check if the child is a kmk_builtin thing.
410 * We recurse here, both because I'm lazy and because it's easier to debug a
411 * problem then (the call stack shows what's been going on).
412 */
413 rc = pEntry->u.pfnMainToSpawn(argc, argv, papszEnvVars, &Ctx, ppapszArgvToSpawn);
414 if ( !rc
415 && *ppapszArgvToSpawn
416 && !strncmp(**ppapszArgvToSpawn, s_szPrefix, sizeof(s_szPrefix) - 1))
417 {
418 char **argv_new = *ppapszArgvToSpawn;
419 int argc_new = 1;
420 while (argv_new[argc_new])
421 argc_new++;
422
423 assert(argv_new[0] != argv[0]);
424 assert(!*pPidSpawned);
425
426 *ppapszArgvToSpawn = NULL;
427 rc = kmk_builtin_command_parsed(argc_new, argv_new, pChild, ppapszArgvToSpawn, pPidSpawned);
428
429 free(argv_new[0]);
430 free(argv_new);
431 }
432 }
433 else
434 rc = 99;
435
436 umask(iUmask); /* restore it */
437
438#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
439 if (print_stats_flag)
440 {
441 uintptr_t iEntry = pEntry - &g_aBuiltIns[0];
442 g_aBuiltInStats[iEntry].cTimes++;
443 g_aBuiltInStats[iEntry].cNs += nano_timestamp() - nsStart;
444 }
445#endif
446 }
447 return rc;
448 }
449
450 /*
451 * No match! :-(
452 */
453 fprintf(stderr, "kmk_builtin: Unknown command '%s%s'!\n", s_szPrefix, pszCmd);
454 }
455 else
456 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
457 return 1;
458}
459
460#ifndef KBUILD_OS_WINDOWS
461/** Dummy. */
462int kmk_builtin_dircache(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
463{
464 (void)argc; (void)argv; (void)envp; (void)pCtx;
465 return 0;
466}
467#endif
468
469#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
470/**
471 * Prints the statistiscs to the given output stream.
472 */
473extern void kmk_builtin_print_stats(FILE *pOutput, const char *pszPrefix)
474{
475 const unsigned cEntries = sizeof(g_aBuiltInStats) / sizeof(g_aBuiltInStats[0]);
476 unsigned i;
477 assert(print_stats_flag);
478 fprintf(pOutput, "\n%skmk built-in command statistics:\n", pszPrefix);
479 for (i = 0; i < cEntries; i++)
480 if (g_aBuiltInStats[i].cTimes > 0)
481 {
482 char szTotal[64];
483 char szAvg[64];
484 format_elapsed_nano(szTotal, sizeof(szTotal), g_aBuiltInStats[i].cNs);
485 format_elapsed_nano(szAvg, sizeof(szAvg), g_aBuiltInStats[i].cNs / g_aBuiltInStats[i].cTimes);
486 fprintf(pOutput, "%s kmk_builtin_%-9s: %4u times, %9s total, %9s/call\n",
487 pszPrefix, g_aBuiltIns[i].uName.s.sz, g_aBuiltInStats[i].cTimes, szTotal, szAvg);
488 }
489 else if (g_aBuiltInStats[i].cAsyncTimes > 0)
490 fprintf(pOutput, "%s kmk_builtin_%-9s: %4u times in worker thread\n",
491 pszPrefix, g_aBuiltIns[i].uName.s.sz, g_aBuiltInStats[i].cAsyncTimes);
492}
493#endif
494
Note: See TracBrowser for help on using the repository browser.

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