| ... | ... | @@ -104,6 +104,7 @@ const normal_usage = |
| 104 | 104 | \\ lib Use Zig as a drop-in lib.exe |
| 105 | 105 | \\ ranlib Use Zig as a drop-in ranlib |
| 106 | 106 | \\ objcopy Use Zig as a drop-in objcopy |
| 107 | \\ rc Use Zig as a drop-in rc.exe |
| 107 | 108 | \\ |
| 108 | 109 | \\ env Print lib path, std path, cache directory, and version |
| 109 | 110 | \\ help Print this help and exit |
| ... | ... | @@ -300,6 +301,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 300 | 301 | return buildOutputType(gpa, arena, args, .cpp); |
| 301 | 302 | } else if (mem.eql(u8, cmd, "translate-c")) { |
| 302 | 303 | return buildOutputType(gpa, arena, args, .translate_c); |
| 304 | } else if (mem.eql(u8, cmd, "rc")) { |
| 305 | return cmdRc(gpa, arena, args[1..]); |
| 303 | 306 | } else if (mem.eql(u8, cmd, "fmt")) { |
| 304 | 307 | return cmdFmt(gpa, arena, cmd_args); |
| 305 | 308 | } else if (mem.eql(u8, cmd, "objcopy")) { |
| ... | ... | @@ -4372,6 +4375,270 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati |
| 4372 | 4375 | } |
| 4373 | 4376 | } |
| 4374 | 4377 | |
| 4378 | fn cmdRc(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4379 | const resinator = @import("resinator.zig"); |
| 4380 | |
| 4381 | const stderr = std.io.getStdErr(); |
| 4382 | const stderr_config = std.io.tty.detectConfig(stderr); |
| 4383 | |
| 4384 | var options = options: { |
| 4385 | var cli_diagnostics = resinator.cli.Diagnostics.init(gpa); |
| 4386 | defer cli_diagnostics.deinit(); |
| 4387 | var options = resinator.cli.parse(gpa, args, &cli_diagnostics) catch |err| switch (err) { |
| 4388 | error.ParseError => { |
| 4389 | cli_diagnostics.renderToStdErr(args, stderr_config); |
| 4390 | process.exit(1); |
| 4391 | }, |
| 4392 | else => |e| return e, |
| 4393 | }; |
| 4394 | try options.maybeAppendRC(std.fs.cwd()); |
| 4395 | |
| 4396 | // print any warnings/notes |
| 4397 | cli_diagnostics.renderToStdErr(args, stderr_config); |
| 4398 | // If there was something printed, then add an extra newline separator |
| 4399 | // so that there is a clear separation between the cli diagnostics and whatever |
| 4400 | // gets printed after |
| 4401 | if (cli_diagnostics.errors.items.len > 0) { |
| 4402 | std.debug.print("\n", .{}); |
| 4403 | } |
| 4404 | break :options options; |
| 4405 | }; |
| 4406 | defer options.deinit(); |
| 4407 | |
| 4408 | if (options.print_help_and_exit) { |
| 4409 | try resinator.cli.writeUsage(stderr.writer(), "zig rc"); |
| 4410 | return; |
| 4411 | } |
| 4412 | |
| 4413 | const stdout_writer = std.io.getStdOut().writer(); |
| 4414 | if (options.verbose) { |
| 4415 | try options.dumpVerbose(stdout_writer); |
| 4416 | try stdout_writer.writeByte('\n'); |
| 4417 | } |
| 4418 | |
| 4419 | var full_input = full_input: { |
| 4420 | if (options.preprocess != .no) { |
| 4421 | if (!build_options.have_llvm) { |
| 4422 | fatal("clang not available: compiler built without LLVM extensions", .{}); |
| 4423 | } |
| 4424 | |
| 4425 | var argv = std.ArrayList([]const u8).init(gpa); |
| 4426 | defer argv.deinit(); |
| 4427 | |
| 4428 | const self_exe_path = try introspect.findZigExePath(arena); |
| 4429 | var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { |
| 4430 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to find zig installation directory: {s}", .{@errorName(err)}); |
| 4431 | process.exit(1); |
| 4432 | }; |
| 4433 | defer zig_lib_directory.handle.close(); |
| 4434 | |
| 4435 | const include_args = detectRcIncludeDirs(arena, zig_lib_directory.path.?, options.auto_includes) catch |err| { |
| 4436 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to detect system include directories: {s}", .{@errorName(err)}); |
| 4437 | process.exit(1); |
| 4438 | }; |
| 4439 | |
| 4440 | try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" }); |
| 4441 | |
| 4442 | const clang_target = clang_target: { |
| 4443 | if (include_args.target_abi) |abi| { |
| 4444 | break :clang_target try std.fmt.allocPrint(arena, "x86_64-unknown-windows-{s}", .{abi}); |
| 4445 | } |
| 4446 | break :clang_target "x86_64-unknown-windows"; |
| 4447 | }; |
| 4448 | try resinator.preprocess.appendClangArgs(arena, &argv, options, .{ |
| 4449 | .clang_target = clang_target, |
| 4450 | .system_include_paths = include_args.include_paths, |
| 4451 | .needs_gnu_workaround = if (include_args.target_abi) |abi| std.mem.eql(u8, abi, "gnu") else false, |
| 4452 | .nostdinc = true, |
| 4453 | }); |
| 4454 | |
| 4455 | try argv.append(options.input_filename); |
| 4456 | |
| 4457 | if (options.verbose) { |
| 4458 | try stdout_writer.writeAll("Preprocessor: zig clang\n"); |
| 4459 | for (argv.items[0 .. argv.items.len - 1]) |arg| { |
| 4460 | try stdout_writer.print("{s} ", .{arg}); |
| 4461 | } |
| 4462 | try stdout_writer.print("{s}\n\n", .{argv.items[argv.items.len - 1]}); |
| 4463 | } |
| 4464 | |
| 4465 | if (std.process.can_spawn) { |
| 4466 | var result = std.ChildProcess.exec(.{ |
| 4467 | .allocator = gpa, |
| 4468 | .argv = argv.items, |
| 4469 | .max_output_bytes = std.math.maxInt(u32), |
| 4470 | }) catch |err| { |
| 4471 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to spawn preprocessor child process: {s}", .{@errorName(err)}); |
| 4472 | process.exit(1); |
| 4473 | }; |
| 4474 | errdefer gpa.free(result.stdout); |
| 4475 | defer gpa.free(result.stderr); |
| 4476 | |
| 4477 | switch (result.term) { |
| 4478 | .Exited => |code| { |
| 4479 | if (code != 0) { |
| 4480 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "the preprocessor failed with exit code {}:", .{code}); |
| 4481 | try stderr.writeAll(result.stderr); |
| 4482 | try stderr.writeAll("\n"); |
| 4483 | process.exit(1); |
| 4484 | } |
| 4485 | }, |
| 4486 | .Signal, .Stopped, .Unknown => { |
| 4487 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "the preprocessor terminated unexpectedly ({s}):", .{@tagName(result.term)}); |
| 4488 | try stderr.writeAll(result.stderr); |
| 4489 | try stderr.writeAll("\n"); |
| 4490 | process.exit(1); |
| 4491 | }, |
| 4492 | } |
| 4493 | |
| 4494 | break :full_input result.stdout; |
| 4495 | } else { |
| 4496 | // need to use an intermediate file |
| 4497 | const rand_int = std.crypto.random.int(u64); |
| 4498 | const preprocessed_path = try std.fmt.allocPrint(gpa, "resinator{x}.rcpp", .{rand_int}); |
| 4499 | defer gpa.free(preprocessed_path); |
| 4500 | defer std.fs.cwd().deleteFile(preprocessed_path) catch {}; |
| 4501 | |
| 4502 | try argv.appendSlice(&.{ "-o", preprocessed_path }); |
| 4503 | const exit_code = try clangMain(arena, argv.items); |
| 4504 | if (exit_code != 0) { |
| 4505 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "the preprocessor failed with exit code {}:", .{exit_code}); |
| 4506 | process.exit(1); |
| 4507 | } |
| 4508 | break :full_input std.fs.cwd().readFileAlloc(gpa, preprocessed_path, std.math.maxInt(usize)) catch |err| { |
| 4509 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to read preprocessed file path '{s}': {s}", .{ preprocessed_path, @errorName(err) }); |
| 4510 | process.exit(1); |
| 4511 | }; |
| 4512 | } |
| 4513 | } else { |
| 4514 | break :full_input std.fs.cwd().readFileAlloc(gpa, options.input_filename, std.math.maxInt(usize)) catch |err| { |
| 4515 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) }); |
| 4516 | process.exit(1); |
| 4517 | }; |
| 4518 | } |
| 4519 | }; |
| 4520 | defer gpa.free(full_input); |
| 4521 | |
| 4522 | if (options.preprocess == .only) { |
| 4523 | std.fs.cwd().writeFile(options.output_filename, full_input) catch |err| { |
| 4524 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to write output file '{s}': {s}", .{ options.output_filename, @errorName(err) }); |
| 4525 | process.exit(1); |
| 4526 | }; |
| 4527 | return cleanExit(); |
| 4528 | } |
| 4529 | |
| 4530 | var mapping_results = try resinator.source_mapping.parseAndRemoveLineCommands(gpa, full_input, full_input, .{ .initial_filename = options.input_filename }); |
| 4531 | defer mapping_results.mappings.deinit(gpa); |
| 4532 | |
| 4533 | var final_input = resinator.comments.removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings); |
| 4534 | |
| 4535 | var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| { |
| 4536 | try resinator.utils.renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) }); |
| 4537 | process.exit(1); |
| 4538 | }; |
| 4539 | var output_file_closed = false; |
| 4540 | defer if (!output_file_closed) output_file.close(); |
| 4541 | |
| 4542 | var diagnostics = resinator.errors.Diagnostics.init(gpa); |
| 4543 | defer diagnostics.deinit(); |
| 4544 | |
| 4545 | var output_buffered_stream = std.io.bufferedWriter(output_file.writer()); |
| 4546 | |
| 4547 | resinator.compile.compile(gpa, final_input, output_buffered_stream.writer(), .{ |
| 4548 | .cwd = std.fs.cwd(), |
| 4549 | .diagnostics = &diagnostics, |
| 4550 | .source_mappings = &mapping_results.mappings, |
| 4551 | .dependencies_list = null, |
| 4552 | .ignore_include_env_var = options.ignore_include_env_var, |
| 4553 | .extra_include_paths = options.extra_include_paths.items, |
| 4554 | .default_language_id = options.default_language_id, |
| 4555 | .default_code_page = options.default_code_page orelse .windows1252, |
| 4556 | .verbose = options.verbose, |
| 4557 | .null_terminate_string_table_strings = options.null_terminate_string_table_strings, |
| 4558 | .max_string_literal_codepoints = options.max_string_literal_codepoints, |
| 4559 | .silent_duplicate_control_ids = options.silent_duplicate_control_ids, |
| 4560 | .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page, |
| 4561 | }) catch |err| switch (err) { |
| 4562 | error.ParseError, error.CompileError => { |
| 4563 | diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings); |
| 4564 | // Delete the output file on error |
| 4565 | output_file.close(); |
| 4566 | output_file_closed = true; |
| 4567 | // Failing to delete is not really a big deal, so swallow any errors |
| 4568 | std.fs.cwd().deleteFile(options.output_filename) catch {}; |
| 4569 | process.exit(1); |
| 4570 | }, |
| 4571 | else => |e| return e, |
| 4572 | }; |
| 4573 | |
| 4574 | try output_buffered_stream.flush(); |
| 4575 | |
| 4576 | // print any warnings/notes |
| 4577 | diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings); |
| 4578 | |
| 4579 | return cleanExit(); |
| 4580 | } |
| 4581 | |
| 4582 | const RcIncludeArgs = struct { |
| 4583 | include_paths: []const []const u8 = &.{}, |
| 4584 | target_abi: ?[]const u8 = null, |
| 4585 | }; |
| 4586 | |
| 4587 | fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: @import("resinator.zig").cli.Options.AutoIncludes) !RcIncludeArgs { |
| 4588 | if (auto_includes == .none) return .{}; |
| 4589 | var cur_includes = auto_includes; |
| 4590 | if (builtin.target.os.tag != .windows) { |
| 4591 | switch (cur_includes) { |
| 4592 | // MSVC can't be found when the host isn't Windows, so short-circuit. |
| 4593 | .msvc => return error.WindowsSdkNotFound, |
| 4594 | // Skip straight to gnu since we won't be able to detect MSVC on non-Windows hosts. |
| 4595 | .any => cur_includes = .gnu, |
| 4596 | .gnu => {}, |
| 4597 | .none => unreachable, |
| 4598 | } |
| 4599 | } |
| 4600 | while (true) { |
| 4601 | switch (cur_includes) { |
| 4602 | .any, .msvc => { |
| 4603 | const cross_target = std.zig.CrossTarget.parse(.{ .arch_os_abi = "native-windows-msvc" }) catch unreachable; |
| 4604 | const target = cross_target.toTarget(); |
| 4605 | const is_native_abi = cross_target.isNativeAbi(); |
| 4606 | const detected_libc = Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| { |
| 4607 | if (cur_includes == .any) { |
| 4608 | // fall back to mingw |
| 4609 | cur_includes = .gnu; |
| 4610 | continue; |
| 4611 | } |
| 4612 | return err; |
| 4613 | }; |
| 4614 | if (detected_libc.libc_include_dir_list.len == 0) { |
| 4615 | if (cur_includes == .any) { |
| 4616 | // fall back to mingw |
| 4617 | cur_includes = .gnu; |
| 4618 | continue; |
| 4619 | } |
| 4620 | return error.WindowsSdkNotFound; |
| 4621 | } |
| 4622 | return .{ |
| 4623 | .include_paths = detected_libc.libc_include_dir_list, |
| 4624 | .target_abi = "msvc", |
| 4625 | }; |
| 4626 | }, |
| 4627 | .gnu => { |
| 4628 | const cross_target = std.zig.CrossTarget.parse(.{ .arch_os_abi = "native-windows-gnu" }) catch unreachable; |
| 4629 | const target = cross_target.toTarget(); |
| 4630 | const is_native_abi = cross_target.isNativeAbi(); |
| 4631 | const detected_libc = try Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null); |
| 4632 | return .{ |
| 4633 | .include_paths = detected_libc.libc_include_dir_list, |
| 4634 | .target_abi = "gnu", |
| 4635 | }; |
| 4636 | }, |
| 4637 | .none => unreachable, |
| 4638 | } |
| 4639 | } |
| 4640 | } |
| 4641 | |
| 4375 | 4642 | pub const usage_libc = |
| 4376 | 4643 | \\Usage: zig libc |
| 4377 | 4644 | \\ |