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:
| M | driver/cmd/cc.c | | | 737 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
| M | test/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);