From 503ba7b27c6e8e248d271aec936623853cd8fcd1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Sep 2020 20:23:00 -0700 Subject: [PATCH] start moving `zig cc` to stage2 * build.zig: repair the ability to link against llvm, clang, and lld * move the zig cc arg parsing logic to stage2 - the preprocessor flag is still TODO - the clang arg iterator code is improved to use slices instead of raw pointers because it no longer has to deal with an extern struct. * clean up error printing with a `fatal` function and use log API for messages rather than std.debug.print * add support for more CLI options to stage2 & update usage text - hooking up most of these new options is TODO * clean up the way libc and libc++ are detected via command line options. target information is used to determine if any of the libc candidate names are chosen. * add native library directory detection * implement the ability to invoke clang from stage2 * introduce a build_options.have_llvm so we can comptime branch on whether LLVM is linked in or not. --- build.zig | 91 +-- src-self-hosted/clang_options.zig | 4 +- src-self-hosted/main.zig | 942 +++++++++++++++++++++++++++--- src-self-hosted/stage2.zig | 280 +-------- src/config.zig.in | 2 + src/main.cpp | 412 +------------ src/stage2.cpp | 17 +- src/stage2.h | 57 +- 8 files changed, 938 insertions(+), 867 deletions(-) diff --git a/build.zig b/build.zig index 3f7f1a9038..43b3b7eebf 100644 --- a/build.zig +++ b/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( 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; diff --git a/src/config.zig.in b/src/config.zig.in index ccb618df2d..a44149111e 100644 --- a/src/config.zig.in +++ b/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; diff --git a/src/main.cpp b/src/main.cpp index e2f6a82a12..af3bc4b2a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -404,7 +404,6 @@ static int main0(int argc, char **argv) { ZigList framework_dirs = {0}; ZigList 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 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 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(); - 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(); - 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, '/', '\\'); diff --git a/src/stage2.cpp b/src/stage2.cpp index 6c010de84f..ad6a9f9d97 100644 --- a/src/stage2.cpp +++ b/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; diff --git a/src/stage2.h b/src/stage2.h index 38a1f77d46..2e64189450 100644 --- a/src/stage2.h +++ b/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;