| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; |
| 2 | 3 | const io = std.io; |
| 3 | 4 | const fs = std.fs; |
| 4 | 5 | const mem = std.mem; |
| ... | ... | @@ -11,6 +12,13 @@ const link = @import("link.zig"); |
| 11 | 12 | const Package = @import("Package.zig"); |
| 12 | 13 | const zir = @import("zir.zig"); |
| 13 | 14 | const build_options = @import("build_options"); |
| 15 | const warn = std.log.warn; |
| 16 | const info = std.log.info; |
| 17 | |
| 18 | fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 19 | std.log.emerg(format, args); |
| 20 | process.exit(1); |
| 21 | } |
| 14 | 22 | |
| 15 | 23 | pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB |
| 16 | 24 | |
| ... | ... | @@ -28,9 +36,11 @@ const usage = |
| 28 | 36 | \\ build-exe [source] Create executable from source or object files |
| 29 | 37 | \\ build-lib [source] Create library from source or object files |
| 30 | 38 | \\ build-obj [source] Create object from source or assembly |
| 39 | \\ cc Use Zig as a drop-in C compiler |
| 40 | \\ c++ Use Zig as a drop-in C++ compiler |
| 41 | \\ env Print lib path, std path, compiler id and version |
| 31 | 42 | \\ fmt [source] Parse file and render in canonical zig format |
| 32 | 43 | \\ targets List available compilation targets |
| 33 | | \\ env Print lib path, std path, compiler id and version |
| 34 | 44 | \\ version Print version number and exit |
| 35 | 45 | \\ zen Print zen of zig and exit |
| 36 | 46 | \\ |
| ... | ... | @@ -84,11 +94,19 @@ pub fn main() !void { |
| 84 | 94 | const cmd = args[1]; |
| 85 | 95 | const cmd_args = args[2..]; |
| 86 | 96 | if (mem.eql(u8, cmd, "build-exe")) { |
| 87 | | return buildOutputType(gpa, arena, cmd_args, .Exe); |
| 97 | return buildOutputType(gpa, arena, args, .{ .build = .Exe }); |
| 88 | 98 | } else if (mem.eql(u8, cmd, "build-lib")) { |
| 89 | | return buildOutputType(gpa, arena, cmd_args, .Lib); |
| 99 | return buildOutputType(gpa, arena, args, .{ .build = .Lib }); |
| 90 | 100 | } else if (mem.eql(u8, cmd, "build-obj")) { |
| 91 | | return buildOutputType(gpa, arena, cmd_args, .Obj); |
| 101 | return buildOutputType(gpa, arena, args, .{ .build = .Obj }); |
| 102 | } else if (mem.eql(u8, cmd, "cc")) { |
| 103 | return buildOutputType(gpa, arena, args, .cc); |
| 104 | } else if (mem.eql(u8, cmd, "c++")) { |
| 105 | return buildOutputType(gpa, arena, args, .cpp); |
| 106 | } else if (mem.eql(u8, cmd, "clang") or |
| 107 | mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) |
| 108 | { |
| 109 | return punt_to_clang(arena, args); |
| 92 | 110 | } else if (mem.eql(u8, cmd, "fmt")) { |
| 93 | 111 | return cmdFmt(gpa, cmd_args); |
| 94 | 112 | } else if (mem.eql(u8, cmd, "targets")) { |
| ... | ... | @@ -147,6 +165,8 @@ const usage_build_generic = |
| 147 | 165 | \\ ReleaseFast Optimizations on, safety off |
| 148 | 166 | \\ ReleaseSafe Optimizations on, safety on |
| 149 | 167 | \\ ReleaseSmall Optimize for small binary, safety off |
| 168 | \\ -fPIC Force-enable Position Independent Code |
| 169 | \\ -fno-PIC Force-disable Position Independent Code |
| 150 | 170 | \\ --dynamic Force output to be dynamically linked |
| 151 | 171 | \\ --strip Exclude debug symbols |
| 152 | 172 | \\ -ofmt=[mode] Override target object format |
| ... | ... | @@ -158,11 +178,19 @@ const usage_build_generic = |
| 158 | 178 | \\ macho (planned) macOS relocatables |
| 159 | 179 | \\ hex (planned) Intel IHEX |
| 160 | 180 | \\ raw (planned) Dump machine code directly |
| 181 | \\ -dirafter [dir] Add directory to AFTER include search path |
| 182 | \\ -isystem [dir] Add directory to SYSTEM include search path |
| 183 | \\ -I[dir] Add directory to include search path |
| 184 | \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted) |
| 161 | 185 | \\ |
| 162 | 186 | \\Link Options: |
| 163 | 187 | \\ -l[lib], --library [lib] Link against system library |
| 188 | \\ -L[d], --library-directory [d] Add a directory to the library search path |
| 189 | \\ -T[script] Use a custom linker script |
| 164 | 190 | \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) |
| 165 | 191 | \\ --version [ver] Dynamic library semver |
| 192 | \\ -rdynamic Add all symbols to the dynamic symbol table |
| 193 | \\ -rpath [path] Add directory to the runtime library search path |
| 166 | 194 | \\ |
| 167 | 195 | \\Debug Options (Zig Compiler Development): |
| 168 | 196 | \\ -ftime-report Print timing diagnostics |
| ... | ... | @@ -181,11 +209,15 @@ const Emit = union(enum) { |
| 181 | 209 | yes: []const u8, |
| 182 | 210 | }; |
| 183 | 211 | |
| 184 | | fn buildOutputType( |
| 212 | pub fn buildOutputType( |
| 185 | 213 | gpa: *Allocator, |
| 186 | 214 | arena: *Allocator, |
| 187 | | args: []const []const u8, |
| 188 | | output_mode: std.builtin.OutputMode, |
| 215 | all_args: []const []const u8, |
| 216 | arg_mode: union(enum) { |
| 217 | build: std.builtin.OutputMode, |
| 218 | cc, |
| 219 | cpp, |
| 220 | }, |
| 189 | 221 | ) !void { |
| 190 | 222 | var color: Color = .Auto; |
| 191 | 223 | var build_mode: std.builtin.Mode = .Debug; |
| ... | ... | @@ -194,6 +226,7 @@ fn buildOutputType( |
| 194 | 226 | var root_src_file: ?[]const u8 = null; |
| 195 | 227 | var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; |
| 196 | 228 | var strip = false; |
| 229 | var emit_h = true; |
| 197 | 230 | var watch = false; |
| 198 | 231 | var debug_tokenize = false; |
| 199 | 232 | var debug_ast_tree = false; |
| ... | ... | @@ -201,6 +234,7 @@ fn buildOutputType( |
| 201 | 234 | var debug_link = false; |
| 202 | 235 | var debug_ir = false; |
| 203 | 236 | var debug_codegen = false; |
| 237 | var debug_cc = false; |
| 204 | 238 | var time_report = false; |
| 205 | 239 | var emit_bin: Emit = .yes_default_path; |
| 206 | 240 | var emit_zir: Emit = .no; |
| ... | ... | @@ -208,11 +242,57 @@ fn buildOutputType( |
| 208 | 242 | var target_mcpu: ?[]const u8 = null; |
| 209 | 243 | var target_dynamic_linker: ?[]const u8 = null; |
| 210 | 244 | var target_ofmt: ?[]const u8 = null; |
| 245 | var output_mode: std.builtin.OutputMode = undefined; |
| 246 | var ensure_libc_on_non_freestanding = false; |
| 247 | var ensure_libcpp_on_non_freestanding = false; |
| 248 | var have_libc = false; |
| 249 | var have_libcpp = false; |
| 250 | var want_native_include_dirs = false; |
| 251 | var enable_cache: ?bool = null; |
| 252 | var want_pic: ?bool = null; |
| 253 | var want_sanitize_c: ?bool = null; |
| 254 | var rdynamic: bool = false; |
| 255 | var only_pp_or_asm = false; |
| 256 | var linker_script: ?[]const u8 = null; |
| 257 | var version_script: ?[]const u8 = null; |
| 258 | var disable_c_depfile = false; |
| 259 | var override_soname: ?[]const u8 = null; |
| 260 | var linker_optimization: ?[]const u8 = null; |
| 261 | var linker_gc_sections: ?bool = null; |
| 262 | var linker_allow_shlib_undefined: ?bool = null; |
| 263 | var linker_bind_global_refs_locally: ?bool = null; |
| 264 | var linker_z_nodelete = false; |
| 265 | var linker_z_defs = false; |
| 266 | var stack_size_override: u64 = 0; |
| 211 | 267 | |
| 212 | 268 | var system_libs = std.ArrayList([]const u8).init(gpa); |
| 213 | 269 | defer system_libs.deinit(); |
| 214 | 270 | |
| 215 | | { |
| 271 | var clang_argv = std.ArrayList([]const u8).init(gpa); |
| 272 | defer clang_argv.deinit(); |
| 273 | |
| 274 | var lib_dirs = std.ArrayList([]const u8).init(gpa); |
| 275 | defer lib_dirs.deinit(); |
| 276 | |
| 277 | var rpath_list = std.ArrayList([]const u8).init(gpa); |
| 278 | defer rpath_list.deinit(); |
| 279 | |
| 280 | var c_source_files = std.ArrayList([]const u8).init(gpa); |
| 281 | defer c_source_files.deinit(); |
| 282 | |
| 283 | var link_objects = std.ArrayList([]const u8).init(gpa); |
| 284 | defer link_objects.deinit(); |
| 285 | |
| 286 | var framework_dirs = std.ArrayList([]const u8).init(gpa); |
| 287 | defer framework_dirs.deinit(); |
| 288 | |
| 289 | var frameworks = std.ArrayList([]const u8).init(gpa); |
| 290 | defer frameworks.deinit(); |
| 291 | |
| 292 | if (arg_mode == .build) { |
| 293 | output_mode = arg_mode.build; |
| 294 | |
| 295 | const args = all_args[2..]; |
| 216 | 296 | var i: usize = 0; |
| 217 | 297 | while (i < args.len) : (i += 1) { |
| 218 | 298 | const arg = args[i]; |
| ... | ... | @@ -222,8 +302,7 @@ fn buildOutputType( |
| 222 | 302 | process.exit(0); |
| 223 | 303 | } else if (mem.eql(u8, arg, "--color")) { |
| 224 | 304 | if (i + 1 >= args.len) { |
| 225 | | std.debug.print("expected [auto|on|off] after --color\n", .{}); |
| 226 | | process.exit(1); |
| 305 | fatal("expected [auto|on|off] after --color", .{}); |
| 227 | 306 | } |
| 228 | 307 | i += 1; |
| 229 | 308 | const next_arg = args[i]; |
| ... | ... | @@ -234,13 +313,11 @@ fn buildOutputType( |
| 234 | 313 | } else if (mem.eql(u8, next_arg, "off")) { |
| 235 | 314 | color = .Off; |
| 236 | 315 | } else { |
| 237 | | std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); |
| 238 | | process.exit(1); |
| 316 | fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); |
| 239 | 317 | } |
| 240 | 318 | } else if (mem.eql(u8, arg, "--mode")) { |
| 241 | 319 | if (i + 1 >= args.len) { |
| 242 | | std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{}); |
| 243 | | process.exit(1); |
| 320 | fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{}); |
| 244 | 321 | } |
| 245 | 322 | i += 1; |
| 246 | 323 | const next_arg = args[i]; |
| ... | ... | @@ -253,44 +330,66 @@ fn buildOutputType( |
| 253 | 330 | } else if (mem.eql(u8, next_arg, "ReleaseSmall")) { |
| 254 | 331 | build_mode = .ReleaseSmall; |
| 255 | 332 | } else { |
| 256 | | std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg}); |
| 257 | | process.exit(1); |
| 333 | fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg}); |
| 258 | 334 | } |
| 335 | } else if (mem.eql(u8, arg, "--stack")) { |
| 336 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 337 | i += 1; |
| 338 | stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| { |
| 339 | fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); |
| 340 | }; |
| 259 | 341 | } else if (mem.eql(u8, arg, "--name")) { |
| 260 | | if (i + 1 >= args.len) { |
| 261 | | std.debug.print("expected parameter after --name\n", .{}); |
| 262 | | process.exit(1); |
| 263 | | } |
| 342 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 264 | 343 | i += 1; |
| 265 | 344 | provided_name = args[i]; |
| 266 | | } else if (mem.eql(u8, arg, "--library")) { |
| 267 | | if (i + 1 >= args.len) { |
| 268 | | std.debug.print("expected parameter after --library\n", .{}); |
| 269 | | process.exit(1); |
| 270 | | } |
| 345 | } else if (mem.eql(u8, arg, "-rpath")) { |
| 346 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 347 | i += 1; |
| 348 | try rpath_list.append(args[i]); |
| 349 | } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { |
| 350 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 351 | i += 1; |
| 352 | try lib_dirs.append(args[i]); |
| 353 | } else if (mem.eql(u8, arg, "-T")) { |
| 354 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 355 | i += 1; |
| 356 | linker_script = args[i]; |
| 357 | } else if (mem.eql(u8, arg, "--version-script")) { |
| 358 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 359 | i += 1; |
| 360 | version_script = args[i]; |
| 361 | } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) { |
| 362 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 363 | // We don't know whether this library is part of libc or libc++ until we resolve the target. |
| 364 | // So we simply append to the list for now. |
| 271 | 365 | i += 1; |
| 272 | 366 | try system_libs.append(args[i]); |
| 367 | } else if (mem.eql(u8, arg, "-D") or |
| 368 | mem.eql(u8, arg, "-isystem") or |
| 369 | mem.eql(u8, arg, "-I") or |
| 370 | mem.eql(u8, arg, "-dirafter")) |
| 371 | { |
| 372 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 373 | i += 1; |
| 374 | try clang_argv.append(arg); |
| 375 | try clang_argv.append(args[i]); |
| 273 | 376 | } else if (mem.eql(u8, arg, "--version")) { |
| 274 | 377 | if (i + 1 >= args.len) { |
| 275 | | std.debug.print("expected parameter after --version\n", .{}); |
| 276 | | process.exit(1); |
| 378 | fatal("expected parameter after --version", .{}); |
| 277 | 379 | } |
| 278 | 380 | i += 1; |
| 279 | 381 | version = std.builtin.Version.parse(args[i]) catch |err| { |
| 280 | | std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) }); |
| 281 | | process.exit(1); |
| 382 | fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) }); |
| 282 | 383 | }; |
| 283 | 384 | } else if (mem.eql(u8, arg, "-target")) { |
| 284 | 385 | if (i + 1 >= args.len) { |
| 285 | | std.debug.print("expected parameter after -target\n", .{}); |
| 286 | | process.exit(1); |
| 386 | fatal("expected parameter after -target", .{}); |
| 287 | 387 | } |
| 288 | 388 | i += 1; |
| 289 | 389 | target_arch_os_abi = args[i]; |
| 290 | 390 | } else if (mem.eql(u8, arg, "-mcpu")) { |
| 291 | 391 | if (i + 1 >= args.len) { |
| 292 | | std.debug.print("expected parameter after -mcpu\n", .{}); |
| 293 | | process.exit(1); |
| 392 | fatal("expected parameter after -mcpu", .{}); |
| 294 | 393 | } |
| 295 | 394 | i += 1; |
| 296 | 395 | target_mcpu = args[i]; |
| ... | ... | @@ -300,8 +399,7 @@ fn buildOutputType( |
| 300 | 399 | target_mcpu = arg["-mcpu=".len..]; |
| 301 | 400 | } else if (mem.eql(u8, arg, "--dynamic-linker")) { |
| 302 | 401 | if (i + 1 >= args.len) { |
| 303 | | std.debug.print("expected parameter after --dynamic-linker\n", .{}); |
| 304 | | process.exit(1); |
| 402 | fatal("expected parameter after --dynamic-linker", .{}); |
| 305 | 403 | } |
| 306 | 404 | i += 1; |
| 307 | 405 | target_dynamic_linker = args[i]; |
| ... | ... | @@ -309,6 +407,12 @@ fn buildOutputType( |
| 309 | 407 | watch = true; |
| 310 | 408 | } else if (mem.eql(u8, arg, "-ftime-report")) { |
| 311 | 409 | time_report = true; |
| 410 | } else if (mem.eql(u8, arg, "-fPIC")) { |
| 411 | want_pic = true; |
| 412 | } else if (mem.eql(u8, arg, "-fno-PIC")) { |
| 413 | want_pic = false; |
| 414 | } else if (mem.eql(u8, arg, "-rdynamic")) { |
| 415 | rdynamic = true; |
| 312 | 416 | } else if (mem.eql(u8, arg, "-femit-bin")) { |
| 313 | 417 | emit_bin = .yes_default_path; |
| 314 | 418 | } else if (mem.startsWith(u8, arg, "-femit-bin=")) { |
| ... | ... | @@ -327,6 +431,8 @@ fn buildOutputType( |
| 327 | 431 | link_mode = .Static; |
| 328 | 432 | } else if (mem.eql(u8, arg, "--strip")) { |
| 329 | 433 | strip = true; |
| 434 | } else if (mem.eql(u8, arg, "-Bsymbolic")) { |
| 435 | linker_bind_global_refs_locally = true; |
| 330 | 436 | } else if (mem.eql(u8, arg, "--debug-tokenize")) { |
| 331 | 437 | debug_tokenize = true; |
| 332 | 438 | } else if (mem.eql(u8, arg, "--debug-ast-tree")) { |
| ... | ... | @@ -339,44 +445,321 @@ fn buildOutputType( |
| 339 | 445 | debug_ir = true; |
| 340 | 446 | } else if (mem.eql(u8, arg, "--debug-codegen")) { |
| 341 | 447 | debug_codegen = true; |
| 448 | } else if (mem.eql(u8, arg, "--debug-cc")) { |
| 449 | debug_cc = true; |
| 450 | } else if (mem.startsWith(u8, arg, "-T")) { |
| 451 | linker_script = arg[2..]; |
| 452 | } else if (mem.startsWith(u8, arg, "-L")) { |
| 453 | try lib_dirs.append(arg[2..]); |
| 342 | 454 | } else if (mem.startsWith(u8, arg, "-l")) { |
| 455 | // We don't know whether this library is part of libc or libc++ until we resolve the target. |
| 456 | // So we simply append to the list for now. |
| 343 | 457 | try system_libs.append(arg[2..]); |
| 458 | } else if (mem.startsWith(u8, arg, "-D") or |
| 459 | mem.startsWith(u8, arg, "-I")) |
| 460 | { |
| 461 | try clang_argv.append(arg); |
| 344 | 462 | } else { |
| 345 | | std.debug.print("unrecognized parameter: '{}'\n", .{arg}); |
| 346 | | process.exit(1); |
| 463 | fatal("unrecognized parameter: '{}'", .{arg}); |
| 347 | 464 | } |
| 348 | | } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) { |
| 349 | | std.debug.print("assembly files not supported yet\n", .{}); |
| 350 | | process.exit(1); |
| 351 | 465 | } else if (mem.endsWith(u8, arg, ".o") or |
| 352 | 466 | mem.endsWith(u8, arg, ".obj") or |
| 353 | 467 | mem.endsWith(u8, arg, ".a") or |
| 354 | 468 | mem.endsWith(u8, arg, ".lib")) |
| 355 | 469 | { |
| 356 | | std.debug.print("object files and static libraries not supported yet\n", .{}); |
| 357 | | process.exit(1); |
| 358 | | } else if (mem.endsWith(u8, arg, ".c") or |
| 359 | | mem.endsWith(u8, arg, ".cpp")) |
| 360 | | { |
| 361 | | std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet\n", .{}); |
| 362 | | process.exit(1); |
| 470 | try link_objects.append(arg); |
| 471 | } else if (hasAsmExt(arg) or hasCExt(arg) or hasCppExt(arg)) { |
| 472 | try c_source_files.append(arg); |
| 363 | 473 | } else if (mem.endsWith(u8, arg, ".so") or |
| 364 | 474 | mem.endsWith(u8, arg, ".dylib") or |
| 365 | 475 | mem.endsWith(u8, arg, ".dll")) |
| 366 | 476 | { |
| 367 | | std.debug.print("linking against dynamic libraries not yet supported\n", .{}); |
| 368 | | process.exit(1); |
| 477 | fatal("linking against dynamic libraries not yet supported", .{}); |
| 369 | 478 | } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) { |
| 370 | 479 | if (root_src_file) |other| { |
| 371 | | std.debug.print("found another zig file '{}' after root source file '{}'\n", .{ arg, other }); |
| 372 | | process.exit(1); |
| 480 | fatal("found another zig file '{}' after root source file '{}'", .{ arg, other }); |
| 373 | 481 | } else { |
| 374 | 482 | root_src_file = arg; |
| 375 | 483 | } |
| 376 | 484 | } else { |
| 377 | | std.debug.print("unrecognized file extension of parameter '{}'\n", .{arg}); |
| 485 | fatal("unrecognized file extension of parameter '{}'", .{arg}); |
| 486 | } |
| 487 | } |
| 488 | } else { |
| 489 | if (!build_options.have_llvm) |
| 490 | fatal("`zig cc` and `zig c++` unavailable: compiler not built with LLVM extensions enabled", .{}); |
| 491 | emit_h = false; |
| 492 | strip = true; |
| 493 | ensure_libc_on_non_freestanding = true; |
| 494 | ensure_libcpp_on_non_freestanding = arg_mode == .cpp; |
| 495 | want_native_include_dirs = true; |
| 496 | |
| 497 | var c_arg = false; |
| 498 | var is_shared_lib = false; |
| 499 | var linker_args = std.ArrayList([]const u8).init(arena); |
| 500 | var it = ClangArgIterator.init(arena, all_args); |
| 501 | while (it.has_next) { |
| 502 | it.next() catch |err| { |
| 503 | fatal("unable to parse command line parameters: {}", .{@errorName(err)}); |
| 504 | }; |
| 505 | switch (it.zig_equivalent) { |
| 506 | .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown |
| 507 | .o => { |
| 508 | // -o |
| 509 | emit_bin = .{ .yes = it.only_arg }; |
| 510 | enable_cache = true; |
| 511 | }, |
| 512 | .c => c_arg = true, // -c |
| 513 | .other => { |
| 514 | try clang_argv.appendSlice(it.other_args); |
| 515 | }, |
| 516 | .positional => { |
| 517 | const file_ext = classify_file_ext(mem.spanZ(it.only_arg)); |
| 518 | switch (file_ext) { |
| 519 | .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg), |
| 520 | .unknown => try link_objects.append(it.only_arg), |
| 521 | } |
| 522 | }, |
| 523 | .l => { |
| 524 | // -l |
| 525 | // We don't know whether this library is part of libc or libc++ until we resolve the target. |
| 526 | // So we simply append to the list for now. |
| 527 | try system_libs.append(it.only_arg); |
| 528 | }, |
| 529 | .ignore => {}, |
| 530 | .driver_punt => { |
| 531 | // Never mind what we're doing, just pass the args directly. For example --help. |
| 532 | return punt_to_clang(arena, all_args); |
| 533 | }, |
| 534 | .pic => want_pic = true, |
| 535 | .no_pic => want_pic = false, |
| 536 | .nostdlib => ensure_libc_on_non_freestanding = false, |
| 537 | .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false, |
| 538 | .shared => { |
| 539 | link_mode = .Dynamic; |
| 540 | is_shared_lib = true; |
| 541 | }, |
| 542 | .rdynamic => rdynamic = true, |
| 543 | .wl => { |
| 544 | var split_it = mem.split(it.only_arg, ","); |
| 545 | @breakpoint(); // TODO the first arg is empty string right? skip past that. |
| 546 | while (split_it.next()) |linker_arg| { |
| 547 | try linker_args.append(linker_arg); |
| 548 | } |
| 549 | }, |
| 550 | .pp_or_asm => { |
| 551 | // This handles both -E and -S. |
| 552 | only_pp_or_asm = true; |
| 553 | try clang_argv.appendSlice(it.other_args); |
| 554 | }, |
| 555 | .optimize => { |
| 556 | // Alright, what release mode do they want? |
| 557 | if (mem.eql(u8, it.only_arg, "Os")) { |
| 558 | build_mode = .ReleaseSmall; |
| 559 | } else if (mem.eql(u8, it.only_arg, "O2") or |
| 560 | mem.eql(u8, it.only_arg, "O3") or |
| 561 | mem.eql(u8, it.only_arg, "O4")) |
| 562 | { |
| 563 | build_mode = .ReleaseFast; |
| 564 | } else if (mem.eql(u8, it.only_arg, "Og") or |
| 565 | mem.eql(u8, it.only_arg, "O0")) |
| 566 | { |
| 567 | build_mode = .Debug; |
| 568 | } else { |
| 569 | try clang_argv.appendSlice(it.other_args); |
| 570 | } |
| 571 | }, |
| 572 | .debug => { |
| 573 | strip = false; |
| 574 | if (mem.eql(u8, it.only_arg, "-g")) { |
| 575 | // We handled with strip = false above. |
| 576 | } else { |
| 577 | try clang_argv.appendSlice(it.other_args); |
| 578 | } |
| 579 | }, |
| 580 | .sanitize => { |
| 581 | if (mem.eql(u8, it.only_arg, "undefined")) { |
| 582 | want_sanitize_c = true; |
| 583 | } else { |
| 584 | try clang_argv.appendSlice(it.other_args); |
| 585 | } |
| 586 | }, |
| 587 | .linker_script => linker_script = it.only_arg, |
| 588 | .verbose_cmds => { |
| 589 | debug_cc = true; |
| 590 | debug_link = true; |
| 591 | }, |
| 592 | .for_linker => try linker_args.append(it.only_arg), |
| 593 | .linker_input_z => { |
| 594 | try linker_args.append("-z"); |
| 595 | try linker_args.append(it.only_arg); |
| 596 | }, |
| 597 | .lib_dir => try lib_dirs.append(it.only_arg), |
| 598 | .mcpu => target_mcpu = it.only_arg, |
| 599 | .dep_file => { |
| 600 | disable_c_depfile = true; |
| 601 | try clang_argv.appendSlice(it.other_args); |
| 602 | }, |
| 603 | .framework_dir => try framework_dirs.append(it.only_arg), |
| 604 | .framework => try frameworks.append(it.only_arg), |
| 605 | .nostdlibinc => want_native_include_dirs = false, |
| 606 | } |
| 607 | } |
| 608 | // Parse linker args. |
| 609 | var i: usize = 0; |
| 610 | while (i < linker_args.items.len) : (i += 1) { |
| 611 | const arg = linker_args.items[i]; |
| 612 | if (mem.eql(u8, arg, "-soname")) { |
| 613 | i += 1; |
| 614 | if (i >= linker_args.items.len) { |
| 615 | fatal("expected linker arg after '{}'", .{arg}); |
| 616 | } |
| 617 | const soname = linker_args.items[i]; |
| 618 | override_soname = soname; |
| 619 | // Use it as --name. |
| 620 | // Example: libsoundio.so.2 |
| 621 | var prefix: usize = 0; |
| 622 | if (mem.startsWith(u8, soname, "lib")) { |
| 623 | prefix = 3; |
| 624 | } |
| 625 | var end: usize = soname.len; |
| 626 | if (mem.endsWith(u8, soname, ".so")) { |
| 627 | end -= 3; |
| 628 | } else { |
| 629 | var found_digit = false; |
| 630 | while (end > 0 and std.ascii.isDigit(soname[end - 1])) { |
| 631 | found_digit = true; |
| 632 | end -= 1; |
| 633 | } |
| 634 | if (found_digit and end > 0 and soname[end - 1] == '.') { |
| 635 | end -= 1; |
| 636 | } else { |
| 637 | end = soname.len; |
| 638 | } |
| 639 | if (mem.endsWith(u8, soname[prefix..end], ".so")) { |
| 640 | end -= 3; |
| 641 | } |
| 642 | } |
| 643 | provided_name = soname[prefix..end]; |
| 644 | } else if (mem.eql(u8, arg, "-rpath")) { |
| 645 | i += 1; |
| 646 | if (i >= linker_args.items.len) { |
| 647 | fatal("expected linker arg after '{}'", .{arg}); |
| 648 | } |
| 649 | try rpath_list.append(linker_args.items[i]); |
| 650 | } else if (mem.eql(u8, arg, "-I") or |
| 651 | mem.eql(u8, arg, "--dynamic-linker") or |
| 652 | mem.eql(u8, arg, "-dynamic-linker")) |
| 653 | { |
| 654 | i += 1; |
| 655 | if (i >= linker_args.items.len) { |
| 656 | fatal("expected linker arg after '{}'", .{arg}); |
| 657 | } |
| 658 | target_dynamic_linker = linker_args.items[i]; |
| 659 | } else if (mem.eql(u8, arg, "-E") or |
| 660 | mem.eql(u8, arg, "--export-dynamic") or |
| 661 | mem.eql(u8, arg, "-export-dynamic")) |
| 662 | { |
| 663 | rdynamic = true; |
| 664 | } else if (mem.eql(u8, arg, "--version-script")) { |
| 665 | i += 1; |
| 666 | if (i >= linker_args.items.len) { |
| 667 | fatal("expected linker arg after '{}'", .{arg}); |
| 668 | } |
| 669 | version_script = linker_args.items[i]; |
| 670 | } else if (mem.startsWith(u8, arg, "-O")) { |
| 671 | linker_optimization = arg; |
| 672 | } else if (mem.eql(u8, arg, "--gc-sections")) { |
| 673 | linker_gc_sections = true; |
| 674 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { |
| 675 | linker_gc_sections = false; |
| 676 | } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or |
| 677 | mem.eql(u8, arg, "-allow-shlib-undefined")) |
| 678 | { |
| 679 | linker_allow_shlib_undefined = true; |
| 680 | } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or |
| 681 | mem.eql(u8, arg, "-no-allow-shlib-undefined")) |
| 682 | { |
| 683 | linker_allow_shlib_undefined = false; |
| 684 | } else if (mem.eql(u8, arg, "-Bsymbolic")) { |
| 685 | linker_bind_global_refs_locally = true; |
| 686 | } else if (mem.eql(u8, arg, "-z")) { |
| 687 | i += 1; |
| 688 | if (i >= linker_args.items.len) { |
| 689 | fatal("expected linker arg after '{}'", .{arg}); |
| 690 | } |
| 691 | const z_arg = linker_args.items[i]; |
| 692 | if (mem.eql(u8, z_arg, "nodelete")) { |
| 693 | linker_z_nodelete = true; |
| 694 | } else if (mem.eql(u8, z_arg, "defs")) { |
| 695 | linker_z_defs = true; |
| 696 | } else { |
| 697 | warn("unsupported linker arg: -z {}", .{z_arg}); |
| 698 | } |
| 699 | } else if (mem.eql(u8, arg, "--major-image-version")) { |
| 700 | i += 1; |
| 701 | if (i >= linker_args.items.len) { |
| 702 | fatal("expected linker arg after '{}'", .{arg}); |
| 703 | } |
| 704 | version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| { |
| 705 | fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); |
| 706 | }; |
| 707 | } else if (mem.eql(u8, arg, "--minor-image-version")) { |
| 708 | i += 1; |
| 709 | if (i >= linker_args.items.len) { |
| 710 | fatal("expected linker arg after '{}'", .{arg}); |
| 711 | } |
| 712 | version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| { |
| 713 | fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); |
| 714 | }; |
| 715 | } else if (mem.eql(u8, arg, "--stack")) { |
| 716 | i += 1; |
| 717 | if (i >= linker_args.items.len) { |
| 718 | fatal("expected linker arg after '{}'", .{arg}); |
| 719 | } |
| 720 | stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| { |
| 721 | fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); |
| 722 | }; |
| 723 | } else { |
| 724 | warn("unsupported linker arg: {}", .{arg}); |
| 378 | 725 | } |
| 379 | 726 | } |
| 727 | |
| 728 | if (want_sanitize_c == true and build_mode == .ReleaseFast) { |
| 729 | build_mode = .ReleaseSafe; |
| 730 | } |
| 731 | |
| 732 | if (only_pp_or_asm) { |
| 733 | output_mode = .Obj; |
| 734 | fatal("TODO implement using zig cc as a preprocessor", .{}); |
| 735 | //// Transfer "link_objects" into c_source_files so that all those |
| 736 | //// args make it onto the command line. |
| 737 | //try c_source_files.appendSlice(link_objects.items); |
| 738 | //for (c_source_files.items) |c_source_file| { |
| 739 | // const src_path = switch (emit_bin) { |
| 740 | // .yes => |p| p, |
| 741 | // else => c_source_file.source_path, |
| 742 | // }; |
| 743 | // const basename = std.fs.path.basename(src_path); |
| 744 | // c_source_file.preprocessor_only_basename = basename; |
| 745 | //} |
| 746 | //emit_bin = .no; |
| 747 | } else if (!c_arg) { |
| 748 | output_mode = if (is_shared_lib) .Lib else .Exe; |
| 749 | switch (emit_bin) { |
| 750 | .no, .yes_default_path => { |
| 751 | emit_bin = .{ .yes = "a.out" }; |
| 752 | enable_cache = true; |
| 753 | }, |
| 754 | .yes => {}, |
| 755 | } |
| 756 | } else { |
| 757 | output_mode = .Obj; |
| 758 | } |
| 759 | if (c_source_files.items.len == 0 and link_objects.items.len == 0) { |
| 760 | // For example `zig cc` and no args should print the "no input files" message. |
| 761 | return punt_to_clang(arena, all_args); |
| 762 | } |
| 380 | 763 | } |
| 381 | 764 | |
| 382 | 765 | const root_name = if (provided_name) |n| n else blk: { |
| ... | ... | @@ -385,16 +768,10 @@ fn buildOutputType( |
| 385 | 768 | var it = mem.split(basename, "."); |
| 386 | 769 | break :blk it.next() orelse basename; |
| 387 | 770 | } else { |
| 388 | | std.debug.print("--name [name] not provided and unable to infer\n", .{}); |
| 389 | | process.exit(1); |
| 771 | fatal("--name [name] not provided and unable to infer", .{}); |
| 390 | 772 | } |
| 391 | 773 | }; |
| 392 | 774 | |
| 393 | | if (system_libs.items.len != 0) { |
| 394 | | std.debug.print("linking against system libraries not yet supported\n", .{}); |
| 395 | | process.exit(1); |
| 396 | | } |
| 397 | | |
| 398 | 775 | var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{}; |
| 399 | 776 | const cross_target = std.zig.CrossTarget.parse(.{ |
| 400 | 777 | .arch_os_abi = target_arch_os_abi, |
| ... | ... | @@ -429,17 +806,67 @@ fn buildOutputType( |
| 429 | 806 | else => |e| return e, |
| 430 | 807 | }; |
| 431 | 808 | |
| 432 | | var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target); |
| 809 | const target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target); |
| 433 | 810 | if (target_info.cpu_detection_unimplemented) { |
| 434 | 811 | // TODO We want to just use detected_info.target but implementing |
| 435 | 812 | // CPU model & feature detection is todo so here we rely on LLVM. |
| 436 | | std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{}); |
| 437 | | process.exit(1); |
| 813 | fatal("CPU features detection is not yet available for this system without LLVM extensions", .{}); |
| 814 | } |
| 815 | |
| 816 | if (target_info.target.os.tag != .freestanding) { |
| 817 | if (ensure_libc_on_non_freestanding) |
| 818 | have_libc = true; |
| 819 | if (ensure_libcpp_on_non_freestanding) |
| 820 | have_libcpp = true; |
| 821 | } |
| 822 | |
| 823 | // Now that we have target info, we can find out if any of the system libraries |
| 824 | // are part of libc or libc++. We remove them from the list and communicate their |
| 825 | // existence via flags instead. |
| 826 | { |
| 827 | var i: usize = 0; |
| 828 | while (i < system_libs.items.len) { |
| 829 | const lib_name = system_libs.items[i]; |
| 830 | if (is_libc_lib_name(target_info.target, lib_name)) { |
| 831 | have_libc = true; |
| 832 | _ = system_libs.orderedRemove(i); |
| 833 | continue; |
| 834 | } |
| 835 | if (is_libcpp_lib_name(target_info.target, lib_name)) { |
| 836 | have_libcpp = true; |
| 837 | _ = system_libs.orderedRemove(i); |
| 838 | continue; |
| 839 | } |
| 840 | i += 1; |
| 841 | } |
| 842 | } |
| 843 | |
| 844 | if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) { |
| 845 | const paths = std.zig.system.NativePaths.detect(arena) catch |err| { |
| 846 | fatal("unable to detect native system paths: {}", .{@errorName(err)}); |
| 847 | }; |
| 848 | for (paths.warnings.items) |warning| { |
| 849 | warn("{}", .{warning}); |
| 850 | } |
| 851 | try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); |
| 852 | for (paths.include_dirs.items) |include_dir| { |
| 853 | clang_argv.appendAssumeCapacity("-isystem"); |
| 854 | clang_argv.appendAssumeCapacity(include_dir); |
| 855 | } |
| 856 | for (paths.lib_dirs.items) |lib_dir| { |
| 857 | try lib_dirs.append(lib_dir); |
| 858 | } |
| 859 | for (paths.rpaths.items) |rpath| { |
| 860 | try rpath_list.append(rpath); |
| 861 | } |
| 862 | } |
| 863 | |
| 864 | if (system_libs.items.len != 0) { |
| 865 | fatal("linking against system libraries not yet supported", .{}); |
| 438 | 866 | } |
| 439 | 867 | |
| 440 | 868 | const src_path = root_src_file orelse { |
| 441 | | std.debug.print("expected at least one file argument", .{}); |
| 442 | | process.exit(1); |
| 869 | fatal("expected at least one file argument", .{}); |
| 443 | 870 | }; |
| 444 | 871 | |
| 445 | 872 | const object_format: ?std.Target.ObjectFormat = blk: { |
| ... | ... | @@ -461,15 +888,13 @@ fn buildOutputType( |
| 461 | 888 | } else if (mem.eql(u8, ofmt, "raw")) { |
| 462 | 889 | break :blk .raw; |
| 463 | 890 | } else { |
| 464 | | std.debug.print("unsupported object format: {}", .{ofmt}); |
| 465 | | process.exit(1); |
| 891 | fatal("unsupported object format: {}", .{ofmt}); |
| 466 | 892 | } |
| 467 | 893 | }; |
| 468 | 894 | |
| 469 | 895 | const bin_path = switch (emit_bin) { |
| 470 | 896 | .no => { |
| 471 | | std.debug.print("-fno-emit-bin not supported yet", .{}); |
| 472 | | process.exit(1); |
| 897 | fatal("-fno-emit-bin not supported yet", .{}); |
| 473 | 898 | }, |
| 474 | 899 | .yes_default_path => if (object_format != null and object_format.? == .c) |
| 475 | 900 | try std.fmt.allocPrint(arena, "{}.c", .{root_name}) |
| ... | ... | @@ -515,6 +940,11 @@ fn buildOutputType( |
| 515 | 940 | |
| 516 | 941 | try updateModule(gpa, &module, zir_out_path); |
| 517 | 942 | |
| 943 | if (build_options.have_llvm and only_pp_or_asm) { |
| 944 | // this may include dumping the output to stdout |
| 945 | fatal("TODO: implement `zig cc` when using it as a preprocessor", .{}); |
| 946 | } |
| 947 | |
| 518 | 948 | while (watch) { |
| 519 | 949 | try stderr.print("🦎 ", .{}); |
| 520 | 950 | if (output_mode == .Exe) { |
| ... | ... | @@ -562,7 +992,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo |
| 562 | 992 | }); |
| 563 | 993 | } |
| 564 | 994 | } else { |
| 565 | | std.log.scoped(.compiler).info("Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms}); |
| 995 | info("Update completed in {} ms", .{update_nanos / std.time.ns_per_ms}); |
| 566 | 996 | } |
| 567 | 997 | |
| 568 | 998 | if (zir_out_path) |zop| { |
| ... | ... | @@ -631,8 +1061,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 631 | 1061 | process.exit(0); |
| 632 | 1062 | } else if (mem.eql(u8, arg, "--color")) { |
| 633 | 1063 | if (i + 1 >= args.len) { |
| 634 | | std.debug.print("expected [auto|on|off] after --color\n", .{}); |
| 635 | | process.exit(1); |
| 1064 | fatal("expected [auto|on|off] after --color", .{}); |
| 636 | 1065 | } |
| 637 | 1066 | i += 1; |
| 638 | 1067 | const next_arg = args[i]; |
| ... | ... | @@ -643,16 +1072,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 643 | 1072 | } else if (mem.eql(u8, next_arg, "off")) { |
| 644 | 1073 | color = .Off; |
| 645 | 1074 | } else { |
| 646 | | std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); |
| 647 | | process.exit(1); |
| 1075 | fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); |
| 648 | 1076 | } |
| 649 | 1077 | } else if (mem.eql(u8, arg, "--stdin")) { |
| 650 | 1078 | stdin_flag = true; |
| 651 | 1079 | } else if (mem.eql(u8, arg, "--check")) { |
| 652 | 1080 | check_flag = true; |
| 653 | 1081 | } else { |
| 654 | | std.debug.print("unrecognized parameter: '{}'", .{arg}); |
| 655 | | process.exit(1); |
| 1082 | fatal("unrecognized parameter: '{}'", .{arg}); |
| 656 | 1083 | } |
| 657 | 1084 | } else { |
| 658 | 1085 | try input_files.append(arg); |
| ... | ... | @@ -662,8 +1089,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 662 | 1089 | |
| 663 | 1090 | if (stdin_flag) { |
| 664 | 1091 | if (input_files.items.len != 0) { |
| 665 | | std.debug.print("cannot use --stdin with positional arguments\n", .{}); |
| 666 | | process.exit(1); |
| 1092 | fatal("cannot use --stdin with positional arguments", .{}); |
| 667 | 1093 | } |
| 668 | 1094 | |
| 669 | 1095 | const stdin = io.getStdIn().inStream(); |
| ... | ... | @@ -672,8 +1098,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 672 | 1098 | defer gpa.free(source_code); |
| 673 | 1099 | |
| 674 | 1100 | const tree = std.zig.parse(gpa, source_code) catch |err| { |
| 675 | | std.debug.print("error parsing stdin: {}\n", .{err}); |
| 676 | | process.exit(1); |
| 1101 | fatal("error parsing stdin: {}", .{err}); |
| 677 | 1102 | }; |
| 678 | 1103 | defer tree.deinit(); |
| 679 | 1104 | |
| ... | ... | @@ -695,8 +1120,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 695 | 1120 | } |
| 696 | 1121 | |
| 697 | 1122 | if (input_files.items.len == 0) { |
| 698 | | std.debug.print("expected at least one source file argument\n", .{}); |
| 699 | | process.exit(1); |
| 1123 | fatal("expected at least one source file argument", .{}); |
| 700 | 1124 | } |
| 701 | 1125 | |
| 702 | 1126 | var fmt = Fmt{ |
| ... | ... | @@ -712,8 +1136,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 712 | 1136 | for (input_files.span()) |file_path| { |
| 713 | 1137 | // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. |
| 714 | 1138 | const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { |
| 715 | | std.debug.print("unable to open '{}': {}\n", .{ file_path, err }); |
| 716 | | process.exit(1); |
| 1139 | fatal("unable to open '{}': {}", .{ file_path, err }); |
| 717 | 1140 | }; |
| 718 | 1141 | defer gpa.free(real_path); |
| 719 | 1142 | |
| ... | ... | @@ -752,7 +1175,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_ |
| 752 | 1175 | fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { |
| 753 | 1176 | error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), |
| 754 | 1177 | else => { |
| 755 | | std.debug.print("unable to format '{}': {}\n", .{ file_path, err }); |
| 1178 | warn("unable to format '{}': {}", .{ file_path, err }); |
| 756 | 1179 | fmt.any_error = true; |
| 757 | 1180 | return; |
| 758 | 1181 | }, |
| ... | ... | @@ -783,7 +1206,7 @@ fn fmtPathDir( |
| 783 | 1206 | try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); |
| 784 | 1207 | } else { |
| 785 | 1208 | fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { |
| 786 | | std.debug.print("unable to format '{}': {}\n", .{ full_path, err }); |
| 1209 | warn("unable to format '{}': {}", .{ full_path, err }); |
| 787 | 1210 | fmt.any_error = true; |
| 788 | 1211 | return; |
| 789 | 1212 | }; |
| ... | ... | @@ -841,6 +1264,7 @@ fn fmtPathFile( |
| 841 | 1264 | if (check_mode) { |
| 842 | 1265 | const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); |
| 843 | 1266 | if (anything_changed) { |
| 1267 | // TODO this should output to stdout instead of stderr. |
| 844 | 1268 | std.debug.print("{}\n", .{file_path}); |
| 845 | 1269 | fmt.any_error = true; |
| 846 | 1270 | } |
| ... | ... | @@ -858,6 +1282,7 @@ fn fmtPathFile( |
| 858 | 1282 | |
| 859 | 1283 | try af.file.writeAll(fmt.out_buffer.items); |
| 860 | 1284 | try af.finish(); |
| 1285 | // TODO this should output to stdout instead of stderr. |
| 861 | 1286 | std.debug.print("{}\n", .{file_path}); |
| 862 | 1287 | } |
| 863 | 1288 | } |
| ... | ... | @@ -925,3 +1350,350 @@ pub const info_zen = |
| 925 | 1350 | \\ |
| 926 | 1351 | \\ |
| 927 | 1352 | ; |
| 1353 | |
| 1354 | const FileExt = enum { |
| 1355 | c, |
| 1356 | cpp, |
| 1357 | h, |
| 1358 | ll, |
| 1359 | bc, |
| 1360 | assembly, |
| 1361 | unknown, |
| 1362 | }; |
| 1363 | |
| 1364 | fn hasCExt(filename: []const u8) bool { |
| 1365 | return mem.endsWith(u8, filename, ".c"); |
| 1366 | } |
| 1367 | |
| 1368 | fn hasCppExt(filename: []const u8) bool { |
| 1369 | return mem.endsWith(u8, filename, ".C") or |
| 1370 | mem.endsWith(u8, filename, ".cc") or |
| 1371 | mem.endsWith(u8, filename, ".cpp") or |
| 1372 | mem.endsWith(u8, filename, ".cxx"); |
| 1373 | } |
| 1374 | |
| 1375 | fn hasAsmExt(filename: []const u8) bool { |
| 1376 | return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); |
| 1377 | } |
| 1378 | |
| 1379 | fn classify_file_ext(filename: []const u8) FileExt { |
| 1380 | if (hasCExt(filename)) { |
| 1381 | return .c; |
| 1382 | } else if (hasCppExt(filename)) { |
| 1383 | return .cpp; |
| 1384 | } else if (mem.endsWith(u8, filename, ".ll")) { |
| 1385 | return .ll; |
| 1386 | } else if (mem.endsWith(u8, filename, ".bc")) { |
| 1387 | return .bc; |
| 1388 | } else if (hasAsmExt(filename)) { |
| 1389 | return .assembly; |
| 1390 | } else if (mem.endsWith(u8, filename, ".h")) { |
| 1391 | return .h; |
| 1392 | } else { |
| 1393 | // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z |
| 1394 | return .unknown; |
| 1395 | } |
| 1396 | } |
| 1397 | |
| 1398 | extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; |
| 1399 | |
| 1400 | /// TODO make it so the return value can be !noreturn |
| 1401 | fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} { |
| 1402 | // Convert the args to the format Clang expects. |
| 1403 | const argv = try arena.alloc(?[*:0]u8, args.len + 1); |
| 1404 | for (args) |arg, i| { |
| 1405 | argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. |
| 1406 | } |
| 1407 | argv[args.len] = null; |
| 1408 | const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr); |
| 1409 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); |
| 1410 | } |
| 1411 | |
| 1412 | const clang_args = @import("clang_options.zig").list; |
| 1413 | |
| 1414 | pub const ClangArgIterator = struct { |
| 1415 | has_next: bool, |
| 1416 | zig_equivalent: ZigEquivalent, |
| 1417 | only_arg: []const u8, |
| 1418 | second_arg: []const u8, |
| 1419 | other_args: []const []const u8, |
| 1420 | argv: []const []const u8, |
| 1421 | next_index: usize, |
| 1422 | root_args: ?*Args, |
| 1423 | allocator: *Allocator, |
| 1424 | |
| 1425 | pub const ZigEquivalent = enum { |
| 1426 | target, |
| 1427 | o, |
| 1428 | c, |
| 1429 | other, |
| 1430 | positional, |
| 1431 | l, |
| 1432 | ignore, |
| 1433 | driver_punt, |
| 1434 | pic, |
| 1435 | no_pic, |
| 1436 | nostdlib, |
| 1437 | nostdlib_cpp, |
| 1438 | shared, |
| 1439 | rdynamic, |
| 1440 | wl, |
| 1441 | pp_or_asm, |
| 1442 | optimize, |
| 1443 | debug, |
| 1444 | sanitize, |
| 1445 | linker_script, |
| 1446 | verbose_cmds, |
| 1447 | for_linker, |
| 1448 | linker_input_z, |
| 1449 | lib_dir, |
| 1450 | mcpu, |
| 1451 | dep_file, |
| 1452 | framework_dir, |
| 1453 | framework, |
| 1454 | nostdlibinc, |
| 1455 | }; |
| 1456 | |
| 1457 | const Args = struct { |
| 1458 | next_index: usize, |
| 1459 | argv: []const []const u8, |
| 1460 | }; |
| 1461 | |
| 1462 | fn init(allocator: *Allocator, argv: []const []const u8) ClangArgIterator { |
| 1463 | return .{ |
| 1464 | .next_index = 2, // `zig cc foo` this points to `foo` |
| 1465 | .has_next = argv.len > 2, |
| 1466 | .zig_equivalent = undefined, |
| 1467 | .only_arg = undefined, |
| 1468 | .second_arg = undefined, |
| 1469 | .other_args = undefined, |
| 1470 | .argv = argv, |
| 1471 | .root_args = null, |
| 1472 | .allocator = allocator, |
| 1473 | }; |
| 1474 | } |
| 1475 | |
| 1476 | fn next(self: *ClangArgIterator) !void { |
| 1477 | assert(self.has_next); |
| 1478 | assert(self.next_index < self.argv.len); |
| 1479 | // In this state we know that the parameter we are looking at is a root parameter |
| 1480 | // rather than an argument to a parameter. |
| 1481 | // We adjust the len below when necessary. |
| 1482 | self.other_args = (self.argv.ptr + self.next_index)[0..1]; |
| 1483 | var arg = mem.span(self.argv[self.next_index]); |
| 1484 | self.incrementArgIndex(); |
| 1485 | |
| 1486 | if (mem.startsWith(u8, arg, "@")) { |
| 1487 | if (self.root_args != null) return error.NestedResponseFile; |
| 1488 | |
| 1489 | // This is a "compiler response file". We must parse the file and treat its |
| 1490 | // contents as command line parameters. |
| 1491 | const allocator = self.allocator; |
| 1492 | const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit |
| 1493 | const resp_file_path = arg[1..]; |
| 1494 | const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { |
| 1495 | fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) }); |
| 1496 | }; |
| 1497 | defer allocator.free(resp_contents); |
| 1498 | // TODO is there a specification for this file format? Let's find it and make this parsing more robust |
| 1499 | // at the very least I'm guessing this needs to handle quotes and `#` comments. |
| 1500 | var it = mem.tokenize(resp_contents, " \t\r\n"); |
| 1501 | var resp_arg_list = std.ArrayList([]const u8).init(allocator); |
| 1502 | defer resp_arg_list.deinit(); |
| 1503 | { |
| 1504 | errdefer { |
| 1505 | for (resp_arg_list.span()) |item| { |
| 1506 | allocator.free(mem.span(item)); |
| 1507 | } |
| 1508 | } |
| 1509 | while (it.next()) |token| { |
| 1510 | const dupe_token = try mem.dupeZ(allocator, u8, token); |
| 1511 | errdefer allocator.free(dupe_token); |
| 1512 | try resp_arg_list.append(dupe_token); |
| 1513 | } |
| 1514 | const args = try allocator.create(Args); |
| 1515 | errdefer allocator.destroy(args); |
| 1516 | args.* = .{ |
| 1517 | .next_index = self.next_index, |
| 1518 | .argv = self.argv, |
| 1519 | }; |
| 1520 | self.root_args = args; |
| 1521 | } |
| 1522 | const resp_arg_slice = resp_arg_list.toOwnedSlice(); |
| 1523 | self.next_index = 0; |
| 1524 | self.argv = resp_arg_slice; |
| 1525 | |
| 1526 | if (resp_arg_slice.len == 0) { |
| 1527 | self.resolveRespFileArgs(); |
| 1528 | return; |
| 1529 | } |
| 1530 | |
| 1531 | self.has_next = true; |
| 1532 | self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary. |
| 1533 | arg = mem.span(self.argv[self.next_index]); |
| 1534 | self.incrementArgIndex(); |
| 1535 | } |
| 1536 | if (!mem.startsWith(u8, arg, "-")) { |
| 1537 | self.zig_equivalent = .positional; |
| 1538 | self.only_arg = arg; |
| 1539 | return; |
| 1540 | } |
| 1541 | |
| 1542 | find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) { |
| 1543 | .flag => { |
| 1544 | const prefix_len = clang_arg.matchEql(arg); |
| 1545 | if (prefix_len > 0) { |
| 1546 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 1547 | self.only_arg = arg[prefix_len..]; |
| 1548 | |
| 1549 | break :find_clang_arg; |
| 1550 | } |
| 1551 | }, |
| 1552 | .joined, .comma_joined => { |
| 1553 | // joined example: --target=foo |
| 1554 | // comma_joined example: -Wl,-soname,libsoundio.so.2 |
| 1555 | const prefix_len = clang_arg.matchStartsWith(arg); |
| 1556 | if (prefix_len != 0) { |
| 1557 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 1558 | self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part. |
| 1559 | |
| 1560 | break :find_clang_arg; |
| 1561 | } |
| 1562 | }, |
| 1563 | .joined_or_separate => { |
| 1564 | // Examples: `-lfoo`, `-l foo` |
| 1565 | const prefix_len = clang_arg.matchStartsWith(arg); |
| 1566 | if (prefix_len == arg.len) { |
| 1567 | if (self.next_index >= self.argv.len) { |
| 1568 | fatal("Expected parameter after '{}'", .{arg}); |
| 1569 | } |
| 1570 | self.only_arg = self.argv[self.next_index]; |
| 1571 | self.incrementArgIndex(); |
| 1572 | self.other_args.len += 1; |
| 1573 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 1574 | |
| 1575 | break :find_clang_arg; |
| 1576 | } else if (prefix_len != 0) { |
| 1577 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 1578 | self.only_arg = arg[prefix_len..]; |
| 1579 | |
| 1580 | break :find_clang_arg; |
| 1581 | } |
| 1582 | }, |
| 1583 | .joined_and_separate => { |
| 1584 | // Example: `-Xopenmp-target=riscv64-linux-unknown foo` |
| 1585 | const prefix_len = clang_arg.matchStartsWith(arg); |
| 1586 | if (prefix_len != 0) { |
| 1587 | self.only_arg = arg[prefix_len..]; |
| 1588 | if (self.next_index >= self.argv.len) { |
| 1589 | fatal("Expected parameter after '{}'", .{arg}); |
| 1590 | } |
| 1591 | self.second_arg = self.argv[self.next_index]; |
| 1592 | self.incrementArgIndex(); |
| 1593 | self.other_args.len += 1; |
| 1594 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 1595 | break :find_clang_arg; |
| 1596 | } |
| 1597 | }, |
| 1598 | .separate => if (clang_arg.matchEql(arg) > 0) { |
| 1599 | if (self.next_index >= self.argv.len) { |
| 1600 | fatal("Expected parameter after '{}'", .{arg}); |
| 1601 | } |
| 1602 | self.only_arg = self.argv[self.next_index]; |
| 1603 | self.incrementArgIndex(); |
| 1604 | self.other_args.len += 1; |
| 1605 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 1606 | break :find_clang_arg; |
| 1607 | }, |
| 1608 | .remaining_args_joined => { |
| 1609 | const prefix_len = clang_arg.matchStartsWith(arg); |
| 1610 | if (prefix_len != 0) { |
| 1611 | @panic("TODO"); |
| 1612 | } |
| 1613 | }, |
| 1614 | .multi_arg => if (clang_arg.matchEql(arg) > 0) { |
| 1615 | @panic("TODO"); |
| 1616 | }, |
| 1617 | } |
| 1618 | else { |
| 1619 | fatal("Unknown Clang option: '{}'", .{arg}); |
| 1620 | } |
| 1621 | } |
| 1622 | |
| 1623 | fn incrementArgIndex(self: *ClangArgIterator) void { |
| 1624 | self.next_index += 1; |
| 1625 | self.resolveRespFileArgs(); |
| 1626 | } |
| 1627 | |
| 1628 | fn resolveRespFileArgs(self: *ClangArgIterator) void { |
| 1629 | const allocator = self.allocator; |
| 1630 | if (self.next_index >= self.argv.len) { |
| 1631 | if (self.root_args) |root_args| { |
| 1632 | self.next_index = root_args.next_index; |
| 1633 | self.argv = root_args.argv; |
| 1634 | |
| 1635 | allocator.destroy(root_args); |
| 1636 | self.root_args = null; |
| 1637 | } |
| 1638 | if (self.next_index >= self.argv.len) { |
| 1639 | self.has_next = false; |
| 1640 | } |
| 1641 | } |
| 1642 | } |
| 1643 | }; |
| 1644 | |
| 1645 | fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { |
| 1646 | if (ignore_case) { |
| 1647 | return std.ascii.eqlIgnoreCase(a, b); |
| 1648 | } else { |
| 1649 | return mem.eql(u8, a, b); |
| 1650 | } |
| 1651 | } |
| 1652 | |
| 1653 | fn is_libc_lib_name(target: std.Target, name: []const u8) bool { |
| 1654 | const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; |
| 1655 | |
| 1656 | if (eqlIgnoreCase(ignore_case, name, "c")) |
| 1657 | return true; |
| 1658 | |
| 1659 | if (target.isMinGW()) { |
| 1660 | if (eqlIgnoreCase(ignore_case, name, "m")) |
| 1661 | return true; |
| 1662 | |
| 1663 | return false; |
| 1664 | } |
| 1665 | |
| 1666 | if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) { |
| 1667 | if (eqlIgnoreCase(ignore_case, name, "m")) |
| 1668 | return true; |
| 1669 | if (eqlIgnoreCase(ignore_case, name, "rt")) |
| 1670 | return true; |
| 1671 | if (eqlIgnoreCase(ignore_case, name, "pthread")) |
| 1672 | return true; |
| 1673 | if (eqlIgnoreCase(ignore_case, name, "crypt")) |
| 1674 | return true; |
| 1675 | if (eqlIgnoreCase(ignore_case, name, "util")) |
| 1676 | return true; |
| 1677 | if (eqlIgnoreCase(ignore_case, name, "xnet")) |
| 1678 | return true; |
| 1679 | if (eqlIgnoreCase(ignore_case, name, "resolv")) |
| 1680 | return true; |
| 1681 | if (eqlIgnoreCase(ignore_case, name, "dl")) |
| 1682 | return true; |
| 1683 | if (eqlIgnoreCase(ignore_case, name, "util")) |
| 1684 | return true; |
| 1685 | } |
| 1686 | |
| 1687 | if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System")) |
| 1688 | return true; |
| 1689 | |
| 1690 | return false; |
| 1691 | } |
| 1692 | |
| 1693 | fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool { |
| 1694 | const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; |
| 1695 | |
| 1696 | return eqlIgnoreCase(ignore_case, name, "c++") or |
| 1697 | eqlIgnoreCase(ignore_case, name, "stdc++") or |
| 1698 | eqlIgnoreCase(ignore_case, name, "c++abi"); |
| 1699 | } |