kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

install.c (7043B)


      1 #include <kit/core.h>
      2 #include <stddef.h>
      3 #include <stdint.h>
      4 #include <string.h>
      5 
      6 #include "driver.h"
      7 #include "env.h"
      8 #include "install_links.h"
      9 
     10 /* `kit install` — busybox-style toolchain installer. Populates a target
     11  * directory with one entry per kit tool, each pointing at the running kit
     12  * binary, so the tools can be invoked by their bare names (cc, ld, nm, ...)
     13  * as a drop-in toolchain. Entries are symlinks on POSIX and hard links on
     14  * Windows (where unprivileged symlinks aren't generally available); either
     15  * can be forced with -s/-H.
     16  *
     17  * Multi-call dispatch in main.c resolves a link named `cc` to the cc tool by
     18  * argv[0]'s basename, so no extra wiring is needed for the links to work.
     19  *
     20  * The default set (no TOOL args, no --all) is the tools tagged with a non-zero
     21  * DriverToolGroup in the centralized table: the binutils/compiler toolchain
     22  * plus the standard-named byte utilities (xxd, cmp). */
     23 
     24 #define INSTALL_TOOL "install"
     25 
     26 void driver_help_install(void) {
     27   driver_printf(
     28       "kit install — symlink the kit tools into a directory\n"
     29       "\n"
     30       "USAGE\n"
     31       "  kit install [OPTIONS] DIR [TOOL...]\n"
     32       "\n"
     33       "DESCRIPTION\n"
     34       "  Populates DIR with one entry per kit tool, each pointing at the\n"
     35       "  running kit binary, so the tools can be invoked by their bare\n"
     36       "  names (cc, ld, nm, ...) as a drop-in toolchain. Entries are\n"
     37       "  symlinks on POSIX and hard links on Windows by default.\n"
     38       "\n"
     39       "  With no arguments, prints this help without changing anything.\n"
     40       "\n"
     41       "  With no TOOL given, installs the default set: the binutils/compiler\n"
     42       "  toolchain (cc cpp as ld ar ranlib strip objcopy objdump nm size\n"
     43       "  addr2line strings) plus the standard-named byte utilities (xxd cmp\n"
     44       "  sha256sum b2sum crc32 gzip gunzip lz4 lz4c). Use --all for every\n"
     45       "  tool, or name specific TOOLs to install just those.\n"
     46       "\n"
     47       "OPTIONS\n"
     48       "  -s, --symlink    create symlinks (default except on Windows)\n"
     49       "  -H, --hardlink   create hard links (default on Windows)\n"
     50       "  -a, --all        install every tool compiled into this binary\n"
     51       "  -f, --force      replace existing entries\n"
     52       "  -n, --dry-run    print what would be done; change nothing\n"
     53       "  -v, --verbose    print each link as it is created\n"
     54       "  -h, --help       show this help\n"
     55       "  --version        show Kit version\n"
     56       "\n"
     57       "RELOCATION NOTE\n"
     58       "  POSIX symlinks created by this release contain the absolute path to\n"
     59       "  the current Kit binary. If the distribution tree is moved afterward,\n"
     60       "  those links become dangling. Invoke the moved bin/kit directly and\n"
     61       "  rerun `kit install --force` for the same prefix. Windows hard links\n"
     62       "  likewise belong to the installed filesystem entry rather than a\n"
     63       "  relocatable path lookup.\n"
     64       "\n"
     65       "EXAMPLES\n"
     66       "  PREFIX=\"$PWD/isolated prefix\"\n"
     67       "  mkdir -p \"$PREFIX/bin\"\n"
     68       "  kit install --all \"$PREFIX/bin\"\n"
     69       "  PATH=\"$PREFIX/bin:/usr/bin:/bin\" cc --help\n"
     70       "\n"
     71       "  kit install --all --force \"$PREFIX/bin\"\n"
     72       "\n"
     73       "EXIT CODES\n"
     74       "  0   success      1   one or more links failed      2   bad usage\n");
     75 }
     76 
     77 int driver_install(int argc, char** argv) {
     78   DriverEnv env;
     79   const char* dir = NULL;
     80   const char** explicit_tools = NULL;
     81   int nexplicit = 0;
     82   int want_hard = -1; /* -1 = pick by host OS; 0 = symlink; 1 = hard link */
     83   int all = 0, force = 0, dry = 0, verbose = 0, opts_done = 0;
     84   char* self = NULL;
     85   size_t self_size = 0;
     86   unsigned done_count = 0, failures = 0;
     87   int i, rc = 2;
     88 
     89   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
     90     driver_help_install();
     91     return 0;
     92   }
     93 
     94   driver_env_init(&env);
     95 
     96   explicit_tools =
     97       (const char**)driver_alloc(&env, (size_t)argc * sizeof(*explicit_tools));
     98   if (!explicit_tools) {
     99     driver_errf(INSTALL_TOOL, "out of memory");
    100     rc = 1;
    101     goto done;
    102   }
    103 
    104   for (i = 1; i < argc; ++i) {
    105     const char* a = argv[i];
    106     if (!opts_done && driver_streq(a, "--")) {
    107       opts_done = 1;
    108       continue;
    109     }
    110     if (!opts_done && a[0] == '-' && a[1] != '\0') {
    111       if (driver_streq(a, "-s") || driver_streq(a, "--symlink")) {
    112         want_hard = 0;
    113       } else if (driver_streq(a, "-H") || driver_streq(a, "--hardlink")) {
    114         want_hard = 1;
    115       } else if (driver_streq(a, "-a") || driver_streq(a, "--all")) {
    116         all = 1;
    117       } else if (driver_streq(a, "-f") || driver_streq(a, "--force")) {
    118         force = 1;
    119       } else if (driver_streq(a, "-n") || driver_streq(a, "--dry-run")) {
    120         dry = 1;
    121       } else if (driver_streq(a, "-v") || driver_streq(a, "--verbose")) {
    122         verbose = 1;
    123       } else {
    124         driver_errf(INSTALL_TOOL, "unknown option: %s", a);
    125         goto done;
    126       }
    127       continue;
    128     }
    129     if (!dir)
    130       dir = a;
    131     else
    132       explicit_tools[nexplicit++] = a;
    133   }
    134 
    135   if (!dir) {
    136     driver_errf(INSTALL_TOOL, "missing target directory");
    137     goto done;
    138   }
    139 
    140   /* Validate explicit tool names up front so a typo fails before we touch the
    141    * filesystem. */
    142   for (i = 0; i < nexplicit; ++i) {
    143     int tool_index = driver_tool_find(explicit_tools[i]);
    144     if (tool_index < 0) {
    145       driver_errf(INSTALL_TOOL, "no such tool: %s", explicit_tools[i]);
    146       goto done;
    147     }
    148     if (driver_tool_groups((unsigned)tool_index) == 0u) {
    149       driver_errf(INSTALL_TOOL, "tool is not installable: %s",
    150                   explicit_tools[i]);
    151       goto done;
    152     }
    153   }
    154 
    155   if (want_hard < 0)
    156     want_hard = (driver_host_target().os == KIT_OS_WINDOWS) ? 1 : 0;
    157 
    158   if (driver_self_exe_path(&env, &self, &self_size) != 0) {
    159     driver_errf(INSTALL_TOOL, "cannot determine path to the kit binary");
    160     rc = 1;
    161     goto done;
    162   }
    163 
    164   if (!dry && driver_mkdir_p(&env, dir) != 0) {
    165     driver_errf(INSTALL_TOOL, "cannot create directory %s", dir);
    166     rc = 1;
    167     goto done;
    168   }
    169 
    170   {
    171     DriverInstallLinkOpts opts;
    172     opts.target_exe = self;
    173     opts.tool_tag = INSTALL_TOOL;
    174     opts.use_hardlink = want_hard;
    175     opts.force = force;
    176     opts.dry_run = dry;
    177     opts.verbose = verbose;
    178 
    179     if (nexplicit > 0) {
    180       for (i = 0; i < nexplicit; ++i) {
    181         if (driver_install_link_one(&env, dir, explicit_tools[i], &opts) != 0)
    182           ++failures;
    183         else
    184           ++done_count;
    185       }
    186     } else {
    187       unsigned mask = all ? DRIVER_GROUP_ALL
    188                           : (DRIVER_GROUP_TOOLCHAIN | DRIVER_GROUP_BYTEUTIL);
    189       failures = driver_install_links(&env, dir, mask, &opts, &done_count);
    190     }
    191   }
    192 
    193   driver_printf("install: %s %u tool%s in %s\n",
    194                 dry ? "would install" : "installed", done_count,
    195                 done_count == 1u ? "" : "s", dir);
    196   rc = failures ? 1 : 0;
    197 
    198 done:
    199   if (self) driver_free(&env, self, self_size);
    200   if (explicit_tools)
    201     driver_free(&env, explicit_tools, (size_t)argc * sizeof(*explicit_tools));
    202   driver_env_fini(&env);
    203   return rc;
    204 }