@@ -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
\\
0 commit comments