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 }