Commit 503ba7b27c
Changed files (8)
src/config.zig.in
@@ -1,3 +1,5 @@
+pub const have_llvm = true;
pub const version: []const u8 = "@ZIG_VERSION@";
pub const log_scopes: []const []const u8 = &[_][]const u8{};
+pub const zir_dumps: []const []const u8 = &[_][]const u8{};
pub const enable_tracy = false;
src/main.cpp
@@ -404,7 +404,6 @@ static int main0(int argc, char **argv) {
ZigList<const char *> framework_dirs = {0};
ZigList<const char *> frameworks = {0};
bool have_libc = false;
- bool have_libcpp = false;
const char *target_string = nullptr;
bool rdynamic = false;
const char *linker_script = nullptr;
@@ -446,18 +445,8 @@ static int main0(int argc, char **argv) {
bool function_sections = false;
const char *mcpu = nullptr;
CodeModel code_model = CodeModelDefault;
- const char *override_soname = nullptr;
- bool only_pp_or_asm = false;
- bool ensure_libc_on_non_freestanding = false;
- bool ensure_libcpp_on_non_freestanding = false;
- bool disable_c_depfile = false;
bool want_native_include_dirs = false;
- Buf *linker_optimization = nullptr;
- OptionalBool linker_gc_sections = OptionalBoolNull;
- OptionalBool linker_allow_shlib_undefined = OptionalBoolNull;
OptionalBool linker_bind_global_refs_locally = OptionalBoolNull;
- bool linker_z_nodelete = false;
- bool linker_z_defs = false;
size_t stack_size_override = 0;
ZigList<const char *> llvm_argv = {0};
@@ -585,355 +574,10 @@ static int main0(int argc, char **argv) {
return stage2_fmt(argc, argv);
} else if (argc >= 2 && strcmp(argv[1], "env") == 0) {
return stage2_env(argc, argv);
- } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) {
- emit_h = false;
- strip = true;
- ensure_libc_on_non_freestanding = true;
- ensure_libcpp_on_non_freestanding = (strcmp(argv[1], "c++") == 0);
- want_native_include_dirs = true;
-
- bool c_arg = false;
- Stage2ClangArgIterator it;
- stage2_clang_arg_iterator(&it, argc, argv);
- bool is_shared_lib = false;
- ZigList<Buf *> linker_args = {};
- while (it.has_next) {
- if ((err = stage2_clang_arg_next(&it))) {
- fprintf(stderr, "unable to parse command line parameters: %s\n", err_str(err));
- return EXIT_FAILURE;
- }
- switch (it.kind) {
- case Stage2ClangArgTarget: // example: -target riscv64-linux-unknown
- target_string = it.only_arg;
- break;
- case Stage2ClangArgO: // -o
- emit_bin_override_path = it.only_arg;
- enable_cache = CacheOptOn;
- break;
- case Stage2ClangArgC: // -c
- c_arg = true;
- break;
- case Stage2ClangArgOther:
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- break;
- case Stage2ClangArgPositional: {
- FileExt file_ext = classify_file_ext(it.only_arg, strlen(it.only_arg));
- switch (file_ext) {
- case FileExtAsm:
- case FileExtC:
- case FileExtCpp:
- case FileExtLLVMIr:
- case FileExtLLVMBitCode:
- case FileExtHeader: {
- CFile *c_file = heap::c_allocator.create<CFile>();
- c_file->source_path = it.only_arg;
- c_source_files.append(c_file);
- break;
- }
- case FileExtUnknown:
- objects.append(it.only_arg);
- break;
- }
- break;
- }
- case Stage2ClangArgL: // -l
- if (strcmp(it.only_arg, "c") == 0) {
- have_libc = true;
- link_libs.append("c");
- } else if (strcmp(it.only_arg, "c++") == 0 ||
- strcmp(it.only_arg, "stdc++") == 0)
- {
- have_libcpp = true;
- link_libs.append("c++");
- } else {
- link_libs.append(it.only_arg);
- }
- break;
- case Stage2ClangArgIgnore:
- break;
- case Stage2ClangArgDriverPunt:
- // Never mind what we're doing, just pass the args directly. For example --help.
- return ZigClang_main(argc, argv);
- case Stage2ClangArgPIC:
- want_pic = WantPICEnabled;
- break;
- case Stage2ClangArgNoPIC:
- want_pic = WantPICDisabled;
- break;
- case Stage2ClangArgNoStdLib:
- ensure_libc_on_non_freestanding = false;
- break;
- case Stage2ClangArgNoStdLibCpp:
- ensure_libcpp_on_non_freestanding = false;
- break;
- case Stage2ClangArgShared:
- is_dynamic = true;
- is_shared_lib = true;
- break;
- case Stage2ClangArgRDynamic:
- rdynamic = true;
- break;
- case Stage2ClangArgWL: {
- const char *arg = it.only_arg;
- for (;;) {
- size_t pos = 0;
- while (arg[pos] != ',' && arg[pos] != 0) pos += 1;
- linker_args.append(buf_create_from_mem(arg, pos));
- if (arg[pos] == 0) break;
- arg += pos + 1;
- }
- break;
- }
- case Stage2ClangArgPreprocessOrAsm:
- // this handles both -E and -S
- only_pp_or_asm = true;
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- break;
- case Stage2ClangArgOptimize:
- // alright what release mode do they want?
- if (strcmp(it.only_arg, "Os") == 0) {
- build_mode = BuildModeSmallRelease;
- } else if (strcmp(it.only_arg, "O2") == 0 ||
- strcmp(it.only_arg, "O3") == 0 ||
- strcmp(it.only_arg, "O4") == 0)
- {
- build_mode = BuildModeFastRelease;
- } else if (strcmp(it.only_arg, "Og") == 0 ||
- strcmp(it.only_arg, "O0") == 0)
- {
- build_mode = BuildModeDebug;
- } else {
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- }
- break;
- case Stage2ClangArgDebug:
- strip = false;
- if (strcmp(it.only_arg, "-g") == 0) {
- // we handled with strip = false above
- } else {
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- }
- break;
- case Stage2ClangArgSanitize:
- if (strcmp(it.only_arg, "undefined") == 0) {
- want_sanitize_c = WantCSanitizeEnabled;
- } else {
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- }
- break;
- case Stage2ClangArgLinkerScript:
- linker_script = it.only_arg;
- break;
- case Stage2ClangArgVerboseCmds:
- verbose_cc = true;
- verbose_link = true;
- break;
- case Stage2ClangArgForLinker:
- linker_args.append(buf_create_from_str(it.only_arg));
- break;
- case Stage2ClangArgLinkerInputZ:
- linker_args.append(buf_create_from_str("-z"));
- linker_args.append(buf_create_from_str(it.only_arg));
- break;
- case Stage2ClangArgLibDir:
- lib_dirs.append(it.only_arg);
- break;
- case Stage2ClangArgMCpu:
- mcpu = it.only_arg;
- break;
- case Stage2ClangArgDepFile:
- disable_c_depfile = true;
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- break;
- case Stage2ClangArgFrameworkDir:
- framework_dirs.append(it.only_arg);
- break;
- case Stage2ClangArgFramework:
- frameworks.append(it.only_arg);
- break;
- case Stage2ClangArgNoStdLibInc:
- want_native_include_dirs = false;
- break;
- }
- }
- // Parse linker args
- for (size_t i = 0; i < linker_args.length; i += 1) {
- Buf *arg = linker_args.at(i);
- if (buf_eql_str(arg, "-soname")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- Buf *soname_buf = linker_args.at(i);
- override_soname = buf_ptr(soname_buf);
- // use it as --name
- // example: libsoundio.so.2
- size_t prefix = 0;
- if (buf_starts_with_str(soname_buf, "lib")) {
- prefix = 3;
- }
- size_t end = buf_len(soname_buf);
- if (buf_ends_with_str(soname_buf, ".so")) {
- end -= 3;
- } else {
- bool found_digit = false;
- while (end > 0 && isdigit(buf_ptr(soname_buf)[end - 1])) {
- found_digit = true;
- end -= 1;
- }
- if (found_digit && end > 0 && buf_ptr(soname_buf)[end - 1] == '.') {
- end -= 1;
- } else {
- end = buf_len(soname_buf);
- }
- if (buf_ends_with_str(buf_slice(soname_buf, prefix, end), ".so")) {
- end -= 3;
- }
- }
- out_name = buf_ptr(buf_slice(soname_buf, prefix, end));
- } else if (buf_eql_str(arg, "-rpath")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- Buf *rpath = linker_args.at(i);
- rpath_list.append(buf_ptr(rpath));
- } else if (buf_eql_str(arg, "-I") ||
- buf_eql_str(arg, "--dynamic-linker") ||
- buf_eql_str(arg, "-dynamic-linker"))
- {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- dynamic_linker = buf_ptr(linker_args.at(i));
- } else if (buf_eql_str(arg, "-E") ||
- buf_eql_str(arg, "--export-dynamic") ||
- buf_eql_str(arg, "-export-dynamic"))
- {
- rdynamic = true;
- } else if (buf_eql_str(arg, "--version-script")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- version_script = linker_args.at(i);
- } else if (buf_starts_with_str(arg, "-O")) {
- linker_optimization = arg;
- } else if (buf_eql_str(arg, "--gc-sections")) {
- linker_gc_sections = OptionalBoolTrue;
- } else if (buf_eql_str(arg, "--no-gc-sections")) {
- linker_gc_sections = OptionalBoolFalse;
- } else if (buf_eql_str(arg, "--allow-shlib-undefined") ||
- buf_eql_str(arg, "-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = OptionalBoolTrue;
- } else if (buf_eql_str(arg, "--no-allow-shlib-undefined") ||
- buf_eql_str(arg, "-no-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = OptionalBoolFalse;
- } else if (buf_eql_str(arg, "-Bsymbolic")) {
- linker_bind_global_refs_locally = OptionalBoolTrue;
- } else if (buf_eql_str(arg, "-z")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- Buf *z_arg = linker_args.at(i);
- if (buf_eql_str(z_arg, "nodelete")) {
- linker_z_nodelete = true;
- } else if (buf_eql_str(z_arg, "defs")) {
- linker_z_defs = true;
- } else {
- fprintf(stderr, "warning: unsupported linker arg: -z %s\n", buf_ptr(z_arg));
- }
- } else if (buf_eql_str(arg, "--major-image-version")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- ver_major = atoi(buf_ptr(linker_args.at(i)));
- } else if (buf_eql_str(arg, "--minor-image-version")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- ver_minor = atoi(buf_ptr(linker_args.at(i)));
- } else if (buf_eql_str(arg, "--stack")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- stack_size_override = atoi(buf_ptr(linker_args.at(i)));
- } else {
- fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg));
- }
- }
-
- if (want_sanitize_c == WantCSanitizeEnabled && build_mode == BuildModeFastRelease) {
- build_mode = BuildModeSafeRelease;
- }
-
- if (only_pp_or_asm) {
- cmd = CmdBuild;
- out_type = OutTypeObj;
- emit_bin = false;
- // Transfer "objects" into c_source_files
- for (size_t i = 0; i < objects.length; i += 1) {
- CFile *c_file = heap::c_allocator.create<CFile>();
- c_file->source_path = objects.at(i);
- c_source_files.append(c_file);
- }
- for (size_t i = 0; i < c_source_files.length; i += 1) {
- Buf *src_path;
- if (emit_bin_override_path != nullptr) {
- src_path = buf_create_from_str(emit_bin_override_path);
- } else {
- src_path = buf_create_from_str(c_source_files.at(i)->source_path);
- }
- Buf basename = BUF_INIT;
- os_path_split(src_path, nullptr, &basename);
- c_source_files.at(i)->preprocessor_only_basename = buf_ptr(&basename);
- }
- } else if (!c_arg) {
- cmd = CmdBuild;
- if (is_shared_lib) {
- out_type = OutTypeLib;
- } else {
- out_type = OutTypeExe;
- }
- if (emit_bin_override_path == nullptr) {
- emit_bin_override_path = "a.out";
- enable_cache = CacheOptOn;
- }
- } else {
- cmd = CmdBuild;
- out_type = OutTypeObj;
- }
- if (c_source_files.length == 0 && objects.length == 0) {
- // For example `zig cc` and no args should print the "no input files" message.
- return ZigClang_main(argc, argv);
- }
+ } else if (argc >= 2 && strcmp(argv[1], "cc") == 0) {
+ return stage2_cc(argc, argv, false);
+ } else if (argc >= 2 && strcmp(argv[1], "c++") == 0) {
+ return stage2_cc(argc, argv, true);
} else for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
@@ -1038,7 +682,6 @@ static int main0(int argc, char **argv) {
have_libc = true;
link_libs.append("c");
} else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) {
- have_libcpp = true;
link_libs.append("c++");
} else {
link_libs.append(l);
@@ -1185,7 +828,6 @@ static int main0(int argc, char **argv) {
have_libc = true;
link_libs.append("c");
} else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) {
- have_libcpp = true;
link_libs.append("c++");
} else {
link_libs.append(argv[i]);
@@ -1351,15 +993,6 @@ static int main0(int argc, char **argv) {
return print_error_usage(arg0);
}
- if (!have_libc && ensure_libc_on_non_freestanding && target.os != OsFreestanding) {
- have_libc = true;
- link_libs.append("c");
- }
- if (!have_libcpp && ensure_libcpp_on_non_freestanding && target.os != OsFreestanding) {
- have_libcpp = true;
- link_libs.append("c++");
- }
-
Buf zig_triple_buf = BUF_INIT;
target_triple_zig(&zig_triple_buf, &target);
@@ -1616,20 +1249,10 @@ static int main0(int argc, char **argv) {
g->system_linker_hack = system_linker_hack;
g->function_sections = function_sections;
g->code_model = code_model;
- g->disable_c_depfile = disable_c_depfile;
- g->linker_optimization = linker_optimization;
- g->linker_gc_sections = linker_gc_sections;
- g->linker_allow_shlib_undefined = linker_allow_shlib_undefined;
g->linker_bind_global_refs_locally = linker_bind_global_refs_locally;
- g->linker_z_nodelete = linker_z_nodelete;
- g->linker_z_defs = linker_z_defs;
g->stack_size_override = stack_size_override;
- if (override_soname) {
- g->override_soname = buf_create_from_str(override_soname);
- }
-
for (size_t i = 0; i < lib_dirs.length; i += 1) {
codegen_add_lib_dir(g, lib_dirs.at(i));
}
@@ -1713,37 +1336,12 @@ static int main0(int argc, char **argv) {
buf_replace(g->output_dir, '/', '\\');
#endif
Buf *dest_path = buf_create_from_str(emit_bin_override_path);
- Buf *source_path;
- if (only_pp_or_asm) {
- source_path = buf_alloc();
- Buf *pp_only_basename = buf_create_from_str(
- c_source_files.at(0)->preprocessor_only_basename);
- os_path_join(g->output_dir, pp_only_basename, source_path);
-
- } else {
- source_path = &g->bin_file_output_path;
- }
+ Buf *source_path = &g->bin_file_output_path;
if ((err = os_update_file(source_path, dest_path))) {
fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(source_path),
buf_ptr(dest_path), err_str(err));
return main_exit(root_progress_node, EXIT_FAILURE);
}
- } else if (only_pp_or_asm) {
-#if defined(ZIG_OS_WINDOWS)
- buf_replace(g->c_artifact_dir, '/', '\\');
-#endif
- // dump the preprocessed output to stdout
- for (size_t i = 0; i < c_source_files.length; i += 1) {
- Buf *source_path = buf_alloc();
- Buf *pp_only_basename = buf_create_from_str(
- c_source_files.at(i)->preprocessor_only_basename);
- os_path_join(g->c_artifact_dir, pp_only_basename, source_path);
- if ((err = os_dump_file(source_path, stdout))) {
- fprintf(stderr, "unable to read %s: %s\n", buf_ptr(source_path),
- err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- }
} else if (g->enable_cache) {
#if defined(ZIG_OS_WINDOWS)
buf_replace(&g->bin_file_output_path, '/', '\\');
src/stage2.cpp
@@ -32,6 +32,11 @@ int stage2_env(int argc, char** argv) {
stage2_panic(msg, strlen(msg));
}
+int stage2_cc(int argc, char** argv, bool is_cpp) {
+ const char *msg = "stage0 called stage2_cc";
+ stage2_panic(msg, strlen(msg));
+}
+
void stage2_attach_segfault_handler(void) { }
void stage2_panic(const char *ptr, size_t len) {
@@ -316,16 +321,4 @@ enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) {
return ErrorNone;
}
-void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
- size_t argc, char **argv)
-{
- const char *msg = "stage0 called stage2_clang_arg_iterator";
- stage2_panic(msg, strlen(msg));
-}
-
-enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) {
- const char *msg = "stage0 called stage2_clang_arg_next";
- stage2_panic(msg, strlen(msg));
-}
-
const bool stage2_is_zig0 = true;
src/stage2.h
@@ -144,6 +144,9 @@ ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
// ABI warning
ZIG_EXTERN_C int stage2_env(int argc, char **argv);
+// ABI warning
+ZIG_EXTERN_C int stage2_cc(int argc, char **argv, bool is_cpp);
+
// ABI warning
ZIG_EXTERN_C void stage2_attach_segfault_handler(void);
@@ -328,60 +331,6 @@ struct Stage2NativePaths {
// ABI warning
ZIG_EXTERN_C enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths);
-// ABI warning
-enum Stage2ClangArg {
- Stage2ClangArgTarget,
- Stage2ClangArgO,
- Stage2ClangArgC,
- Stage2ClangArgOther,
- Stage2ClangArgPositional,
- Stage2ClangArgL,
- Stage2ClangArgIgnore,
- Stage2ClangArgDriverPunt,
- Stage2ClangArgPIC,
- Stage2ClangArgNoPIC,
- Stage2ClangArgNoStdLib,
- Stage2ClangArgNoStdLibCpp,
- Stage2ClangArgShared,
- Stage2ClangArgRDynamic,
- Stage2ClangArgWL,
- Stage2ClangArgPreprocessOrAsm,
- Stage2ClangArgOptimize,
- Stage2ClangArgDebug,
- Stage2ClangArgSanitize,
- Stage2ClangArgLinkerScript,
- Stage2ClangArgVerboseCmds,
- Stage2ClangArgForLinker,
- Stage2ClangArgLinkerInputZ,
- Stage2ClangArgLibDir,
- Stage2ClangArgMCpu,
- Stage2ClangArgDepFile,
- Stage2ClangArgFrameworkDir,
- Stage2ClangArgFramework,
- Stage2ClangArgNoStdLibInc,
-};
-
-// ABI warning
-struct Stage2ClangArgIterator {
- bool has_next;
- enum Stage2ClangArg kind;
- const char *only_arg;
- const char *second_arg;
- const char **other_args_ptr;
- size_t other_args_len;
- const char **argv_ptr;
- size_t argv_len;
- size_t next_index;
- size_t root_args;
-};
-
-// ABI warning
-ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
- size_t argc, char **argv);
-
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it);
-
// ABI warning
ZIG_EXTERN_C const bool stage2_is_zig0;
src-self-hosted/clang_options.zig
@@ -7,9 +7,7 @@ pub const CliArg = struct {
name: []const u8,
syntax: Syntax,
- /// TODO we're going to want to change this when we start shipping self-hosted because this causes
- /// all the functions in stage2.zig to get exported.
- zig_equivalent: @import("stage2.zig").ClangArgIterator.ZigEquivalent,
+ zig_equivalent: @import("main.zig").ClangArgIterator.ZigEquivalent,
/// Prefixed by "-"
pd1: bool = false,
src-self-hosted/main.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const assert = std.debug.assert;
const io = std.io;
const fs = std.fs;
const mem = std.mem;
@@ -11,6 +12,13 @@ const link = @import("link.zig");
const Package = @import("Package.zig");
const zir = @import("zir.zig");
const build_options = @import("build_options");
+const warn = std.log.warn;
+const info = std.log.info;
+
+fn fatal(comptime format: []const u8, args: anytype) noreturn {
+ std.log.emerg(format, args);
+ process.exit(1);
+}
pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
@@ -28,9 +36,11 @@ const usage =
\\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly
+ \\ cc Use Zig as a drop-in C compiler
+ \\ c++ Use Zig as a drop-in C++ compiler
+ \\ env Print lib path, std path, compiler id and version
\\ fmt [source] Parse file and render in canonical zig format
\\ targets List available compilation targets
- \\ env Print lib path, std path, compiler id and version
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\
@@ -84,11 +94,19 @@ pub fn main() !void {
const cmd = args[1];
const cmd_args = args[2..];
if (mem.eql(u8, cmd, "build-exe")) {
- return buildOutputType(gpa, arena, cmd_args, .Exe);
+ return buildOutputType(gpa, arena, args, .{ .build = .Exe });
} else if (mem.eql(u8, cmd, "build-lib")) {
- return buildOutputType(gpa, arena, cmd_args, .Lib);
+ return buildOutputType(gpa, arena, args, .{ .build = .Lib });
} else if (mem.eql(u8, cmd, "build-obj")) {
- return buildOutputType(gpa, arena, cmd_args, .Obj);
+ return buildOutputType(gpa, arena, args, .{ .build = .Obj });
+ } else if (mem.eql(u8, cmd, "cc")) {
+ return buildOutputType(gpa, arena, args, .cc);
+ } else if (mem.eql(u8, cmd, "c++")) {
+ return buildOutputType(gpa, arena, args, .cpp);
+ } else if (mem.eql(u8, cmd, "clang") or
+ mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
+ {
+ return punt_to_clang(arena, args);
} else if (mem.eql(u8, cmd, "fmt")) {
return cmdFmt(gpa, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) {
@@ -147,6 +165,8 @@ const usage_build_generic =
\\ ReleaseFast Optimizations on, safety off
\\ ReleaseSafe Optimizations on, safety on
\\ ReleaseSmall Optimize for small binary, safety off
+ \\ -fPIC Force-enable Position Independent Code
+ \\ -fno-PIC Force-disable Position Independent Code
\\ --dynamic Force output to be dynamically linked
\\ --strip Exclude debug symbols
\\ -ofmt=[mode] Override target object format
@@ -158,11 +178,19 @@ const usage_build_generic =
\\ macho (planned) macOS relocatables
\\ hex (planned) Intel IHEX
\\ raw (planned) Dump machine code directly
+ \\ -dirafter [dir] Add directory to AFTER include search path
+ \\ -isystem [dir] Add directory to SYSTEM include search path
+ \\ -I[dir] Add directory to include search path
+ \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
\\
\\Link Options:
\\ -l[lib], --library [lib] Link against system library
+ \\ -L[d], --library-directory [d] Add a directory to the library search path
+ \\ -T[script] Use a custom linker script
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
\\ --version [ver] Dynamic library semver
+ \\ -rdynamic Add all symbols to the dynamic symbol table
+ \\ -rpath [path] Add directory to the runtime library search path
\\
\\Debug Options (Zig Compiler Development):
\\ -ftime-report Print timing diagnostics
@@ -181,11 +209,15 @@ const Emit = union(enum) {
yes: []const u8,
};
-fn buildOutputType(
+pub fn buildOutputType(
gpa: *Allocator,
arena: *Allocator,
- args: []const []const u8,
- output_mode: std.builtin.OutputMode,
+ all_args: []const []const u8,
+ arg_mode: union(enum) {
+ build: std.builtin.OutputMode,
+ cc,
+ cpp,
+ },
) !void {
var color: Color = .Auto;
var build_mode: std.builtin.Mode = .Debug;
@@ -194,6 +226,7 @@ fn buildOutputType(
var root_src_file: ?[]const u8 = null;
var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
var strip = false;
+ var emit_h = true;
var watch = false;
var debug_tokenize = false;
var debug_ast_tree = false;
@@ -201,6 +234,7 @@ fn buildOutputType(
var debug_link = false;
var debug_ir = false;
var debug_codegen = false;
+ var debug_cc = false;
var time_report = false;
var emit_bin: Emit = .yes_default_path;
var emit_zir: Emit = .no;
@@ -208,11 +242,57 @@ fn buildOutputType(
var target_mcpu: ?[]const u8 = null;
var target_dynamic_linker: ?[]const u8 = null;
var target_ofmt: ?[]const u8 = null;
+ var output_mode: std.builtin.OutputMode = undefined;
+ var ensure_libc_on_non_freestanding = false;
+ var ensure_libcpp_on_non_freestanding = false;
+ var have_libc = false;
+ var have_libcpp = false;
+ var want_native_include_dirs = false;
+ var enable_cache: ?bool = null;
+ var want_pic: ?bool = null;
+ var want_sanitize_c: ?bool = null;
+ var rdynamic: bool = false;
+ var only_pp_or_asm = false;
+ var linker_script: ?[]const u8 = null;
+ var version_script: ?[]const u8 = null;
+ var disable_c_depfile = false;
+ var override_soname: ?[]const u8 = null;
+ var linker_optimization: ?[]const u8 = null;
+ var linker_gc_sections: ?bool = null;
+ var linker_allow_shlib_undefined: ?bool = null;
+ var linker_bind_global_refs_locally: ?bool = null;
+ var linker_z_nodelete = false;
+ var linker_z_defs = false;
+ var stack_size_override: u64 = 0;
var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
- {
+ var clang_argv = std.ArrayList([]const u8).init(gpa);
+ defer clang_argv.deinit();
+
+ var lib_dirs = std.ArrayList([]const u8).init(gpa);
+ defer lib_dirs.deinit();
+
+ var rpath_list = std.ArrayList([]const u8).init(gpa);
+ defer rpath_list.deinit();
+
+ var c_source_files = std.ArrayList([]const u8).init(gpa);
+ defer c_source_files.deinit();
+
+ var link_objects = std.ArrayList([]const u8).init(gpa);
+ defer link_objects.deinit();
+
+ var framework_dirs = std.ArrayList([]const u8).init(gpa);
+ defer framework_dirs.deinit();
+
+ var frameworks = std.ArrayList([]const u8).init(gpa);
+ defer frameworks.deinit();
+
+ if (arg_mode == .build) {
+ output_mode = arg_mode.build;
+
+ const args = all_args[2..];
var i: usize = 0;
while (i < args.len) : (i += 1) {
const arg = args[i];
@@ -222,8 +302,7 @@ fn buildOutputType(
process.exit(0);
} else if (mem.eql(u8, arg, "--color")) {
if (i + 1 >= args.len) {
- std.debug.print("expected [auto|on|off] after --color\n", .{});
- process.exit(1);
+ fatal("expected [auto|on|off] after --color", .{});
}
i += 1;
const next_arg = args[i];
@@ -234,13 +313,11 @@ fn buildOutputType(
} else if (mem.eql(u8, next_arg, "off")) {
color = .Off;
} else {
- std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
- process.exit(1);
+ fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
}
} else if (mem.eql(u8, arg, "--mode")) {
if (i + 1 >= args.len) {
- std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
- process.exit(1);
+ fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});
}
i += 1;
const next_arg = args[i];
@@ -253,44 +330,66 @@ fn buildOutputType(
} else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
build_mode = .ReleaseSmall;
} else {
- std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
- process.exit(1);
+ fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg});
}
+ } else if (mem.eql(u8, arg, "--stack")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
} else if (mem.eql(u8, arg, "--name")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --name\n", .{});
- process.exit(1);
- }
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
provided_name = args[i];
- } else if (mem.eql(u8, arg, "--library")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --library\n", .{});
- process.exit(1);
- }
+ } else if (mem.eql(u8, arg, "-rpath")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try rpath_list.append(args[i]);
+ } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try lib_dirs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-T")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ linker_script = args[i];
+ } else if (mem.eql(u8, arg, "--version-script")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ version_script = args[i];
+ } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
i += 1;
try system_libs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-D") or
+ mem.eql(u8, arg, "-isystem") or
+ mem.eql(u8, arg, "-I") or
+ mem.eql(u8, arg, "-dirafter"))
+ {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try clang_argv.append(arg);
+ try clang_argv.append(args[i]);
} else if (mem.eql(u8, arg, "--version")) {
if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --version\n", .{});
- process.exit(1);
+ fatal("expected parameter after --version", .{});
}
i += 1;
version = std.builtin.Version.parse(args[i]) catch |err| {
- std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
- process.exit(1);
+ fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
};
} else if (mem.eql(u8, arg, "-target")) {
if (i + 1 >= args.len) {
- std.debug.print("expected parameter after -target\n", .{});
- process.exit(1);
+ fatal("expected parameter after -target", .{});
}
i += 1;
target_arch_os_abi = args[i];
} else if (mem.eql(u8, arg, "-mcpu")) {
if (i + 1 >= args.len) {
- std.debug.print("expected parameter after -mcpu\n", .{});
- process.exit(1);
+ fatal("expected parameter after -mcpu", .{});
}
i += 1;
target_mcpu = args[i];
@@ -300,8 +399,7 @@ fn buildOutputType(
target_mcpu = arg["-mcpu=".len..];
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --dynamic-linker\n", .{});
- process.exit(1);
+ fatal("expected parameter after --dynamic-linker", .{});
}
i += 1;
target_dynamic_linker = args[i];
@@ -309,6 +407,12 @@ fn buildOutputType(
watch = true;
} else if (mem.eql(u8, arg, "-ftime-report")) {
time_report = true;
+ } else if (mem.eql(u8, arg, "-fPIC")) {
+ want_pic = true;
+ } else if (mem.eql(u8, arg, "-fno-PIC")) {
+ want_pic = false;
+ } else if (mem.eql(u8, arg, "-rdynamic")) {
+ rdynamic = true;
} else if (mem.eql(u8, arg, "-femit-bin")) {
emit_bin = .yes_default_path;
} else if (mem.startsWith(u8, arg, "-femit-bin=")) {
@@ -327,6 +431,8 @@ fn buildOutputType(
link_mode = .Static;
} else if (mem.eql(u8, arg, "--strip")) {
strip = true;
+ } else if (mem.eql(u8, arg, "-Bsymbolic")) {
+ linker_bind_global_refs_locally = true;
} else if (mem.eql(u8, arg, "--debug-tokenize")) {
debug_tokenize = true;
} else if (mem.eql(u8, arg, "--debug-ast-tree")) {
@@ -339,44 +445,321 @@ fn buildOutputType(
debug_ir = true;
} else if (mem.eql(u8, arg, "--debug-codegen")) {
debug_codegen = true;
+ } else if (mem.eql(u8, arg, "--debug-cc")) {
+ debug_cc = true;
+ } else if (mem.startsWith(u8, arg, "-T")) {
+ linker_script = arg[2..];
+ } else if (mem.startsWith(u8, arg, "-L")) {
+ try lib_dirs.append(arg[2..]);
} else if (mem.startsWith(u8, arg, "-l")) {
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
try system_libs.append(arg[2..]);
+ } else if (mem.startsWith(u8, arg, "-D") or
+ mem.startsWith(u8, arg, "-I"))
+ {
+ try clang_argv.append(arg);
} else {
- std.debug.print("unrecognized parameter: '{}'\n", .{arg});
- process.exit(1);
+ fatal("unrecognized parameter: '{}'", .{arg});
}
- } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
- std.debug.print("assembly files not supported yet\n", .{});
- process.exit(1);
} else if (mem.endsWith(u8, arg, ".o") or
mem.endsWith(u8, arg, ".obj") or
mem.endsWith(u8, arg, ".a") or
mem.endsWith(u8, arg, ".lib"))
{
- std.debug.print("object files and static libraries not supported yet\n", .{});
- process.exit(1);
- } else if (mem.endsWith(u8, arg, ".c") or
- mem.endsWith(u8, arg, ".cpp"))
- {
- std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet\n", .{});
- process.exit(1);
+ try link_objects.append(arg);
+ } else if (hasAsmExt(arg) or hasCExt(arg) or hasCppExt(arg)) {
+ try c_source_files.append(arg);
} else if (mem.endsWith(u8, arg, ".so") or
mem.endsWith(u8, arg, ".dylib") or
mem.endsWith(u8, arg, ".dll"))
{
- std.debug.print("linking against dynamic libraries not yet supported\n", .{});
- process.exit(1);
+ fatal("linking against dynamic libraries not yet supported", .{});
} else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
if (root_src_file) |other| {
- std.debug.print("found another zig file '{}' after root source file '{}'\n", .{ arg, other });
- process.exit(1);
+ fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
} else {
root_src_file = arg;
}
} else {
- std.debug.print("unrecognized file extension of parameter '{}'\n", .{arg});
+ fatal("unrecognized file extension of parameter '{}'", .{arg});
+ }
+ }
+ } else {
+ if (!build_options.have_llvm)
+ fatal("`zig cc` and `zig c++` unavailable: compiler not built with LLVM extensions enabled", .{});
+ emit_h = false;
+ strip = true;
+ ensure_libc_on_non_freestanding = true;
+ ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
+ want_native_include_dirs = true;
+
+ var c_arg = false;
+ var is_shared_lib = false;
+ var linker_args = std.ArrayList([]const u8).init(arena);
+ var it = ClangArgIterator.init(arena, all_args);
+ while (it.has_next) {
+ it.next() catch |err| {
+ fatal("unable to parse command line parameters: {}", .{@errorName(err)});
+ };
+ switch (it.zig_equivalent) {
+ .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
+ .o => {
+ // -o
+ emit_bin = .{ .yes = it.only_arg };
+ enable_cache = true;
+ },
+ .c => c_arg = true, // -c
+ .other => {
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .positional => {
+ const file_ext = classify_file_ext(mem.spanZ(it.only_arg));
+ switch (file_ext) {
+ .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg),
+ .unknown => try link_objects.append(it.only_arg),
+ }
+ },
+ .l => {
+ // -l
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ try system_libs.append(it.only_arg);
+ },
+ .ignore => {},
+ .driver_punt => {
+ // Never mind what we're doing, just pass the args directly. For example --help.
+ return punt_to_clang(arena, all_args);
+ },
+ .pic => want_pic = true,
+ .no_pic => want_pic = false,
+ .nostdlib => ensure_libc_on_non_freestanding = false,
+ .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
+ .shared => {
+ link_mode = .Dynamic;
+ is_shared_lib = true;
+ },
+ .rdynamic => rdynamic = true,
+ .wl => {
+ var split_it = mem.split(it.only_arg, ",");
+ @breakpoint(); // TODO the first arg is empty string right? skip past that.
+ while (split_it.next()) |linker_arg| {
+ try linker_args.append(linker_arg);
+ }
+ },
+ .pp_or_asm => {
+ // This handles both -E and -S.
+ only_pp_or_asm = true;
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .optimize => {
+ // Alright, what release mode do they want?
+ if (mem.eql(u8, it.only_arg, "Os")) {
+ build_mode = .ReleaseSmall;
+ } else if (mem.eql(u8, it.only_arg, "O2") or
+ mem.eql(u8, it.only_arg, "O3") or
+ mem.eql(u8, it.only_arg, "O4"))
+ {
+ build_mode = .ReleaseFast;
+ } else if (mem.eql(u8, it.only_arg, "Og") or
+ mem.eql(u8, it.only_arg, "O0"))
+ {
+ build_mode = .Debug;
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .debug => {
+ strip = false;
+ if (mem.eql(u8, it.only_arg, "-g")) {
+ // We handled with strip = false above.
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .sanitize => {
+ if (mem.eql(u8, it.only_arg, "undefined")) {
+ want_sanitize_c = true;
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .linker_script => linker_script = it.only_arg,
+ .verbose_cmds => {
+ debug_cc = true;
+ debug_link = true;
+ },
+ .for_linker => try linker_args.append(it.only_arg),
+ .linker_input_z => {
+ try linker_args.append("-z");
+ try linker_args.append(it.only_arg);
+ },
+ .lib_dir => try lib_dirs.append(it.only_arg),
+ .mcpu => target_mcpu = it.only_arg,
+ .dep_file => {
+ disable_c_depfile = true;
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .framework_dir => try framework_dirs.append(it.only_arg),
+ .framework => try frameworks.append(it.only_arg),
+ .nostdlibinc => want_native_include_dirs = false,
+ }
+ }
+ // Parse linker args.
+ var i: usize = 0;
+ while (i < linker_args.items.len) : (i += 1) {
+ const arg = linker_args.items[i];
+ if (mem.eql(u8, arg, "-soname")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ const soname = linker_args.items[i];
+ override_soname = soname;
+ // Use it as --name.
+ // Example: libsoundio.so.2
+ var prefix: usize = 0;
+ if (mem.startsWith(u8, soname, "lib")) {
+ prefix = 3;
+ }
+ var end: usize = soname.len;
+ if (mem.endsWith(u8, soname, ".so")) {
+ end -= 3;
+ } else {
+ var found_digit = false;
+ while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
+ found_digit = true;
+ end -= 1;
+ }
+ if (found_digit and end > 0 and soname[end - 1] == '.') {
+ end -= 1;
+ } else {
+ end = soname.len;
+ }
+ if (mem.endsWith(u8, soname[prefix..end], ".so")) {
+ end -= 3;
+ }
+ }
+ provided_name = soname[prefix..end];
+ } else if (mem.eql(u8, arg, "-rpath")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ try rpath_list.append(linker_args.items[i]);
+ } else if (mem.eql(u8, arg, "-I") or
+ mem.eql(u8, arg, "--dynamic-linker") or
+ mem.eql(u8, arg, "-dynamic-linker"))
+ {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ target_dynamic_linker = linker_args.items[i];
+ } else if (mem.eql(u8, arg, "-E") or
+ mem.eql(u8, arg, "--export-dynamic") or
+ mem.eql(u8, arg, "-export-dynamic"))
+ {
+ rdynamic = true;
+ } else if (mem.eql(u8, arg, "--version-script")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version_script = linker_args.items[i];
+ } else if (mem.startsWith(u8, arg, "-O")) {
+ linker_optimization = arg;
+ } else if (mem.eql(u8, arg, "--gc-sections")) {
+ linker_gc_sections = true;
+ } else if (mem.eql(u8, arg, "--no-gc-sections")) {
+ linker_gc_sections = false;
+ } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
+ mem.eql(u8, arg, "-allow-shlib-undefined"))
+ {
+ linker_allow_shlib_undefined = true;
+ } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
+ mem.eql(u8, arg, "-no-allow-shlib-undefined"))
+ {
+ linker_allow_shlib_undefined = false;
+ } else if (mem.eql(u8, arg, "-Bsymbolic")) {
+ linker_bind_global_refs_locally = true;
+ } else if (mem.eql(u8, arg, "-z")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ const z_arg = linker_args.items[i];
+ if (mem.eql(u8, z_arg, "nodelete")) {
+ linker_z_nodelete = true;
+ } else if (mem.eql(u8, z_arg, "defs")) {
+ linker_z_defs = true;
+ } else {
+ warn("unsupported linker arg: -z {}", .{z_arg});
+ }
+ } else if (mem.eql(u8, arg, "--major-image-version")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ } else if (mem.eql(u8, arg, "--minor-image-version")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ } else if (mem.eql(u8, arg, "--stack")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ } else {
+ warn("unsupported linker arg: {}", .{arg});
}
}
+
+ if (want_sanitize_c == true and build_mode == .ReleaseFast) {
+ build_mode = .ReleaseSafe;
+ }
+
+ if (only_pp_or_asm) {
+ output_mode = .Obj;
+ fatal("TODO implement using zig cc as a preprocessor", .{});
+ //// Transfer "link_objects" into c_source_files so that all those
+ //// args make it onto the command line.
+ //try c_source_files.appendSlice(link_objects.items);
+ //for (c_source_files.items) |c_source_file| {
+ // const src_path = switch (emit_bin) {
+ // .yes => |p| p,
+ // else => c_source_file.source_path,
+ // };
+ // const basename = std.fs.path.basename(src_path);
+ // c_source_file.preprocessor_only_basename = basename;
+ //}
+ //emit_bin = .no;
+ } else if (!c_arg) {
+ output_mode = if (is_shared_lib) .Lib else .Exe;
+ switch (emit_bin) {
+ .no, .yes_default_path => {
+ emit_bin = .{ .yes = "a.out" };
+ enable_cache = true;
+ },
+ .yes => {},
+ }
+ } else {
+ output_mode = .Obj;
+ }
+ if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
+ // For example `zig cc` and no args should print the "no input files" message.
+ return punt_to_clang(arena, all_args);
+ }
}
const root_name = if (provided_name) |n| n else blk: {
@@ -385,16 +768,10 @@ fn buildOutputType(
var it = mem.split(basename, ".");
break :blk it.next() orelse basename;
} else {
- std.debug.print("--name [name] not provided and unable to infer\n", .{});
- process.exit(1);
+ fatal("--name [name] not provided and unable to infer", .{});
}
};
- if (system_libs.items.len != 0) {
- std.debug.print("linking against system libraries not yet supported\n", .{});
- process.exit(1);
- }
-
var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
const cross_target = std.zig.CrossTarget.parse(.{
.arch_os_abi = target_arch_os_abi,
@@ -429,17 +806,67 @@ fn buildOutputType(
else => |e| return e,
};
- var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
+ const target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
if (target_info.cpu_detection_unimplemented) {
// TODO We want to just use detected_info.target but implementing
// CPU model & feature detection is todo so here we rely on LLVM.
- std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
- process.exit(1);
+ fatal("CPU features detection is not yet available for this system without LLVM extensions", .{});
+ }
+
+ if (target_info.target.os.tag != .freestanding) {
+ if (ensure_libc_on_non_freestanding)
+ have_libc = true;
+ if (ensure_libcpp_on_non_freestanding)
+ have_libcpp = true;
+ }
+
+ // Now that we have target info, we can find out if any of the system libraries
+ // are part of libc or libc++. We remove them from the list and communicate their
+ // existence via flags instead.
+ {
+ var i: usize = 0;
+ while (i < system_libs.items.len) {
+ const lib_name = system_libs.items[i];
+ if (is_libc_lib_name(target_info.target, lib_name)) {
+ have_libc = true;
+ _ = system_libs.orderedRemove(i);
+ continue;
+ }
+ if (is_libcpp_lib_name(target_info.target, lib_name)) {
+ have_libcpp = true;
+ _ = system_libs.orderedRemove(i);
+ continue;
+ }
+ i += 1;
+ }
+ }
+
+ if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) {
+ const paths = std.zig.system.NativePaths.detect(arena) catch |err| {
+ fatal("unable to detect native system paths: {}", .{@errorName(err)});
+ };
+ for (paths.warnings.items) |warning| {
+ warn("{}", .{warning});
+ }
+ try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2);
+ for (paths.include_dirs.items) |include_dir| {
+ clang_argv.appendAssumeCapacity("-isystem");
+ clang_argv.appendAssumeCapacity(include_dir);
+ }
+ for (paths.lib_dirs.items) |lib_dir| {
+ try lib_dirs.append(lib_dir);
+ }
+ for (paths.rpaths.items) |rpath| {
+ try rpath_list.append(rpath);
+ }
+ }
+
+ if (system_libs.items.len != 0) {
+ fatal("linking against system libraries not yet supported", .{});
}
const src_path = root_src_file orelse {
- std.debug.print("expected at least one file argument", .{});
- process.exit(1);
+ fatal("expected at least one file argument", .{});
};
const object_format: ?std.Target.ObjectFormat = blk: {
@@ -461,15 +888,13 @@ fn buildOutputType(
} else if (mem.eql(u8, ofmt, "raw")) {
break :blk .raw;
} else {
- std.debug.print("unsupported object format: {}", .{ofmt});
- process.exit(1);
+ fatal("unsupported object format: {}", .{ofmt});
}
};
const bin_path = switch (emit_bin) {
.no => {
- std.debug.print("-fno-emit-bin not supported yet", .{});
- process.exit(1);
+ fatal("-fno-emit-bin not supported yet", .{});
},
.yes_default_path => if (object_format != null and object_format.? == .c)
try std.fmt.allocPrint(arena, "{}.c", .{root_name})
@@ -515,6 +940,11 @@ fn buildOutputType(
try updateModule(gpa, &module, zir_out_path);
+ if (build_options.have_llvm and only_pp_or_asm) {
+ // this may include dumping the output to stdout
+ fatal("TODO: implement `zig cc` when using it as a preprocessor", .{});
+ }
+
while (watch) {
try stderr.print("๐ฆ ", .{});
if (output_mode == .Exe) {
@@ -562,7 +992,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
});
}
} else {
- std.log.scoped(.compiler).info("Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
+ info("Update completed in {} ms", .{update_nanos / std.time.ns_per_ms});
}
if (zir_out_path) |zop| {
@@ -631,8 +1061,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
process.exit(0);
} else if (mem.eql(u8, arg, "--color")) {
if (i + 1 >= args.len) {
- std.debug.print("expected [auto|on|off] after --color\n", .{});
- process.exit(1);
+ fatal("expected [auto|on|off] after --color", .{});
}
i += 1;
const next_arg = args[i];
@@ -643,16 +1072,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
} else if (mem.eql(u8, next_arg, "off")) {
color = .Off;
} else {
- std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
- process.exit(1);
+ fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
}
} else if (mem.eql(u8, arg, "--stdin")) {
stdin_flag = true;
} else if (mem.eql(u8, arg, "--check")) {
check_flag = true;
} else {
- std.debug.print("unrecognized parameter: '{}'", .{arg});
- process.exit(1);
+ fatal("unrecognized parameter: '{}'", .{arg});
}
} else {
try input_files.append(arg);
@@ -662,8 +1089,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
if (stdin_flag) {
if (input_files.items.len != 0) {
- std.debug.print("cannot use --stdin with positional arguments\n", .{});
- process.exit(1);
+ fatal("cannot use --stdin with positional arguments", .{});
}
const stdin = io.getStdIn().inStream();
@@ -672,8 +1098,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
defer gpa.free(source_code);
const tree = std.zig.parse(gpa, source_code) catch |err| {
- std.debug.print("error parsing stdin: {}\n", .{err});
- process.exit(1);
+ fatal("error parsing stdin: {}", .{err});
};
defer tree.deinit();
@@ -695,8 +1120,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
}
if (input_files.items.len == 0) {
- std.debug.print("expected at least one source file argument\n", .{});
- process.exit(1);
+ fatal("expected at least one source file argument", .{});
}
var fmt = Fmt{
@@ -712,8 +1136,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
for (input_files.span()) |file_path| {
// Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
- std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
- process.exit(1);
+ fatal("unable to open '{}': {}", .{ file_path, err });
};
defer gpa.free(real_path);
@@ -752,7 +1175,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_
fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
else => {
- std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
+ warn("unable to format '{}': {}", .{ file_path, err });
fmt.any_error = true;
return;
},
@@ -783,7 +1206,7 @@ fn fmtPathDir(
try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
} else {
fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
- std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
+ warn("unable to format '{}': {}", .{ full_path, err });
fmt.any_error = true;
return;
};
@@ -841,6 +1264,7 @@ fn fmtPathFile(
if (check_mode) {
const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
if (anything_changed) {
+ // TODO this should output to stdout instead of stderr.
std.debug.print("{}\n", .{file_path});
fmt.any_error = true;
}
@@ -858,6 +1282,7 @@ fn fmtPathFile(
try af.file.writeAll(fmt.out_buffer.items);
try af.finish();
+ // TODO this should output to stdout instead of stderr.
std.debug.print("{}\n", .{file_path});
}
}
@@ -925,3 +1350,350 @@ pub const info_zen =
\\
\\
;
+
+const FileExt = enum {
+ c,
+ cpp,
+ h,
+ ll,
+ bc,
+ assembly,
+ unknown,
+};
+
+fn hasCExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".c");
+}
+
+fn hasCppExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".C") or
+ mem.endsWith(u8, filename, ".cc") or
+ mem.endsWith(u8, filename, ".cpp") or
+ mem.endsWith(u8, filename, ".cxx");
+}
+
+fn hasAsmExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
+}
+
+fn classify_file_ext(filename: []const u8) FileExt {
+ if (hasCExt(filename)) {
+ return .c;
+ } else if (hasCppExt(filename)) {
+ return .cpp;
+ } else if (mem.endsWith(u8, filename, ".ll")) {
+ return .ll;
+ } else if (mem.endsWith(u8, filename, ".bc")) {
+ return .bc;
+ } else if (hasAsmExt(filename)) {
+ return .assembly;
+ } else if (mem.endsWith(u8, filename, ".h")) {
+ return .h;
+ } else {
+ // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z
+ return .unknown;
+ }
+}
+
+extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
+
+/// TODO make it so the return value can be !noreturn
+fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
+ // Convert the args to the format Clang expects.
+ const argv = try arena.alloc(?[*:0]u8, args.len + 1);
+ for (args) |arg, i| {
+ argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
+ }
+ argv[args.len] = null;
+ const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr);
+ process.exit(@bitCast(u8, @truncate(i8, exit_code)));
+}
+
+const clang_args = @import("clang_options.zig").list;
+
+pub const ClangArgIterator = struct {
+ has_next: bool,
+ zig_equivalent: ZigEquivalent,
+ only_arg: []const u8,
+ second_arg: []const u8,
+ other_args: []const []const u8,
+ argv: []const []const u8,
+ next_index: usize,
+ root_args: ?*Args,
+ allocator: *Allocator,
+
+ pub const ZigEquivalent = enum {
+ target,
+ o,
+ c,
+ other,
+ positional,
+ l,
+ ignore,
+ driver_punt,
+ pic,
+ no_pic,
+ nostdlib,
+ nostdlib_cpp,
+ shared,
+ rdynamic,
+ wl,
+ pp_or_asm,
+ optimize,
+ debug,
+ sanitize,
+ linker_script,
+ verbose_cmds,
+ for_linker,
+ linker_input_z,
+ lib_dir,
+ mcpu,
+ dep_file,
+ framework_dir,
+ framework,
+ nostdlibinc,
+ };
+
+ const Args = struct {
+ next_index: usize,
+ argv: []const []const u8,
+ };
+
+ fn init(allocator: *Allocator, argv: []const []const u8) ClangArgIterator {
+ return .{
+ .next_index = 2, // `zig cc foo` this points to `foo`
+ .has_next = argv.len > 2,
+ .zig_equivalent = undefined,
+ .only_arg = undefined,
+ .second_arg = undefined,
+ .other_args = undefined,
+ .argv = argv,
+ .root_args = null,
+ .allocator = allocator,
+ };
+ }
+
+ fn next(self: *ClangArgIterator) !void {
+ assert(self.has_next);
+ assert(self.next_index < self.argv.len);
+ // In this state we know that the parameter we are looking at is a root parameter
+ // rather than an argument to a parameter.
+ // We adjust the len below when necessary.
+ self.other_args = (self.argv.ptr + self.next_index)[0..1];
+ var arg = mem.span(self.argv[self.next_index]);
+ self.incrementArgIndex();
+
+ if (mem.startsWith(u8, arg, "@")) {
+ if (self.root_args != null) return error.NestedResponseFile;
+
+ // This is a "compiler response file". We must parse the file and treat its
+ // contents as command line parameters.
+ const allocator = self.allocator;
+ const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
+ const resp_file_path = arg[1..];
+ const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| {
+ fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) });
+ };
+ defer allocator.free(resp_contents);
+ // TODO is there a specification for this file format? Let's find it and make this parsing more robust
+ // at the very least I'm guessing this needs to handle quotes and `#` comments.
+ var it = mem.tokenize(resp_contents, " \t\r\n");
+ var resp_arg_list = std.ArrayList([]const u8).init(allocator);
+ defer resp_arg_list.deinit();
+ {
+ errdefer {
+ for (resp_arg_list.span()) |item| {
+ allocator.free(mem.span(item));
+ }
+ }
+ while (it.next()) |token| {
+ const dupe_token = try mem.dupeZ(allocator, u8, token);
+ errdefer allocator.free(dupe_token);
+ try resp_arg_list.append(dupe_token);
+ }
+ const args = try allocator.create(Args);
+ errdefer allocator.destroy(args);
+ args.* = .{
+ .next_index = self.next_index,
+ .argv = self.argv,
+ };
+ self.root_args = args;
+ }
+ const resp_arg_slice = resp_arg_list.toOwnedSlice();
+ self.next_index = 0;
+ self.argv = resp_arg_slice;
+
+ if (resp_arg_slice.len == 0) {
+ self.resolveRespFileArgs();
+ return;
+ }
+
+ self.has_next = true;
+ self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
+ arg = mem.span(self.argv[self.next_index]);
+ self.incrementArgIndex();
+ }
+ if (!mem.startsWith(u8, arg, "-")) {
+ self.zig_equivalent = .positional;
+ self.only_arg = arg;
+ return;
+ }
+
+ find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
+ .flag => {
+ const prefix_len = clang_arg.matchEql(arg);
+ if (prefix_len > 0) {
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ self.only_arg = arg[prefix_len..];
+
+ break :find_clang_arg;
+ }
+ },
+ .joined, .comma_joined => {
+ // joined example: --target=foo
+ // comma_joined example: -Wl,-soname,libsoundio.so.2
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len != 0) {
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part.
+
+ break :find_clang_arg;
+ }
+ },
+ .joined_or_separate => {
+ // Examples: `-lfoo`, `-l foo`
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len == arg.len) {
+ if (self.next_index >= self.argv.len) {
+ fatal("Expected parameter after '{}'", .{arg});
+ }
+ self.only_arg = self.argv[self.next_index];
+ self.incrementArgIndex();
+ self.other_args.len += 1;
+ self.zig_equivalent = clang_arg.zig_equivalent;
+
+ break :find_clang_arg;
+ } else if (prefix_len != 0) {
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ self.only_arg = arg[prefix_len..];
+
+ break :find_clang_arg;
+ }
+ },
+ .joined_and_separate => {
+ // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len != 0) {
+ self.only_arg = arg[prefix_len..];
+ if (self.next_index >= self.argv.len) {
+ fatal("Expected parameter after '{}'", .{arg});
+ }
+ self.second_arg = self.argv[self.next_index];
+ self.incrementArgIndex();
+ self.other_args.len += 1;
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ break :find_clang_arg;
+ }
+ },
+ .separate => if (clang_arg.matchEql(arg) > 0) {
+ if (self.next_index >= self.argv.len) {
+ fatal("Expected parameter after '{}'", .{arg});
+ }
+ self.only_arg = self.argv[self.next_index];
+ self.incrementArgIndex();
+ self.other_args.len += 1;
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ break :find_clang_arg;
+ },
+ .remaining_args_joined => {
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len != 0) {
+ @panic("TODO");
+ }
+ },
+ .multi_arg => if (clang_arg.matchEql(arg) > 0) {
+ @panic("TODO");
+ },
+ }
+ else {
+ fatal("Unknown Clang option: '{}'", .{arg});
+ }
+ }
+
+ fn incrementArgIndex(self: *ClangArgIterator) void {
+ self.next_index += 1;
+ self.resolveRespFileArgs();
+ }
+
+ fn resolveRespFileArgs(self: *ClangArgIterator) void {
+ const allocator = self.allocator;
+ if (self.next_index >= self.argv.len) {
+ if (self.root_args) |root_args| {
+ self.next_index = root_args.next_index;
+ self.argv = root_args.argv;
+
+ allocator.destroy(root_args);
+ self.root_args = null;
+ }
+ if (self.next_index >= self.argv.len) {
+ self.has_next = false;
+ }
+ }
+ }
+};
+
+fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
+ if (ignore_case) {
+ return std.ascii.eqlIgnoreCase(a, b);
+ } else {
+ return mem.eql(u8, a, b);
+ }
+}
+
+fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
+ const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
+
+ if (eqlIgnoreCase(ignore_case, name, "c"))
+ return true;
+
+ if (target.isMinGW()) {
+ if (eqlIgnoreCase(ignore_case, name, "m"))
+ return true;
+
+ return false;
+ }
+
+ if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) {
+ if (eqlIgnoreCase(ignore_case, name, "m"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "rt"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "pthread"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "crypt"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "util"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "xnet"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "resolv"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "dl"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "util"))
+ return true;
+ }
+
+ if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System"))
+ return true;
+
+ return false;
+}
+
+fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool {
+ const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
+
+ return eqlIgnoreCase(ignore_case, name, "c++") or
+ eqlIgnoreCase(ignore_case, name, "stdc++") or
+ eqlIgnoreCase(ignore_case, name, "c++abi");
+}
src-self-hosted/stage2.zig
@@ -414,6 +414,23 @@ export fn stage2_env(argc: c_int, argv: [*]const [*:0]const u8) c_int {
return 0;
}
+export fn stage2_cc(argc: c_int, argv: [*]const [*:0]const u8, is_cpp: bool) c_int {
+ const allocator = std.heap.c_allocator;
+
+ var args_list = argvToArrayList(allocator, argc, argv) catch |err| {
+ std.debug.print("unable to parse arguments: {}\n", .{@errorName(err)});
+ return -1;
+ };
+ defer args_list.deinit();
+
+ self_hosted_main.buildOutputType(allocator, allocator, args_list.items, if (is_cpp) .cpp else .cc) catch |err| {
+ std.debug.print("zig cc failure: {}\n", .{@errorName(err)});
+ return -1;
+ };
+
+ return 0;
+}
+
// ABI warning
export fn stage2_cmd_targets(
zig_triple: ?[*:0]const u8,
@@ -1038,267 +1055,4 @@ fn convertSlice(slice: [][:0]u8, ptr: *[*][*:0]u8, len: *usize) !void {
ptr.* = new_slice.ptr;
}
-const clang_args = @import("clang_options.zig").list;
-
-// ABI warning
-pub const ClangArgIterator = extern struct {
- has_next: bool,
- zig_equivalent: ZigEquivalent,
- only_arg: [*:0]const u8,
- second_arg: [*:0]const u8,
- other_args_ptr: [*]const [*:0]const u8,
- other_args_len: usize,
- argv_ptr: [*]const [*:0]const u8,
- argv_len: usize,
- next_index: usize,
- root_args: ?*Args,
-
- // ABI warning
- pub const ZigEquivalent = extern enum {
- target,
- o,
- c,
- other,
- positional,
- l,
- ignore,
- driver_punt,
- pic,
- no_pic,
- nostdlib,
- nostdlib_cpp,
- shared,
- rdynamic,
- wl,
- pp_or_asm,
- optimize,
- debug,
- sanitize,
- linker_script,
- verbose_cmds,
- for_linker,
- linker_input_z,
- lib_dir,
- mcpu,
- dep_file,
- framework_dir,
- framework,
- nostdlibinc,
- };
-
- const Args = struct {
- next_index: usize,
- argv_ptr: [*]const [*:0]const u8,
- argv_len: usize,
- };
-
- pub fn init(argv: []const [*:0]const u8) ClangArgIterator {
- return .{
- .next_index = 2, // `zig cc foo` this points to `foo`
- .has_next = argv.len > 2,
- .zig_equivalent = undefined,
- .only_arg = undefined,
- .second_arg = undefined,
- .other_args_ptr = undefined,
- .other_args_len = undefined,
- .argv_ptr = argv.ptr,
- .argv_len = argv.len,
- .root_args = null,
- };
- }
-
- pub fn next(self: *ClangArgIterator) !void {
- assert(self.has_next);
- assert(self.next_index < self.argv_len);
- // In this state we know that the parameter we are looking at is a root parameter
- // rather than an argument to a parameter.
- self.other_args_ptr = self.argv_ptr + self.next_index;
- self.other_args_len = 1; // We adjust this value below when necessary.
- var arg = mem.span(self.argv_ptr[self.next_index]);
- self.incrementArgIndex();
-
- if (mem.startsWith(u8, arg, "@")) {
- if (self.root_args != null) return error.NestedResponseFile;
-
- // This is a "compiler response file". We must parse the file and treat its
- // contents as command line parameters.
- const allocator = std.heap.c_allocator;
- const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
- const resp_file_path = arg[1..];
- const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| {
- std.debug.warn("unable to read response file '{}': {}\n", .{ resp_file_path, @errorName(err) });
- process.exit(1);
- };
- defer allocator.free(resp_contents);
- // TODO is there a specification for this file format? Let's find it and make this parsing more robust
- // at the very least I'm guessing this needs to handle quotes and `#` comments.
- var it = mem.tokenize(resp_contents, " \t\r\n");
- var resp_arg_list = std.ArrayList([*:0]const u8).init(allocator);
- defer resp_arg_list.deinit();
- {
- errdefer {
- for (resp_arg_list.span()) |item| {
- allocator.free(mem.span(item));
- }
- }
- while (it.next()) |token| {
- const dupe_token = try mem.dupeZ(allocator, u8, token);
- errdefer allocator.free(dupe_token);
- try resp_arg_list.append(dupe_token);
- }
- const args = try allocator.create(Args);
- errdefer allocator.destroy(args);
- args.* = .{
- .next_index = self.next_index,
- .argv_ptr = self.argv_ptr,
- .argv_len = self.argv_len,
- };
- self.root_args = args;
- }
- const resp_arg_slice = resp_arg_list.toOwnedSlice();
- self.next_index = 0;
- self.argv_ptr = resp_arg_slice.ptr;
- self.argv_len = resp_arg_slice.len;
-
- if (resp_arg_slice.len == 0) {
- self.resolveRespFileArgs();
- return;
- }
-
- self.has_next = true;
- self.other_args_ptr = self.argv_ptr + self.next_index;
- self.other_args_len = 1; // We adjust this value below when necessary.
- arg = mem.span(self.argv_ptr[self.next_index]);
- self.incrementArgIndex();
- }
- if (!mem.startsWith(u8, arg, "-")) {
- self.zig_equivalent = .positional;
- self.only_arg = arg.ptr;
- return;
- }
-
- find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
- .flag => {
- const prefix_len = clang_arg.matchEql(arg);
- if (prefix_len > 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg.ptr + prefix_len;
-
- break :find_clang_arg;
- }
- },
- .joined, .comma_joined => {
- // joined example: --target=foo
- // comma_joined example: -Wl,-soname,libsoundio.so.2
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg.ptr + prefix_len; // This will skip over the "--target=" part.
-
- break :find_clang_arg;
- }
- },
- .joined_or_separate => {
- // Examples: `-lfoo`, `-l foo`
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len == arg.len) {
- if (self.next_index >= self.argv_len) {
- std.debug.warn("Expected parameter after '{}'\n", .{arg});
- process.exit(1);
- }
- self.only_arg = self.argv_ptr[self.next_index];
- self.incrementArgIndex();
- self.other_args_len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
-
- break :find_clang_arg;
- } else if (prefix_len != 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg.ptr + prefix_len;
-
- break :find_clang_arg;
- }
- },
- .joined_and_separate => {
- // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- self.only_arg = arg.ptr + prefix_len;
- if (self.next_index >= self.argv_len) {
- std.debug.warn("Expected parameter after '{}'\n", .{arg});
- process.exit(1);
- }
- self.second_arg = self.argv_ptr[self.next_index];
- self.incrementArgIndex();
- self.other_args_len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
- break :find_clang_arg;
- }
- },
- .separate => if (clang_arg.matchEql(arg) > 0) {
- if (self.next_index >= self.argv_len) {
- std.debug.warn("Expected parameter after '{}'\n", .{arg});
- process.exit(1);
- }
- self.only_arg = self.argv_ptr[self.next_index];
- self.incrementArgIndex();
- self.other_args_len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
- break :find_clang_arg;
- },
- .remaining_args_joined => {
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- @panic("TODO");
- }
- },
- .multi_arg => if (clang_arg.matchEql(arg) > 0) {
- @panic("TODO");
- },
- }
- else {
- std.debug.warn("Unknown Clang option: '{}'\n", .{arg});
- process.exit(1);
- }
- }
-
- fn incrementArgIndex(self: *ClangArgIterator) void {
- self.next_index += 1;
- self.resolveRespFileArgs();
- }
-
- fn resolveRespFileArgs(self: *ClangArgIterator) void {
- const allocator = std.heap.c_allocator;
- if (self.next_index >= self.argv_len) {
- if (self.root_args) |root_args| {
- self.next_index = root_args.next_index;
- self.argv_ptr = root_args.argv_ptr;
- self.argv_len = root_args.argv_len;
-
- allocator.destroy(root_args);
- self.root_args = null;
- }
- if (self.next_index >= self.argv_len) {
- self.has_next = false;
- }
- }
- }
-};
-
-export fn stage2_clang_arg_iterator(
- result: *ClangArgIterator,
- argc: usize,
- argv: [*]const [*:0]const u8,
-) void {
- result.* = ClangArgIterator.init(argv[0..argc]);
-}
-
-export fn stage2_clang_arg_next(it: *ClangArgIterator) Error {
- it.next() catch |err| switch (err) {
- error.NestedResponseFile => return .NestedResponseFile,
- error.OutOfMemory => return .OutOfMemory,
- };
- return .None;
-}
-
export const stage2_is_zig0 = false;
build.zig
@@ -9,6 +9,7 @@ const ArrayList = std.ArrayList;
const io = std.io;
const fs = std.fs;
const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
+const assert = std.debug.assert;
const zig_version = std.builtin.Version{ .major = 0, .minor = 6, .patch = 0 };
@@ -57,11 +58,13 @@ pub fn build(b: *Builder) !void {
if (!only_install_lib_files) {
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
+ exe.install();
exe.setBuildMode(mode);
exe.setTarget(target);
test_step.dependOn(&exe.step);
b.default_step.dependOn(&exe.step);
+ exe.addBuildOption(bool, "have_llvm", enable_llvm);
if (enable_llvm) {
const config_h_text = if (config_h_path_option) |config_h_path|
try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes)
@@ -73,11 +76,8 @@ pub fn build(b: *Builder) !void {
try configureStage2(b, exe, ctx);
}
- if (!only_install_lib_files) {
- exe.install();
- }
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
- const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
+ const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
if (link_libc) {
exe.linkLibC();
test_stage2.linkLibC();
@@ -323,17 +323,13 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
exe.addIncludeDir("src");
exe.addIncludeDir(ctx.cmake_binary_dir);
addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
- if (ctx.lld_include_dir.len != 0) {
- exe.addIncludeDir(ctx.lld_include_dir);
+ assert(ctx.lld_include_dir.len != 0);
+ exe.addIncludeDir(ctx.lld_include_dir);
+ {
var it = mem.tokenize(ctx.lld_libraries, ";");
while (it.next()) |lib| {
exe.addObjectFile(lib);
}
- } else {
- addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm");
- addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf");
- addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff");
- addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib");
}
{
var it = mem.tokenize(ctx.clang_libraries, ";");
@@ -343,42 +339,51 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
}
dependOnLib(b, exe, ctx.llvm);
- if (exe.target.getOsTag() == .linux) {
- // First we try to static link against gcc libstdc++. If that doesn't work,
- // we fall back to -lc++ and cross our fingers.
- addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) {
- error.RequiredLibraryNotFound => {
- exe.linkSystemLibrary("c++");
- },
- else => |e| return e,
- };
+ // Boy, it sure would be nice to simply linkSystemLibrary("c++") and rely on zig's
+ // ability to provide libc++ right? Well thanks to C++ not having a stable ABI this
+ // will cause linker errors. It would work in the situation when `zig cc` is used to
+ // build LLVM, Clang, and LLD, however when depending on them as system libraries, system
+ // libc++ must be used.
+ const cross_compile = false; // TODO
+ if (cross_compile) {
+ // In this case we assume that zig cc was used to build the LLVM, Clang, LLD dependencies.
+ exe.linkSystemLibrary("c++");
+ } else {
+ if (exe.target.getOsTag() == .linux) {
+ // First we try to static link against gcc libstdc++. If that doesn't work,
+ // we fall back to -lc++ and cross our fingers.
+ addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) {
+ error.RequiredLibraryNotFound => {
+ exe.linkSystemLibrary("c++");
+ },
+ else => |e| return e,
+ };
- exe.linkSystemLibrary("pthread");
- } else if (exe.target.isFreeBSD()) {
- try addCxxKnownPath(b, ctx, exe, "libc++.a", null);
- exe.linkSystemLibrary("pthread");
- } else if (exe.target.isDarwin()) {
- if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
- // Compiler is GCC.
- try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
exe.linkSystemLibrary("pthread");
- // TODO LLD cannot perform this link.
- // See https://github.com/ziglang/zig/issues/1535
- exe.enableSystemLinkerHack();
- } else |err| switch (err) {
- error.RequiredLibraryNotFound => {
- // System compiler, not gcc.
- exe.linkSystemLibrary("c++");
- },
- else => |e| return e,
+ } else if (exe.target.isFreeBSD()) {
+ try addCxxKnownPath(b, ctx, exe, "libc++.a", null);
+ exe.linkSystemLibrary("pthread");
+ } else if (exe.target.isDarwin()) {
+ if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
+ // Compiler is GCC.
+ try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
+ exe.linkSystemLibrary("pthread");
+ // TODO LLD cannot perform this link.
+ // See https://github.com/ziglang/zig/issues/1535
+ exe.enableSystemLinkerHack();
+ } else |err| switch (err) {
+ error.RequiredLibraryNotFound => {
+ // System compiler, not gcc.
+ exe.linkSystemLibrary("c++");
+ },
+ else => |e| return e,
+ }
}
- }
- if (ctx.dia_guids_lib.len != 0) {
- exe.addObjectFile(ctx.dia_guids_lib);
+ if (ctx.dia_guids_lib.len != 0) {
+ exe.addObjectFile(ctx.dia_guids_lib);
+ }
}
-
- exe.linkSystemLibrary("c");
}
fn addCxxKnownPath(