VirtualBox

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

Last change on this file since 3387 was 3352, checked in by bird, 4 years ago

Adding kmk_kill for windows.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use