install.c (8018B)
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 9 /* `kit install` — busybox-style toolchain installer. Populates a target 10 * directory with one entry per kit tool, each pointing at the running kit 11 * binary, so the tools can be invoked by their bare names (cc, ld, nm, ...) 12 * as a drop-in toolchain. Entries are symlinks on POSIX and hard links on 13 * Windows (where unprivileged symlinks aren't generally available); either 14 * can be forced with -s/-H. 15 * 16 * Multi-call dispatch in main.c resolves a link named `cc` to the cc tool by 17 * argv[0]'s basename, so no extra wiring is needed for the links to work. 18 * 19 * The default set (no TOOL args, no --all) is the tools tagged with a non-zero 20 * DriverToolGroup in the centralized table: the binutils/compiler toolchain 21 * plus the standard-named byte utilities (xxd, cmp). */ 22 23 #define INSTALL_TOOL "install" 24 25 void driver_help_install(void) { 26 driver_printf( 27 "kit install — symlink the kit tools into a directory\n" 28 "\n" 29 "USAGE\n" 30 " kit install [OPTIONS] DIR [TOOL...]\n" 31 "\n" 32 "DESCRIPTION\n" 33 " Populates DIR with one entry per kit tool, each pointing at the\n" 34 " running kit binary, so the tools can be invoked by their bare\n" 35 " names (cc, ld, nm, ...) as a drop-in toolchain. Entries are\n" 36 " symlinks on POSIX and hard links on Windows by default.\n" 37 "\n" 38 " With no TOOL given, installs the default set: the binutils/compiler\n" 39 " toolchain (cc cpp as ld ar ranlib strip objcopy objdump nm size\n" 40 " addr2line strings) plus the standard-named byte utilities (xxd cmp\n" 41 " sha256sum b2sum crc32 gzip gunzip lz4 lz4c). Use --all for every\n" 42 " tool, or name specific TOOLs to install just those.\n" 43 "\n" 44 "OPTIONS\n" 45 " -s, --symlink create symlinks (default except on Windows)\n" 46 " -H, --hardlink create hard links (default on Windows)\n" 47 " -a, --all install every tool compiled into this binary\n" 48 " -f, --force replace existing entries\n" 49 " -n, --dry-run print what would be done; change nothing\n" 50 " -v, --verbose print each link as it is created\n" 51 " -h, --help show this help\n" 52 "\n" 53 "EXIT CODES\n" 54 " 0 success 1 one or more links failed 2 bad usage\n"); 55 } 56 57 /* Join DIR + "/" + NAME into a freshly allocated, NUL-terminated path. Returns 58 * NULL on allocation failure; on success stores the allocation size in 59 * *out_size for driver_free. */ 60 static char* install_join(DriverEnv* env, const char* dir, const char* name, 61 size_t* out_size) { 62 size_t dl = driver_strlen(dir); 63 size_t nl = driver_strlen(name); 64 int slash = dl > 0 && dir[dl - 1u] != '/'; 65 size_t size = dl + (slash ? 1u : 0u) + nl + 1u; 66 size_t off; 67 char* p = (char*)driver_alloc(env, size); 68 if (!p) return NULL; 69 driver_memcpy(p, dir, dl); 70 off = dl; 71 if (slash) p[off++] = '/'; 72 driver_memcpy(p + off, name, nl); 73 off += nl; 74 p[off] = '\0'; 75 *out_size = size; 76 return p; 77 } 78 79 /* Create one link DIR/NAME -> self. Returns 0 on success, nonzero on failure 80 * (already emitted a diagnostic). Honors dry-run (prints, no change), force 81 * (replace existing), and verbose. */ 82 static int install_one(DriverEnv* env, const char* dir, const char* name, 83 const char* self, int want_hard, int force, int dry, 84 int verbose) { 85 size_t path_size = 0; 86 char* link_path = install_join(env, dir, name, &path_size); 87 int rc = 0; 88 89 if (!link_path) { 90 driver_errf(INSTALL_TOOL, "out of memory building path for %s", name); 91 return 1; 92 } 93 94 if (driver_path_lexists(link_path)) { 95 if (!force) { 96 driver_errf(INSTALL_TOOL, "%s already exists (use -f to overwrite)", 97 link_path); 98 rc = 1; 99 goto done; 100 } 101 if (!dry && driver_remove_file(link_path) != 0) { 102 driver_errf(INSTALL_TOOL, "cannot replace %s", link_path); 103 rc = 1; 104 goto done; 105 } 106 } 107 108 if (dry) { 109 driver_printf("%s -> %s (dry-run)\n", link_path, self); 110 goto done; 111 } 112 113 rc = want_hard ? driver_create_hardlink(self, link_path) 114 : driver_create_symlink(self, link_path); 115 if (rc != 0) { 116 driver_errf(INSTALL_TOOL, "failed to %s %s", 117 want_hard ? "hard-link" : "symlink", link_path); 118 rc = 1; 119 goto done; 120 } 121 if (verbose) driver_printf("%s -> %s\n", link_path, self); 122 123 done: 124 driver_free(env, link_path, path_size); 125 return rc; 126 } 127 128 int driver_install(int argc, char** argv) { 129 DriverEnv env; 130 const char* dir = NULL; 131 const char** explicit_tools = NULL; 132 int nexplicit = 0; 133 int want_hard = -1; /* -1 = pick by host OS; 0 = symlink; 1 = hard link */ 134 int all = 0, force = 0, dry = 0, verbose = 0, opts_done = 0; 135 char* self = NULL; 136 size_t self_size = 0; 137 unsigned done_count = 0, failures = 0; 138 int i, rc = 2; 139 140 if (driver_argv_wants_help(argc, argv, 1)) { 141 driver_help_install(); 142 return 0; 143 } 144 145 driver_env_init(&env); 146 147 explicit_tools = 148 (const char**)driver_alloc(&env, (size_t)argc * sizeof(*explicit_tools)); 149 if (!explicit_tools) { 150 driver_errf(INSTALL_TOOL, "out of memory"); 151 rc = 1; 152 goto done; 153 } 154 155 for (i = 1; i < argc; ++i) { 156 const char* a = argv[i]; 157 if (!opts_done && driver_streq(a, "--")) { 158 opts_done = 1; 159 continue; 160 } 161 if (!opts_done && a[0] == '-' && a[1] != '\0') { 162 if (driver_streq(a, "-s") || driver_streq(a, "--symlink")) { 163 want_hard = 0; 164 } else if (driver_streq(a, "-H") || driver_streq(a, "--hardlink")) { 165 want_hard = 1; 166 } else if (driver_streq(a, "-a") || driver_streq(a, "--all")) { 167 all = 1; 168 } else if (driver_streq(a, "-f") || driver_streq(a, "--force")) { 169 force = 1; 170 } else if (driver_streq(a, "-n") || driver_streq(a, "--dry-run")) { 171 dry = 1; 172 } else if (driver_streq(a, "-v") || driver_streq(a, "--verbose")) { 173 verbose = 1; 174 } else { 175 driver_errf(INSTALL_TOOL, "unknown option: %s", a); 176 goto done; 177 } 178 continue; 179 } 180 if (!dir) 181 dir = a; 182 else 183 explicit_tools[nexplicit++] = a; 184 } 185 186 if (!dir) { 187 driver_errf(INSTALL_TOOL, "missing target directory"); 188 goto done; 189 } 190 191 /* Validate explicit tool names up front so a typo fails before we touch the 192 * filesystem. */ 193 for (i = 0; i < nexplicit; ++i) { 194 if (driver_tool_find(explicit_tools[i]) < 0) { 195 driver_errf(INSTALL_TOOL, "no such tool: %s", explicit_tools[i]); 196 goto done; 197 } 198 } 199 200 if (want_hard < 0) 201 want_hard = (driver_host_target().os == KIT_OS_WINDOWS) ? 1 : 0; 202 203 if (driver_self_exe_path(&env, &self, &self_size) != 0) { 204 driver_errf(INSTALL_TOOL, "cannot determine path to the kit binary"); 205 rc = 1; 206 goto done; 207 } 208 209 if (!dry && driver_mkdir_p(&env, dir) != 0) { 210 driver_errf(INSTALL_TOOL, "cannot create directory %s", dir); 211 rc = 1; 212 goto done; 213 } 214 215 if (nexplicit > 0) { 216 for (i = 0; i < nexplicit; ++i) { 217 if (install_one(&env, dir, explicit_tools[i], self, want_hard, force, dry, 218 verbose) != 0) 219 ++failures; 220 else 221 ++done_count; 222 } 223 } else { 224 unsigned n = driver_tool_count(); 225 unsigned j; 226 for (j = 0; j < n; ++j) { 227 if (!all && driver_tool_groups(j) == 0) continue; 228 if (install_one(&env, dir, driver_tool_name(j), self, want_hard, force, 229 dry, verbose) != 0) 230 ++failures; 231 else 232 ++done_count; 233 } 234 } 235 236 driver_printf("install: %s %u tool%s in %s\n", 237 dry ? "would install" : "installed", done_count, 238 done_count == 1u ? "" : "s", dir); 239 rc = failures ? 1 : 0; 240 241 done: 242 if (self) driver_free(&env, self, self_size); 243 if (explicit_tools) 244 driver_free(&env, explicit_tools, (size_t)argc * sizeof(*explicit_tools)); 245 driver_env_fini(&env); 246 return rc; 247 }