kit

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

commit 880e14bb778689d371ccb98f0846db43469a6c2b
parent a9068bc8a65245385dab6ef24628b662be82a1fa
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 16 Jul 2026 10:12:07 -0700

cc: normalize portable C and multi-source dependencies

Diffstat:
Mdriver/cmd/cc.c | 737++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mtest/driver/run.sh | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 744 insertions(+), 153 deletions(-)

diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -50,7 +50,7 @@ * MMD — like MD but skip headers resolved via system (-isystem) dirs. * The MM/MMD filter keys on the resolved include directory (GCC semantics), * not the <...>-vs-"..." spelling, so a <foo.h> found via a plain -I dir is - * still reported. MD/MMD currently require -c (single-source path). */ + * still reported. Multi-source modes keep one rule and output path per source. */ typedef enum CcDepMode { CC_DEP_NONE = 0, CC_DEP_M, @@ -92,6 +92,7 @@ typedef struct CcOptions { int function_sections; /* -ffunction-sections */ int data_sections; /* -fdata-sections */ int auto_var_init; /* -ftrivial-auto-var-init= (KitAutoVarInit) */ + int stack_protector; /* KitStackProtectorMode */ int lto; /* -flto/-fno-lto */ uint64_t disabled_backend_features; int warnings_are_errors; /* -Werror */ @@ -141,6 +142,8 @@ typedef struct CcOptions { int dep_phony; /* -MP */ const char* dep_file; /* -MF */ const char** dep_targets; /* -MT/-MQ */ + char** owned_dep_targets; /* quoted -MQ storage */ + size_t* owned_dep_target_sizes; uint32_t ndep_targets; /* Link-session options and owned -Wl state. */ @@ -203,21 +206,24 @@ void driver_help_cc(void) { " -MD, -MMD, -MF FILE Side-effect dependencies/output file\n" " -MT TARGET, -MQ TARGET Dependency target spelling\n" "\n" - " -M and -MM currently require exactly one input. Generate one\n" - " dependency result per source instead of passing several sources.\n" + " -M/-MM emit one rule per source in input order. -MD/-MMD write\n" + " per-source dependency files during compile and compile-link.\n" "\n" "COMPILATION\n" " -std=c11 Select the supported C language level\n" " -fsyntax-only Diagnose only; write no output\n" " -c, -S Stop after object or assembly emission\n" - " --emit=c Portable C output; use -O0 in this release\n" + " --emit=c Portable C output (-O1/-O2 normalize to -O0)\n" " --emit=ir Semantic IR dump; requires -O1 or -O2\n" - " -O0, -O1, -O2 Optimize (-O2 currently aliases -O1)\n" + " -O0, -O1, -O2 Optimize (-O2 aliases -O1)\n" " -g Emit debug information\n" " -flto Record source inputs for LTO at link\n" " -fPIC/-fpic, -fPIE/-fpie Position-independent code/executable\n" " -Werror, -fmax-errors=N Diagnostic policy\n" " -ffreestanding Freestanding language/runtime mode\n" + " -fstack-protector[-strong|-all]\n" + " Select conventional stack canaries\n" + " -fno-stack-protector Disable stack canaries\n" " -x c|assembler|assembler-with-cpp\n" " Override suffix classification\n" "\n" @@ -231,8 +237,9 @@ void driver_help_cc(void) { " -Wl,... Forward supported linker tokens\n" " -framework NAME, -F DIR Darwin framework/search directory\n" "\n" - " `-Wl,-T,...` is not accepted in this release; pass -T SCRIPT and\n" - " -e SYMBOL directly. Static/shared availability follows the target\n" + " Linker scripts and entries accept direct joined/equal forms,\n" + " -Wl comma lists, and equivalent -Xlinker sequences. Static/shared\n" + " availability follows the target\n" " platform; non-ELF shared-library output is not provided here.\n"))); driver_printf( "%.*s", @@ -259,36 +266,31 @@ void driver_help_cc(void) { " WebAssembly source target: wasm32-none (source-batch module; this\n" " release does not link separate Wasm .o/.a inputs).\n" "\n" - "CURRENT RELEASE REQUIREMENTS AND LIMITS\n" - " From an unrelated working directory, pass\n" - " --support-dir \"$DIST/support\"\n" - " because this release does not reliably find the sibling support\n" - " tree after relocation. On macOS, feed the value printed by\n" - " -print-sysroot back through -isysroot for native SDK headers.\n" - " Cross-hosted compilation requires an explicit user-supplied sysroot.\n" + "DISCOVERY AND LIMITS\n" + " Kit resolves support relative to its canonical executable path;\n" + " --support-dir is an authoritative override. Native macOS applies\n" + " the discovered SDK automatically. Cross-hosted compilation still\n" + " requires an explicit user-supplied sysroot.\n" "\n" - " -fstack-protector is not implemented; use -fno-stack-protector.\n" - " Do not combine --emit=c with -O1/-O2 in this release: that path can\n" - " terminate the compiler. Portable-C emission is demonstrated at -O0.\n" + " Portable-C output currently uses the -O0 semantic pipeline; explicit\n" + " -O1/-O2 requests are accepted and normalized before code generation.\n" "\n" "GETTING HELP\n" " -h, --help Show this help and exit\n" " --version Show Kit version and exit\n" "\n" "EXAMPLES\n" - " # Native macOS with the current release's explicit paths.\n" + " # Native macOS: support and the SDK are discovered automatically.\n" " DIST=\"$PWD/kit\"\n" " K=\"$DIST/bin/kit\"\n" - " SDK=\"$(\"$K\" cc -print-sysroot)\"\n" " printf 'int main(void) { return 0; }\\n' > hello.c\n" - " \"$K\" cc --support-dir \"$DIST/support\" -isysroot \"$SDK\" \\\n" - " hello.c -o hello\n" + " \"$K\" cc hello.c -o hello\n" " ./hello\n" "\n" " # Replace SYSROOT with a supplied hosted cross sysroot.\n" " SYSROOT=/replace/with/aarch64-linux-sysroot\n" - " \"$K\" cc --support-dir \"$DIST/support\" \\\n" - " -target aarch64-linux-gnu --sysroot \"$SYSROOT\" hello.c -o hello.aa64\n" + " \"$K\" cc -target aarch64-linux-gnu --sysroot \"$SYSROOT\" \\\n" + " hello.c -o hello.aa64\n" "\n" " # Freestanding final link; provide startup object and linker script.\n" " \"$K\" cc --support-dir \"$DIST/support\" -target aarch64-none-elf \\\n" @@ -326,9 +328,9 @@ void driver_help_check(void) { " -target, --sysroot/-isysroot, and --support-dir. Inputs are C\n" " sources; each is preprocessed and checked independently.\n" "\n" - " A relocated distribution currently needs\n" - " --support-dir \"$DIST/support\". Hosted SDK headers need an\n" - " explicit --sysroot/-isysroot as described by `kit cc --help`.\n" + " Relocated distributions discover their sibling support tree. Native\n" + " macOS discovers SDK headers automatically; cross-hosted checks use\n" + " an explicit --sysroot/-isysroot as described by `kit cc --help`.\n" "\n" "GETTING HELP\n" " -h, --help Show this help and exit\n" @@ -353,13 +355,18 @@ static int cc_alloc_arrays(CcOptions* o, int argc) { o->source_memory = driver_alloc_zeroed(o->env, bound * sizeof(*o->source_memory)); o->dep_targets = driver_alloc_zeroed(o->env, bound * sizeof(*o->dep_targets)); + o->owned_dep_targets = + driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_dep_targets)); + o->owned_dep_target_sizes = driver_alloc_zeroed( + o->env, bound * sizeof(*o->owned_dep_target_sizes)); o->path_map = driver_alloc_zeroed(o->env, bound * sizeof(*o->path_map)); o->owned_path_map_olds = driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_map_olds)); o->owned_path_map_old_sizes = driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_map_old_sizes)); if (!o->source_files || !o->source_langs || !o->source_memory || - !o->dep_targets || !o->path_map || !o->owned_path_map_olds || + !o->dep_targets || !o->owned_dep_targets || + !o->owned_dep_target_sizes || !o->path_map || !o->owned_path_map_olds || !o->owned_path_map_old_sizes) { driver_errf(CC_TOOL, "out of memory"); return 1; @@ -386,6 +393,12 @@ static void cc_options_release(CcOptions* o) { o->owned_path_map_old_sizes[i]); } } + for (i = 0; i < o->ndep_targets; ++i) { + if (o->owned_dep_targets[i]) { + driver_free(o->env, o->owned_dep_targets[i], + o->owned_dep_target_sizes[i]); + } + } if (o->stdin_buf) driver_free(o->env, o->stdin_buf, o->stdin_size); if (o->owned_output_path) driver_free(o->env, o->owned_output_path, o->owned_output_path_size); @@ -398,6 +411,10 @@ static void cc_options_release(CcOptions* o) { driver_free(o->env, o->source_langs, bound * sizeof(*o->source_langs)); driver_free(o->env, o->source_memory, bound * sizeof(*o->source_memory)); driver_free(o->env, o->dep_targets, bound * sizeof(*o->dep_targets)); + driver_free(o->env, o->owned_dep_targets, + bound * sizeof(*o->owned_dep_targets)); + driver_free(o->env, o->owned_dep_target_sizes, + bound * sizeof(*o->owned_dep_target_sizes)); driver_free(o->env, o->path_map, bound * sizeof(*o->path_map)); driver_free(o->env, o->owned_path_map_olds, bound * sizeof(*o->owned_path_map_olds)); @@ -581,7 +598,12 @@ static int cc_run_probe(CcOptions* o) { KIT_SLICE_ARG(kit_slice_cstr(dirs.root ? dirs.root : ""))); driver_hosted_dirs_fini(&dirs); } else { - driver_printf("\n"); + /* An explicitly selected (argv or KIT_SYSROOT) path is authoritative. + * The resolver has already diagnosed an invalid one; do not turn that + * failure into a successful empty probe. A cross target with no + * supplied sysroot still resolves successfully with dirs.root == NULL + * and prints the conventional empty line above. */ + return 1; } return 0; } @@ -674,15 +696,15 @@ static int cc_classify_positional(CcOptions* o, const char* a, static int cc_apply_hosted_profile(CcOptions* o) { /* A link action (not -c/-E/-M/-MM) gets crt files + interpreter; the include * + define + lib-search-dir parts apply regardless. */ - int link_action = !o->compile_only && !o->preprocess_only && + int link_action = !o->syntax_only && !o->compile_only && + !o->preprocess_only && o->dep_mode != CC_DEP_M && o->dep_mode != CC_DEP_MM; int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target); if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0; - if (o->no_stdlib || o->no_defaultlibs) { - driver_errf(CC_TOOL, - "-lc hosted expansion is disabled by -nostdlib/-nodefaultlibs"); - return 1; - } + /* -nostdlib/-nodefaultlibs suppress the link-time CRT/libc additions, not + * the hosted compiler profile. Native compilation still needs the SDK's + * defines and headers (matching clang/gcc). */ + if (o->no_stdlib || o->no_defaultlibs) link_action = 0; return driver_link_inputs_apply_hosted( &o->inputs, &o->hosted, &o->cf, &o->link, o->target, o->sysroot, o->static_link, o->shared, o->no_startfiles, link_action); @@ -699,8 +721,8 @@ static int cc_apply_env(CcOptions* o) { } static int cc_has_link_action(const CcOptions* o) { - return !o->compile_only && !o->preprocess_only && o->dep_mode != CC_DEP_M && - o->dep_mode != CC_DEP_MM; + return !o->syntax_only && !o->compile_only && !o->preprocess_only && + o->dep_mode != CC_DEP_M && o->dep_mode != CC_DEP_MM; } /* A sysroot on its own means "compile/link hosted against that root" — the @@ -708,22 +730,27 @@ static int cc_has_link_action(const CcOptions* o) { * libc profile so the host headers, their feature-test defines (__GNUC__, * __APPLE__, __has_builtin, ...) and (for link actions) the host C runtime are * brought in. -ffreestanding and -nostdinc opt back out, keeping the - * freestanding rt/include set standalone; -shared and the -nostdlib family - * keep their existing meaning. The Windows-COFF default profile and an explicit + * freestanding rt/include set standalone; -shared keeps its existing meaning. + * -nostdlib/-nodefaultlibs only suppress link additions. The Windows-COFF + * default profile and an explicit * -lc already set the flag, so this is a no-op in those cases. */ static void cc_enable_hosted_for_sysroot(CcOptions* o) { if (o->wants_hosted_libc) return; if (o->shared && !driver_target_shared_uses_hosted(o->target)) return; if (!o->sysroot || !o->sysroot[0]) return; if (o->freestanding || o->nostdinc) return; - if (o->no_stdlib || o->no_defaultlibs) return; o->wants_hosted_libc = 1; } static void cc_apply_default_hosted_profile(CcOptions* o) { + KitTargetSpec host; + int native_macos; if (!driver_target_default_hosted_profile(o->target)) return; - if (o->no_stdlib || o->no_defaultlibs || o->wants_hosted_libc) return; - if (!o->sysroot || !o->sysroot[0]) return; + if (o->wants_hosted_libc) return; + if (o->freestanding || o->nostdinc) return; + host = driver_host_target(); + native_macos = host.os == KIT_OS_MACOS && o->target.os == KIT_OS_MACOS; + if ((!o->sysroot || !o->sysroot[0]) && !native_macos) return; if (!cc_has_link_action(o) && o->nsource_files + o->nsource_memory == 0) return; o->wants_hosted_libc = 1; @@ -732,6 +759,32 @@ static void cc_apply_default_hosted_profile(CcOptions* o) { static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o, size_t* out_size); +/* GNU make quoting used by -MQ and by driver-derived dependency paths. -MT is + * intentionally left verbatim. */ +static char* cc_dep_quote_make_alloc(DriverEnv* env, const char* s, + size_t* out_size) { + size_t n = driver_strlen(s); + size_t i; + size_t pos = 0; + char* out = driver_alloc(env, n * 2u + 1u); + if (!out) return NULL; + for (i = 0; i < n; ++i) { + char c = s[i]; + if (c == '$') { + out[pos++] = '$'; + out[pos++] = '$'; + } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') { + out[pos++] = '\\'; + out[pos++] = c; + } else { + out[pos++] = c; + } + } + out[pos] = '\0'; + if (out_size) *out_size = n * 2u + 1u; + return out; +} + static int cc_parse(int argc, char** argv, CcOptions* o) { int forced_lang = -1; int i; @@ -892,11 +945,23 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { continue; } if (driver_streq(a, "-fno-stack-protector")) { + o->stack_protector = KIT_STACK_PROTECTOR_NONE; + continue; + } + if (driver_streq(a, "-fstack-protector")) { + o->stack_protector = KIT_STACK_PROTECTOR_BASIC; + continue; + } + if (driver_streq(a, "-fstack-protector-strong")) { + o->stack_protector = KIT_STACK_PROTECTOR_STRONG; + continue; + } + if (driver_streq(a, "-fstack-protector-all")) { + o->stack_protector = KIT_STACK_PROTECTOR_ALL; continue; } if (driver_strneq(a, "-fstack-protector", 17)) { - driver_errf(CC_TOOL, - "%.*s is not supported yet; use -fno-stack-protector", + driver_errf(CC_TOOL, "unsupported stack protector mode: %.*s", KIT_SLICE_ARG(kit_slice_cstr(a))); return 1; } @@ -1181,7 +1246,31 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { driver_errf(CC_TOOL, "-e requires an argument"); return 1; } - o->link.entry = argv[i]; + if (driver_link_flags_record_entry(&o->link, argv[i], + driver_strlen(argv[i])) != 0) + return 1; + continue; + } + if (driver_strneq(a, "--entry=", 8)) { + if (driver_link_flags_record_entry(&o->link, a + 8, + driver_strlen(a + 8)) != 0) + return 1; + continue; + } + if (driver_streq(a, "--entry")) { + if (++i >= argc) { + driver_errf(CC_TOOL, "--entry requires an argument"); + return 1; + } + if (driver_link_flags_record_entry(&o->link, argv[i], + driver_strlen(argv[i])) != 0) + return 1; + continue; + } + if (a[0] == '-' && a[1] == 'e' && a[2] != '\0') { + if (driver_link_flags_record_entry(&o->link, a + 2, + driver_strlen(a + 2)) != 0) + return 1; continue; } if (driver_streq(a, "-T")) { @@ -1189,7 +1278,34 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { driver_errf(CC_TOOL, "-T requires an argument"); return 1; } - o->link.linker_script = argv[i]; + if (driver_link_flags_record_script(&o->link, argv[i], + driver_strlen(argv[i])) != 0) + return 1; + continue; + } + if (driver_strneq(a, "--script=", 9)) { + if (driver_link_flags_record_script(&o->link, a + 9, + driver_strlen(a + 9)) != 0) + return 1; + continue; + } + if (driver_streq(a, "--script")) { + if (++i >= argc) { + driver_errf(CC_TOOL, "--script requires an argument"); + return 1; + } + if (driver_link_flags_record_script(&o->link, argv[i], + driver_strlen(argv[i])) != 0) + return 1; + continue; + } + if (a[0] == '-' && a[1] == 'T' && a[2] != '\0' && + !driver_strneq(a, "-Ttext", 6) && + !driver_strneq(a, "-Tdata", 6) && + !driver_strneq(a, "-Tbss", 5)) { + if (driver_link_flags_record_script(&o->link, a + 2, + driver_strlen(a + 2)) != 0) + return 1; continue; } if (driver_streq(a, "-target")) { @@ -1198,8 +1314,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { return 1; } if (driver_target_from_triple(argv[i], &o->target) != 0) { - driver_errf(CC_TOOL, "unrecognized target triple: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(argv[i]))); + driver_err_unknown_target(CC_TOOL, argv[i]); return 1; } o->target_set = 1; @@ -1207,8 +1322,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { } if (driver_strneq(a, "--target=", 9)) { if (driver_target_from_triple(a + 9, &o->target) != 0) { - driver_errf(CC_TOOL, "unrecognized target triple: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(a + 9))); + driver_err_unknown_target(CC_TOOL, a + 9); return 1; } o->target_set = 1; @@ -1220,18 +1334,19 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { return 1; } if (driver_target_from_triple(argv[i], &o->target) != 0) { - driver_errf(CC_TOOL, "unrecognized target triple: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(argv[i]))); + driver_err_unknown_target(CC_TOOL, argv[i]); return 1; } o->target_set = 1; continue; } if (driver_streq(a, "-Xlinker")) { + const char* xarg; if (++i >= argc) { driver_errf(CC_TOOL, "-Xlinker requires an argument"); return 1; } + xarg = argv[i]; if (driver_streq(argv[i], "-framework")) { if (++i >= argc) { driver_errf(CC_TOOL, "-framework requires an argument"); @@ -1246,7 +1361,31 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { if (cc_record_framework(o, argv[i]) != 0) return 1; continue; } - if (driver_link_flags_record_wl(&o->link, argv[i]) != 0) return 1; + if (driver_streq(xarg, "-T") || driver_streq(xarg, "--script") || + driver_streq(xarg, "-e") || driver_streq(xarg, "--entry")) { + const char* value; + if (++i >= argc) { + driver_errf(CC_TOOL, "%s passed via -Xlinker requires an argument", + xarg); + return 1; + } + if (driver_streq(argv[i], "-Xlinker")) { + if (++i >= argc) { + driver_errf(CC_TOOL, + "%s passed via -Xlinker requires an argument", xarg); + return 1; + } + } + value = argv[i]; + if ((xarg[1] == 'T' || driver_streq(xarg, "--script")) + ? driver_link_flags_record_script(&o->link, value, + driver_strlen(value)) + : driver_link_flags_record_entry(&o->link, value, + driver_strlen(value))) + return 1; + continue; + } + if (driver_link_flags_record_wl(&o->link, xarg) != 0) return 1; continue; } if (driver_streq(a, "-x")) { @@ -1267,8 +1406,25 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { { KitLanguage lang = kit_language_for_name(NULL, argv[i]); if (lang == KIT_LANG_UNKNOWN) { - driver_errf(CC_TOOL, "unsupported -x language: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(argv[i]))); + const char* const known[] = {"c", "asm", "assembler", + "s", "toy", "wasm", + "wat", "none", "asm-cpp"}; + const char* candidates[sizeof known / sizeof known[0]]; + DriverSuggestion suggestions[3]; + size_t k, count = 0, n; + for (k = 0; k < sizeof known / sizeof known[0]; ++k) { + if (driver_streq(known[k], "none") || + driver_streq(known[k], "asm-cpp") || + kit_language_for_name(NULL, known[k]) != KIT_LANG_UNKNOWN) + candidates[count++] = known[k]; + } + n = driver_suggest_values(argv[i], candidates, count, suggestions, 3); + if (n) + driver_errf(CC_TOOL, + "unsupported -x language: %s; did you mean '%s'?", + argv[i], suggestions[0].value); + else + driver_errf(CC_TOOL, "unsupported -x language: %s", argv[i]); return 1; } forced_lang = lang; @@ -1354,12 +1510,25 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { continue; } if (driver_streq(a, "-MT") || driver_streq(a, "-MQ")) { + int quote = driver_streq(a, "-MQ"); + uint32_t slot = o->ndep_targets; if (++i >= argc) { driver_errf(CC_TOOL, "%.*s requires an argument", KIT_SLICE_ARG(kit_slice_cstr(a))); return 1; } - o->dep_targets[o->ndep_targets++] = argv[i]; + if (quote) { + o->owned_dep_targets[slot] = cc_dep_quote_make_alloc( + o->env, argv[i], &o->owned_dep_target_sizes[slot]); + if (!o->owned_dep_targets[slot]) { + driver_errf(CC_TOOL, "out of memory"); + return 1; + } + o->dep_targets[slot] = o->owned_dep_targets[slot]; + } else { + o->dep_targets[slot] = argv[i]; + } + o->ndep_targets++; continue; } @@ -1374,8 +1543,22 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { continue; } if (a[0] == '-' && a[1] != '\0') { - driver_errf(CC_TOOL, "unknown flag: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(a))); + const char* const valid[] = { + "--help", "-h", "--version", "-target", + "--target", "-o", "--output", "-T", + "--script", "-e", "--entry", "-x", + "-c", "-S", "-E", "-shared", + "-static", "-nostdlib", "-isysroot", "--sysroot", + "--start-group", "--end-group"}; + DriverSuggestion suggestions[3]; + size_t n = driver_suggest_values(a, valid, + sizeof valid / sizeof valid[0], + suggestions, 3); + if (n) + driver_errf(CC_TOOL, "unknown flag: %s; did you mean '%s'?", a, + suggestions[0].value); + else + driver_errf(CC_TOOL, "unknown flag: %s", a); return 1; } @@ -1481,22 +1664,13 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { } { int dep_only = (o->dep_mode == CC_DEP_M || o->dep_mode == CC_DEP_MM); - int dep_with_compile = - (o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD); if (o->dep_mode != CC_DEP_NONE && o->preprocess_only) { driver_errf(CC_TOOL, "-M* is incompatible with -E"); return 1; } - if (dep_only && total_sources != 1) { - driver_errf(CC_TOOL, "-M/-MM requires exactly one input"); - return 1; - } - if (dep_with_compile && !o->compile_only) { - driver_errf(CC_TOOL, "-MD/-MMD currently requires -c"); - return 1; - } - if (dep_with_compile && total_sources != 1) { - driver_errf(CC_TOOL, "-MD/-MMD currently requires exactly one source"); + if (dep_only && (total_sources == 0 || total_link != 0)) { + driver_errf(CC_TOOL, + "-M/-MM requires source inputs and no link inputs"); return 1; } if (!o->output_path && !dep_only) { @@ -1919,67 +2093,113 @@ static int cc_dep_collect(DriverEnv* env, KitCompiler* compiler, static void cc_dep_emit_rule(KitWriter* w, const char* const* targets, uint32_t ntargets, const char* primary_src, - const CcDepList* deps, int phony) { + const CcDepList* deps, int phony, + int quote_default_target) { uint32_t i; for (i = 0; i < ntargets; ++i) { + const char* s; if (i) cc_write_str(w, " "); - cc_write_str(w, targets[i]); + s = targets[i]; + if (!quote_default_target) { + cc_write_str(w, s); + } else { + for (; *s; ++s) { + char c = *s; + if (c == '$') { + cc_write_str(w, "$$"); + } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') { + kit_writer_write(w, "\\", 1); + kit_writer_write(w, &c, 1); + } else { + kit_writer_write(w, &c, 1); + } + } + } } cc_write_str(w, ":"); if (primary_src) { + const char* s = primary_src; cc_write_str(w, " "); - cc_write_str(w, primary_src); + for (; *s; ++s) { + char c = *s; + if (c == '$') { + cc_write_str(w, "$$"); + } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') { + kit_writer_write(w, "\\", 1); + kit_writer_write(w, &c, 1); + } else { + kit_writer_write(w, &c, 1); + } + } } for (i = 0; i < deps->n; ++i) { + const char* s = deps->items[i]; cc_write_str(w, " \\\n "); - cc_write_str(w, deps->items[i]); + for (; *s; ++s) { + char c = *s; + if (c == '$') { + cc_write_str(w, "$$"); + } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') { + kit_writer_write(w, "\\", 1); + kit_writer_write(w, &c, 1); + } else { + kit_writer_write(w, &c, 1); + } + } } cc_write_str(w, "\n"); if (phony) { for (i = 0; i < deps->n; ++i) { + const char* s = deps->items[i]; cc_write_str(w, "\n"); - cc_write_str(w, deps->items[i]); + for (; *s; ++s) { + char c = *s; + if (c == '$') { + cc_write_str(w, "$$"); + } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') { + kit_writer_write(w, "\\", 1); + kit_writer_write(w, &c, 1); + } else { + kit_writer_write(w, &c, 1); + } + } cc_write_str(w, ":\n"); } } } -static KitWriter* cc_dep_open_writer(DriverEnv* env, const KitContext* ctx, - const CcOptions* o, char** owned_path, - size_t* owned_path_size) { - KitWriter* w = NULL; - *owned_path = NULL; - *owned_path_size = 0; - if (o->dep_file) { - if (ctx->file_io->open_writer(ctx->file_io->user, o->dep_file, &w) != - KIT_OK) - return NULL; - return w; - } - if (o->dep_mode == CC_DEP_M || o->dep_mode == CC_DEP_MM) { - return driver_stdout_writer(env); - } - { - char* p = cc_dep_default_path(env, o->output_path, owned_path_size); - if (!p) return NULL; - *owned_path = p; - if (ctx->file_io->open_writer(ctx->file_io->user, p, &w) != KIT_OK) - return NULL; - return w; - } -} - static const char* cc_primary_source_name(const CcOptions* o) { if (o->nsource_memory == 1) return o->source_memory[0].name.s; return o->source_files[0]; } -static int cc_dep_finish(DriverEnv* env, const KitContext* ctx, - KitCompiler* compiler, const CcOptions* o) { +static char* cc_dep_default_target_for(DriverEnv* env, const CcOptions* o, + const char* source_name, + const char* output_path, + size_t* out_size) { + CcOptions view = *o; + const char* one_file[1]; + KitSourceInput one_memory[1]; + view.output_path = output_path; + view.nsource_files = 0; + view.nsource_memory = 0; + if (driver_streq(source_name, "<stdin>")) { + memset(one_memory, 0, sizeof(one_memory)); + one_memory[0].name = kit_slice_cstr(source_name); + view.source_memory = one_memory; + view.nsource_memory = 1; + } else { + one_file[0] = source_name; + view.source_files = one_file; + view.nsource_files = 1; + } + return cc_dep_default_target(env, &view, out_size); +} + +static int cc_dep_finish_to(DriverEnv* env, KitCompiler* compiler, + const CcOptions* o, const char* source_name, + const char* output_path, KitWriter* dep_w) { CcDepList deps = {0}; - KitWriter* dep_w = NULL; - char* owned_path = NULL; - size_t owned_size = 0; char* owned_target = NULL; size_t owned_target_size = 0; const char* one_target[1]; @@ -2000,7 +2220,8 @@ static int cc_dep_finish(DriverEnv* env, const KitContext* ctx, targets = o->dep_targets; ntargets = o->ndep_targets; if (ntargets == 0) { - owned_target = cc_dep_default_target(env, o, &owned_target_size); + owned_target = cc_dep_default_target_for( + env, o, source_name, output_path, &owned_target_size); if (!owned_target) { driver_errf(CC_TOOL, "out of memory"); goto out; @@ -2010,27 +2231,78 @@ static int cc_dep_finish(DriverEnv* env, const KitContext* ctx, ntargets = 1; } - dep_w = cc_dep_open_writer(env, ctx, o, &owned_path, &owned_size); - if (!dep_w) { - driver_errf(CC_TOOL, "failed to open dep output: %.*s", - KIT_SLICE_ARG(kit_slice_cstr( - o->dep_file ? o->dep_file - : (owned_path ? owned_path : "<stdout>")))); - goto out; - } - - cc_dep_emit_rule(dep_w, targets, ntargets, cc_primary_source_name(o), &deps, - o->dep_phony); + cc_dep_emit_rule(dep_w, targets, ntargets, source_name, &deps, o->dep_phony, + o->ndep_targets == 0); rc = kit_writer_status(dep_w) == KIT_OK ? 0 : 1; out: - if (dep_w) kit_writer_close(dep_w); - if (owned_path) driver_free(env, owned_path, owned_size); if (owned_target) driver_free(env, owned_target, owned_target_size); cc_dep_list_free(env, &deps); return rc; } +static KitWriter* cc_dep_buffer_new(const KitContext* ctx) { + KitWriter* w = NULL; + if (!ctx || !ctx->heap || kit_writer_mem(ctx->heap, &w) != KIT_OK) return NULL; + return w; +} + +static int cc_dep_commit_buffer(DriverEnv* env, const KitContext* ctx, + KitWriter* mem, const char* path) { + KitWriter* out = NULL; + const uint8_t* bytes; + size_t len = 0; + int rc = 1; + bytes = kit_writer_mem_bytes(mem, &len); + if (path) { + if (ctx->file_io->open_writer(ctx->file_io->user, path, &out) != KIT_OK) { + driver_errf(CC_TOOL, "failed to open dep output: %.*s", + KIT_SLICE_ARG(kit_slice_cstr(path))); + return 1; + } + } else { + out = driver_stdout_writer(env); + if (!out) { + driver_errf(CC_TOOL, "out of memory"); + return 1; + } + } + if (kit_writer_write(out, bytes, len) == KIT_OK && + kit_writer_status(out) == KIT_OK) + rc = 0; + if (rc != 0 && path) driver_writer_abort(out); + kit_writer_close(out); + return rc; +} + +static int cc_dep_finish_default_file(DriverEnv* env, const KitContext* ctx, + KitCompiler* compiler, + const CcOptions* o, + const char* source_name, + const char* output_path) { + KitWriter* mem = NULL; + char* dep_path = NULL; + size_t dep_path_size = 0; + int rc = 1; + mem = cc_dep_buffer_new(ctx); + if (!mem) { + driver_errf(CC_TOOL, "out of memory"); + goto out; + } + if (cc_dep_finish_to(env, compiler, o, source_name, output_path, mem) != 0) + goto out; + dep_path = cc_dep_default_path(env, output_path, &dep_path_size); + if (!dep_path) { + driver_errf(CC_TOOL, "out of memory"); + goto out; + } + rc = cc_dep_commit_buffer(env, ctx, mem, dep_path); +out: + if (dep_path) driver_free(env, dep_path, dep_path_size); + if (mem) kit_writer_close(mem); + return rc; +} + static void cc_fill_c_opts(const CcOptions* o, KitCCompileOptions* copts) { KitCCompileOptions zero = {0}; *copts = zero; @@ -2045,6 +2317,7 @@ static void cc_fill_c_opts(const CcOptions* o, KitCCompileOptions* copts) { copts->code.data_sections = o->data_sections ? true : false; copts->code.disabled_backend_features = o->disabled_backend_features; copts->code.trivial_auto_var_init = (uint8_t)o->auto_var_init; + copts->code.stack_protector = (uint8_t)o->stack_protector; copts->code.lto = o->lto ? true : false; copts->code.epoch = o->epoch; copts->code.path_map = o->npath_map ? o->path_map : NULL; @@ -2053,8 +2326,15 @@ static void cc_fill_c_opts(const CcOptions* o, KitCCompileOptions* copts) { copts->diagnostics.max_errors = o->max_errors; } -static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, - const KitPreprocessOptions* pp) { +static const char* cc_source_name(const CcOptions* o, int is_memory, + uint32_t index) { + return is_memory ? o->source_memory[index].name.s : o->source_files[index]; +} + +static int cc_dep_scan_one(DriverEnv* env, const CcOptions* o, + const KitPreprocessOptions* pp, int is_memory, + uint32_t index, const char* output_path, + KitWriter* rules) { KitContext ctx = driver_env_to_context(env); KitTarget* target = NULL; KitCompiler* compiler = NULL; @@ -2063,8 +2343,21 @@ static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, KitSlice input = {0}; int loaded = 0; int rc = 1; + const char* source_name = cc_source_name(o, is_memory, index); - if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out; + if (is_memory) { + input = o->source_memory[index].bytes; + } else { + if (ctx.file_io->read_all(ctx.file_io->user, o->source_files[index], &fd) != + KIT_OK) { + driver_errf(CC_TOOL, "failed to read: %.*s", + KIT_SLICE_ARG(kit_slice_cstr(o->source_files[index]))); + goto out; + } + loaded = 1; + input.data = fd.data; + input.len = fd.size; + } discard = cc_discard_writer_new(env); if (!discard) { @@ -2077,12 +2370,11 @@ static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, goto out; } - if (kit_cpp_preprocess(compiler, pp, - kit_slice_cstr(cc_primary_source_name(o)), &input, + if (kit_cpp_preprocess(compiler, pp, kit_slice_cstr(source_name), &input, discard) != KIT_OK) goto out; - rc = cc_dep_finish(env, &ctx, compiler, o); + rc = cc_dep_finish_to(env, compiler, o, source_name, output_path, rules); out: if (compiler) driver_compiler_free(compiler); @@ -2092,6 +2384,114 @@ out: return rc; } +static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, + const KitPreprocessOptions* pp) { + KitContext ctx = driver_env_to_context(env); + KitWriter* rules = NULL; + uint32_t i; + int rc = 1; + rules = cc_dep_buffer_new(&ctx); + if (!rules) { + driver_errf(CC_TOOL, "out of memory"); + return 1; + } + for (i = 0; i < o->inputs.nlink_items; ++i) { + const DriverLinkItem* item = &o->inputs.link_items[i]; + if (item->kind == DRIVER_LINK_SOURCE) { + if (cc_dep_scan_one(env, o, pp, 0, item->index, NULL, rules) != 0) + goto out; + } else if (item->kind == DRIVER_LINK_SOURCE_MEMORY) { + if (cc_dep_scan_one(env, o, pp, 1, item->index, NULL, rules) != 0) + goto out; + } + } + rc = cc_dep_commit_buffer(env, &ctx, rules, o->dep_file); +out: + kit_writer_close(rules); + return rc; +} + +static int cc_dep_scan_default_file(DriverEnv* env, const CcOptions* o, + const KitPreprocessOptions* pp, + int is_memory, uint32_t index, + const char* output_path) { + KitContext ctx = driver_env_to_context(env); + KitWriter* rules = NULL; + char* dep_path = NULL; + size_t dep_path_size = 0; + int rc = 1; + rules = cc_dep_buffer_new(&ctx); + if (!rules) { + driver_errf(CC_TOOL, "out of memory"); + goto out; + } + if (cc_dep_scan_one(env, o, pp, is_memory, index, output_path, rules) != 0) + goto out; + dep_path = cc_dep_default_path(env, output_path, &dep_path_size); + if (!dep_path) { + driver_errf(CC_TOOL, "out of memory"); + goto out; + } + rc = cc_dep_commit_buffer(env, &ctx, rules, dep_path); +out: + if (dep_path) driver_free(env, dep_path, dep_path_size); + if (rules) kit_writer_close(rules); + return rc; +} + +static int cc_run_link_dependencies(DriverEnv* env, const CcOptions* o, + const KitPreprocessOptions* pp) { + KitContext ctx = driver_env_to_context(env); + KitWriter* shared = NULL; + uint32_t i; + int rc = 1; + if (o->dep_file) { + shared = cc_dep_buffer_new(&ctx); + if (!shared) { + driver_errf(CC_TOOL, "out of memory"); + return 1; + } + } + for (i = 0; i < o->inputs.nlink_items; ++i) { + const DriverLinkItem* item = &o->inputs.link_items[i]; + int is_memory; + const char* source_name; + const char* obj_path; + char* owned_obj = NULL; + size_t owned_obj_size = 0; + if (item->kind != DRIVER_LINK_SOURCE && + item->kind != DRIVER_LINK_SOURCE_MEMORY) + continue; + is_memory = item->kind == DRIVER_LINK_SOURCE_MEMORY; + source_name = cc_source_name(o, is_memory, item->index); + if (is_memory) { + obj_path = "<stdin>.o"; + } else { + owned_obj = + cc_default_obj_path_for_name(env, o, source_name, &owned_obj_size); + if (!owned_obj) { + driver_errf(CC_TOOL, "out of memory"); + goto out; + } + obj_path = owned_obj; + } + if (shared) { + rc = cc_dep_scan_one(env, o, pp, is_memory, item->index, obj_path, + shared); + } else { + rc = cc_dep_scan_default_file(env, o, pp, is_memory, item->index, + obj_path); + } + if (owned_obj) driver_free(env, owned_obj, owned_obj_size); + if (rc != 0) goto out; + } + if (shared) rc = cc_dep_commit_buffer(env, &ctx, shared, o->dep_file); + else rc = 0; +out: + if (shared) kit_writer_close(shared); + return rc; +} + /* Compile one source to an object builder via the shared engine. cc never * passes frontend-specific language_options (it has no flag surface for them); * those are the `compile` tool's domain. */ @@ -2117,7 +2517,8 @@ static KitStatus cc_compile_source_emit(KitCompiler* compiler, KitLanguage lang, static int cc_run_compile_one(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp, int is_memory, - uint32_t index, const char* out_path) { + uint32_t index, const char* out_path, + KitWriter* dep_rules) { KitContext ctx = driver_env_to_context(env); KitTarget* target = NULL; KitCompiler* compiler = NULL; @@ -2127,6 +2528,7 @@ static int cc_run_compile_one(DriverEnv* env, const CcOptions* o, KitCCompileOptions copts; int loaded = 0; int rc = 1; + const char* source_name = cc_source_name(o, is_memory, index); if (is_memory) { input = o->source_memory[index].bytes; @@ -2171,17 +2573,21 @@ static int cc_run_compile_one(DriverEnv* env, const CcOptions* o, ? o->source_memory[index].lang : cc_resolve_lang(compiler, o->source_files[index], o->source_langs[index]); - KitSlice in_name = is_memory ? o->source_memory[index].name - : kit_slice_cstr(o->source_files[index]); + KitSlice in_name = kit_slice_cstr(source_name); KitStatus st; st = cc_compile_source_emit(compiler, lang, &copts, pp, in_name, &input, obj_w); if (st != KIT_OK) goto out; } - rc = (o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) - ? cc_dep_finish(env, &ctx, compiler, o) - : 0; + if (o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) { + rc = dep_rules ? cc_dep_finish_to(env, compiler, o, source_name, out_path, + dep_rules) + : cc_dep_finish_default_file(env, &ctx, compiler, o, + source_name, out_path); + } else { + rc = 0; + } out: if (compiler) driver_compiler_free(compiler); @@ -2191,36 +2597,57 @@ out: return rc; } -static int cc_run_compile_obj(DriverEnv* env, const CcOptions* o, - const KitPreprocessOptions* pp) { - return cc_run_compile_one(env, o, pp, o->nsource_memory == 1, 0, - o->output_path); -} - static int cc_run_compile_objs(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp) { + KitContext ctx = driver_env_to_context(env); + KitWriter* dep_rules = NULL; uint32_t i; - if (o->output_path) return cc_run_compile_obj(env, o, pp); - for (i = 0; i < o->nsource_files; ++i) { - size_t out_size = 0; - char* out = - cc_default_obj_path_for_name(env, o, o->source_files[i], &out_size); - int rc; - if (!out) { + int rc = 1; + if ((o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) && + o->dep_file) { + dep_rules = cc_dep_buffer_new(&ctx); + if (!dep_rules) { driver_errf(CC_TOOL, "out of memory"); return 1; } - rc = cc_run_compile_one(env, o, pp, 0, i, out); - driver_free(env, out, out_size); - if (rc != 0) return rc; } - for (i = 0; i < o->nsource_memory; ++i) { - const char* out = o->emit_asm_source ? "<stdin>.s" - : o->emit_ir ? "<stdin>.ir" - : "<stdin>.o"; - if (cc_run_compile_one(env, o, pp, 1, i, out) != 0) return 1; + for (i = 0; i < o->inputs.nlink_items; ++i) { + const DriverLinkItem* item = &o->inputs.link_items[i]; + int is_memory; + const char* source_name; + const char* out; + char* owned_out = NULL; + size_t owned_out_size = 0; + if (item->kind != DRIVER_LINK_SOURCE && + item->kind != DRIVER_LINK_SOURCE_MEMORY) + continue; + is_memory = item->kind == DRIVER_LINK_SOURCE_MEMORY; + source_name = cc_source_name(o, is_memory, item->index); + if (o->output_path) { + out = o->output_path; + } else if (is_memory) { + out = o->emit_asm_source ? "<stdin>.s" + : o->emit_ir ? "<stdin>.ir" + : "<stdin>.o"; + } else { + owned_out = cc_default_obj_path_for_name(env, o, source_name, + &owned_out_size); + if (!owned_out) { + driver_errf(CC_TOOL, "out of memory"); + goto out; + } + out = owned_out; + } + rc = cc_run_compile_one(env, o, pp, is_memory, item->index, out, + dep_rules); + if (owned_out) driver_free(env, owned_out, owned_out_size); + if (rc != 0) goto out; } - return 0; + if (dep_rules) rc = cc_dep_commit_buffer(env, &ctx, dep_rules, o->dep_file); + else rc = 0; +out: + if (dep_rules) kit_writer_close(dep_rules); + return rc; } static int cc_run_check(DriverEnv* env, const CcOptions* o, @@ -2558,6 +2985,10 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o, if (st != KIT_OK) goto out; } + if ((o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) && + cc_run_link_dependencies(env, o, pp) != 0) + goto out; + if (io->open_writer(io->user, o->output_path, &out_w) != KIT_OK) { driver_errf(CC_TOOL, "failed to open output: %.*s", KIT_SLICE_ARG(kit_slice_cstr(o->output_path))); @@ -2713,7 +3144,7 @@ static int driver_cc_main(int argc, char** argv, int force_check) { if (driver_runtime_resolve(&env, co.support_dir, co.driver_path, &runtime) == 0) { runtime_resolved = 1; - if (co.nsource_files || co.nsource_memory) { + if ((co.nsource_files || co.nsource_memory) && !co.nostdinc) { int add_headers; if (co.hosted.profile_name) { add_headers = diff --git a/test/driver/run.sh b/test/driver/run.sh @@ -133,6 +133,166 @@ for xlang in wasm wat; do fi done +# Portable-C accepts the public optimization spellings by normalizing them +# before optimizer construction. Recompile every emitted translation unit to +# prove the result is usable for both the native target and representative +# cross-target ABI selections. +for opt in 0 1 2; do + out="$work/portable-native-O$opt.c" + if "$KIT" cc "-O$opt" --emit=c "$work/main.c" -o "$out" \ + > "$work/portable-native-O$opt.out" \ + 2> "$work/portable-native-O$opt.err" && + "$KIT" cc -c "$out" -o "$work/portable-native-O$opt.o" \ + >> "$work/portable-native-O$opt.out" \ + 2>> "$work/portable-native-O$opt.err"; then + ok "cc-portable-native-O$opt" + else + not_ok "cc-portable-native-O$opt" "$work/portable-native-O$opt.err" + fi +done +for target in aarch64-none-elf x86_64-none-elf riscv64-none-elf; do + for opt in 0 1 2; do + tag=$(printf '%s' "$target" | tr - _) + out="$work/portable-$tag-O$opt.c" + if "$KIT" cc -target "$target" -ffreestanding "-O$opt" --emit=c \ + "$work/main.c" -o "$out" \ + > "$work/portable-$tag-O$opt.out" \ + 2> "$work/portable-$tag-O$opt.err" && + "$KIT" cc -target "$target" -ffreestanding -c "$out" \ + -o "$work/portable-$tag-O$opt.o" \ + >> "$work/portable-$tag-O$opt.out" \ + 2>> "$work/portable-$tag-O$opt.err"; then + ok "cc-portable-$tag-O$opt" + else + not_ok "cc-portable-$tag-O$opt" "$work/portable-$tag-O$opt.err" + fi + done +done + +# ---- multi-source dependency generation ---- +cat > "$work/dep common.h" <<'SRC' +#define DEP_VALUE 19 +SRC +cat > "$work/dep-a.c" <<'SRC' +#include "dep common.h" +int dep_a(void) { return DEP_VALUE; } +SRC +cat > "$work/dep-b.c" <<'SRC' +#include "dep common.h" +int dep_b(void) { return DEP_VALUE + 4; } +SRC +cat > "$work/dep-main.c" <<'SRC' +int dep_a(void); +int dep_b(void); +int main(void) { return dep_a() + dep_b() == 42 ? 0 : 1; } +SRC + +run_ok "cc-deps-mm-multi" "$KIT" cc -MM -I"$work" \ + "$work/dep-a.c" "$work/dep-b.c" +contains "cc-deps-mm-first" "$work/cc-deps-mm-multi.out" "dep-a.o:" +contains "cc-deps-mm-second" "$work/cc-deps-mm-multi.out" "dep-b.o:" +contains "cc-deps-path-escaping" "$work/cc-deps-mm-multi.out" \ + "dep\\ common.h" +dep_a_line=$(grep -n '^dep-a\.o:' "$work/cc-deps-mm-multi.out" | \ + sed -n '1s/:.*//p') +dep_b_line=$(grep -n '^dep-b\.o:' "$work/cc-deps-mm-multi.out" | \ + sed -n '1s/:.*//p') +if [ -n "$dep_a_line" ] && [ -n "$dep_b_line" ] && \ + [ "$dep_a_line" -lt "$dep_b_line" ]; then + ok "cc-deps-input-order" +else + echo "dependency rules are not in source input order" > \ + "$work/cc-deps-input-order.diag" + not_ok "cc-deps-input-order" "$work/cc-deps-input-order.diag" +fi + +# A stdin source between file sources must stay in argv order even though the +# driver's owning storage for file and memory sources is separate. +if printf 'int dep_stdin(void) { return 0; }\n' | \ + "$KIT" cc -MM -I"$work" "$work/dep-a.c" - "$work/dep-b.c" \ + > "$work/cc-deps-stdin-order.out" \ + 2> "$work/cc-deps-stdin-order.err"; then + stdin_a=$(grep -n '^dep-a\.o:' "$work/cc-deps-stdin-order.out" | \ + sed -n '1s/:.*//p') + stdin_mid=$(grep -n '^<stdin>\.o:' "$work/cc-deps-stdin-order.out" | \ + sed -n '1s/:.*//p') + stdin_b=$(grep -n '^dep-b\.o:' "$work/cc-deps-stdin-order.out" | \ + sed -n '1s/:.*//p') + if [ -n "$stdin_a" ] && [ -n "$stdin_mid" ] && [ -n "$stdin_b" ] && \ + [ "$stdin_a" -lt "$stdin_mid" ] && \ + [ "$stdin_mid" -lt "$stdin_b" ]; then + ok "cc-deps-stdin-input-order" + else + not_ok "cc-deps-stdin-input-order" "$work/cc-deps-stdin-order.out" + fi +else + not_ok "cc-deps-stdin-input-order" "$work/cc-deps-stdin-order.err" +fi + +run_ok "cc-deps-mf-multi" "$KIT" cc -M -I"$work" \ + -MF "$work/all-deps.mk" "$work/dep-a.c" "$work/dep-b.c" +contains "cc-deps-mf-first" "$work/all-deps.mk" "dep-a.o:" +contains "cc-deps-mf-second" "$work/all-deps.mk" "dep-b.o:" +if [ ! -s "$work/cc-deps-mf-multi.out" ]; then + ok "cc-deps-mf-no-stdout" +else + not_ok "cc-deps-mf-no-stdout" "$work/cc-deps-mf-multi.out" +fi + +# Explicit -MF is committed only after every source succeeds. +printf 'keep-existing-dependency-file\n' > "$work/transaction.mk" +run_fail "cc-deps-mf-transaction-failure" "$KIT" cc -M -I"$work" \ + -MF "$work/transaction.mk" "$work/dep-a.c" "$work/missing-dep.c" +contains "cc-deps-mf-transaction-preserved" "$work/transaction.mk" \ + "keep-existing-dependency-file" + +run_ok "cc-deps-target-spellings" "$KIT" cc -MM -I"$work" \ + -MT raw-target -MT second-raw -MQ 'quoted target#$\\name' \ + "$work/dep-a.c" "$work/dep-b.c" +contains "cc-deps-repeated-mt" "$work/cc-deps-target-spellings.out" \ + "raw-target second-raw" +contains "cc-deps-mq-escaping" "$work/cc-deps-target-spellings.out" \ + 'quoted\ target\#$$\\\\name' + +mkdir -p "$work/deps-compile" +if (cd "$work/deps-compile" && "$KIT" cc -I"$work" -c -MD \ + "$work/dep-a.c" "$work/dep-b.c") \ + > "$work/cc-deps-md-compile.out" \ + 2> "$work/cc-deps-md-compile.err" && + [ -s "$work/deps-compile/dep-a.o" ] && + [ -s "$work/deps-compile/dep-b.o" ] && + [ -s "$work/deps-compile/dep-a.d" ] && + [ -s "$work/deps-compile/dep-b.d" ]; then + ok "cc-deps-md-compile-multi" +else + not_ok "cc-deps-md-compile-multi" "$work/cc-deps-md-compile.err" +fi + +mkdir -p "$work/deps-compile-mf" +if (cd "$work/deps-compile-mf" && "$KIT" cc -I"$work" -c -MMD \ + -MF "$work/compile-deps.mk" "$work/dep-a.c" "$work/dep-b.c") \ + > "$work/cc-deps-mmd-compile-mf.out" \ + 2> "$work/cc-deps-mmd-compile-mf.err"; then + ok "cc-deps-mmd-compile-mf" +else + not_ok "cc-deps-mmd-compile-mf" "$work/cc-deps-mmd-compile-mf.err" +fi +contains "cc-deps-mmd-compile-mf-first" "$work/compile-deps.mk" "dep-a.o:" +contains "cc-deps-mmd-compile-mf-second" "$work/compile-deps.mk" "dep-b.o:" + +mkdir -p "$work/deps-link" +if (cd "$work/deps-link" && "$KIT" cc -I"$work" -MD \ + "$work/dep-main.c" "$work/dep-a.c" "$work/dep-b.c" -o dep-app) \ + > "$work/cc-deps-md-link.out" 2> "$work/cc-deps-md-link.err" && + [ -x "$work/deps-link/dep-app" ] && + [ -s "$work/deps-link/dep-main.d" ] && + [ -s "$work/deps-link/dep-a.d" ] && + [ -s "$work/deps-link/dep-b.d" ]; then + ok "cc-deps-md-compile-link" +else + not_ok "cc-deps-md-compile-link" "$work/cc-deps-md-link.err" +fi + # ---- ld -r partial link: output mode + relinkability ---- cat > "$work/partial-main.c" <<'SRC' int foo(void);