aboutsummaryrefslogtreecommitdiff
path: root/src-self-hosted
diff options
context:
space:
mode:
Diffstat (limited to 'src-self-hosted')
-rw-r--r--src-self-hosted/clang_options.zig4
-rw-r--r--src-self-hosted/main.zig942
-rw-r--r--src-self-hosted/stage2.zig280
3 files changed, 875 insertions, 351 deletions
diff --git a/src-self-hosted/clang_options.zig b/src-self-hosted/clang_options.zig
index 1b70c71dac..42bfecb746 100644
--- a/src-self-hosted/clang_options.zig
+++ b/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,
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index fb20a09f1d..330c0468f1 100644
--- a/src-self-hosted/main.zig
+++ b/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");
+}
diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig
index 45b8ad3073..a9a9496b94 100644
--- a/src-self-hosted/stage2.zig
+++ b/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;