authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-23 13:33:43-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-23 14:45:14-04:00
log29b82d20a4534fc7e3393c21ad637d44d20449a2
treeeea4bdabfc738bb3f058e042a972c98980f7eeac
parent35c1d8cefc3a77b867ecc2da999162781099b4c6

zig build: linkSystemLibrary integrates with pkg-config

* add -D CLI option for setting C macros * add std.ascii.allocLowerString * add std.ascii.eqlIgnoreCase * add std.ascii.indexOfIgnoreCasePos * add std.ascii.indexOfIgnoreCase

3 files changed, 289 insertions(+), 23 deletions(-)

src/main.cpp+7
...@@ -91,6 +91,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {...@@ -91,6 +91,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
91 " --override-std-dir [arg] override path to Zig standard library\n"91 " --override-std-dir [arg] override path to Zig standard library\n"
92 " --override-lib-dir [arg] override path to Zig lib library\n"92 " --override-lib-dir [arg] override path to Zig lib library\n"
93 " -ffunction-sections places each function in a separate section\n"93 " -ffunction-sections places each function in a separate section\n"
94 " -D[macro]=[value] define C [macro] to [value] (1 if [value] omitted)\n"
94 "\n"95 "\n"
95 "Link Options:\n"96 "Link Options:\n"
96 " --bundle-compiler-rt for static libraries, include compiler-rt symbols\n"97 " --bundle-compiler-rt for static libraries, include compiler-rt symbols\n"
...@@ -691,6 +692,9 @@ int main(int argc, char **argv) {...@@ -691,6 +692,9 @@ int main(int argc, char **argv) {
691 bundle_compiler_rt = true;692 bundle_compiler_rt = true;
692 } else if (strcmp(arg, "--test-cmd-bin") == 0) {693 } else if (strcmp(arg, "--test-cmd-bin") == 0) {
693 test_exec_args.append(nullptr);694 test_exec_args.append(nullptr);
695 } else if (arg[1] == 'D' && arg[2] != 0) {
696 clang_argv.append("-D");
697 clang_argv.append(&arg[2]);
694 } else if (arg[1] == 'L' && arg[2] != 0) {698 } else if (arg[1] == 'L' && arg[2] != 0) {
695 // alias for --library-path699 // alias for --library-path
696 lib_dirs.append(&arg[2]);700 lib_dirs.append(&arg[2]);
...@@ -769,6 +773,9 @@ int main(int argc, char **argv) {...@@ -769,6 +773,9 @@ int main(int argc, char **argv) {
769 dynamic_linker = buf_create_from_str(argv[i]);773 dynamic_linker = buf_create_from_str(argv[i]);
770 } else if (strcmp(arg, "--libc") == 0) {774 } else if (strcmp(arg, "--libc") == 0) {
771 libc_txt = argv[i];775 libc_txt = argv[i];
776 } else if (strcmp(arg, "-D") == 0) {
777 clang_argv.append("-D");
778 clang_argv.append(argv[i]);
772 } else if (strcmp(arg, "-isystem") == 0) {779 } else if (strcmp(arg, "-isystem") == 0) {
773 clang_argv.append("-isystem");780 clang_argv.append("-isystem");
774 clang_argv.append(argv[i]);781 clang_argv.append(argv[i]);
std/ascii.zig+58-2
...@@ -7,6 +7,8 @@...@@ -7,6 +7,8 @@
7//7//
8// https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/USASCII_code_chart.png/1200px-USASCII_code_chart.png8// https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/USASCII_code_chart.png/1200px-USASCII_code_chart.png
99
10const std = @import("std");
11
10const tIndex = enum(u3) {12const tIndex = enum(u3) {
11 Alpha,13 Alpha,
12 Hex,14 Hex,
...@@ -25,7 +27,6 @@ const tIndex = enum(u3) {...@@ -25,7 +27,6 @@ const tIndex = enum(u3) {
25const combinedTable = init: {27const combinedTable = init: {
26 comptime var table: [256]u8 = undefined;28 comptime var table: [256]u8 = undefined;
2729
28 const std = @import("std");
29 const mem = std.mem;30 const mem = std.mem;
3031
31 const alpha = [_]u1{32 const alpha = [_]u1{
...@@ -215,7 +216,6 @@ pub fn toLower(c: u8) u8 {...@@ -215,7 +216,6 @@ pub fn toLower(c: u8) u8 {
215}216}
216217
217test "ascii character classes" {218test "ascii character classes" {
218 const std = @import("std");
219 const testing = std.testing;219 const testing = std.testing;
220220
221 testing.expect('C' == toUpper('c'));221 testing.expect('C' == toUpper('c'));
...@@ -226,3 +226,59 @@ test "ascii character classes" {...@@ -226,3 +226,59 @@ test "ascii character classes" {
226 testing.expect(!isAlpha('5'));226 testing.expect(!isAlpha('5'));
227 testing.expect(isSpace(' '));227 testing.expect(isSpace(' '));
228}228}
229
230pub fn allocLowerString(allocator: *std.mem.Allocator, ascii_string: []const u8) ![]u8 {
231 const result = try allocator.alloc(u8, ascii_string.len);
232 for (result) |*c, i| {
233 c.* = toLower(ascii_string[i]);
234 }
235 return result;
236}
237
238test "allocLowerString" {
239 var buf: [100]u8 = undefined;
240 const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator;
241 const result = try allocLowerString(allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!");
242 std.testing.expect(std.mem.eql(u8, "abcdefghijklmnopqrst0234+💩!", result));
243}
244
245pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool {
246 if (a.len != b.len) return false;
247 for (a) |a_c, i| {
248 if (toLower(a_c) != toLower(b[i])) return false;
249 }
250 return true;
251}
252
253test "eqlIgnoreCase" {
254 std.testing.expect(eqlIgnoreCase("HEl💩Lo!", "hel💩lo!"));
255 std.testing.expect(!eqlIgnoreCase("hElLo!", "hello! "));
256 std.testing.expect(!eqlIgnoreCase("hElLo!", "helro!"));
257}
258
259/// Finds `substr` in `container`, starting at `start_index`.
260/// TODO boyer-moore algorithm
261pub fn indexOfIgnoreCasePos(container: []const u8, start_index: usize, substr: []const u8) ?usize {
262 if (substr.len > container.len) return null;
263
264 var i: usize = start_index;
265 const end = container.len - substr.len;
266 while (i <= end) : (i += 1) {
267 if (eqlIgnoreCase(container[i .. i + substr.len], substr)) return i;
268 }
269 return null;
270}
271
272/// Finds `substr` in `container`, starting at `start_index`.
273pub fn indexOfIgnoreCase(container: []const u8, substr: []const u8) ?usize {
274 return indexOfIgnoreCasePos(container, 0, substr);
275}
276
277test "indexOfIgnoreCase" {
278 std.testing.expect(indexOfIgnoreCase("one Two Three Four", "foUr").? == 14);
279 std.testing.expect(indexOfIgnoreCase("one two three FouR", "gOur") == null);
280 std.testing.expect(indexOfIgnoreCase("foO", "Foo").? == 0);
281 std.testing.expect(indexOfIgnoreCase("foo", "fool") == null);
282
283 std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0);
284}
std/build.zig+224-21
...@@ -55,6 +55,20 @@ pub const Builder = struct {...@@ -55,6 +55,20 @@ pub const Builder = struct {
55 override_std_dir: ?[]const u8,55 override_std_dir: ?[]const u8,
56 override_lib_dir: ?[]const u8,56 override_lib_dir: ?[]const u8,
5757
58 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
59
60 const PkgConfigError = error{
61 PkgConfigCrashed,
62 PkgConfigFailed,
63 PkgConfigNotInstalled,
64 PkgConfigInvalidOutput,
65 };
66
67 pub const PkgConfigPkg = struct {
68 name: []const u8,
69 desc: []const u8,
70 };
71
58 pub const CStd = enum {72 pub const CStd = enum {
59 C89,73 C89,
60 C99,74 C99,
...@@ -833,20 +847,21 @@ pub const Builder = struct {...@@ -833,20 +847,21 @@ pub const Builder = struct {
833 return error.FileNotFound;847 return error.FileNotFound;
834 }848 }
835849
836 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {850 pub fn execAllowFail(
851 self: *Builder,
852 argv: []const []const u8,
853 out_code: *u8,
854 stderr_behavior: std.ChildProcess.StdIo,
855 ) ![]u8 {
837 assert(argv.len != 0);856 assert(argv.len != 0);
838857
839 if (self.verbose) {
840 printCmd(null, argv);
841 }
842
843 const max_output_size = 100 * 1024;858 const max_output_size = 100 * 1024;
844 const child = try std.ChildProcess.init(argv, self.allocator);859 const child = try std.ChildProcess.init(argv, self.allocator);
845 defer child.deinit();860 defer child.deinit();
846861
847 child.stdin_behavior = .Ignore;862 child.stdin_behavior = .Ignore;
848 child.stdout_behavior = .Pipe;863 child.stdout_behavior = .Pipe;
849 child.stderr_behavior = .Inherit;864 child.stderr_behavior = stderr_behavior;
850865
851 try child.spawn();866 try child.spawn();
852867
...@@ -856,24 +871,48 @@ pub const Builder = struct {...@@ -856,24 +871,48 @@ pub const Builder = struct {
856 var stdout_file_in_stream = child.stdout.?.inStream();871 var stdout_file_in_stream = child.stdout.?.inStream();
857 try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);872 try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
858873
859 const term = child.wait() catch |err| panic("unable to spawn {}: {}", argv[0], err);874 const term = try child.wait();
860 switch (term) {875 switch (term) {
861 .Exited => |code| {876 .Exited => |code| {
862 if (code != 0) {877 if (code != 0) {
863 warn("The following command exited with error code {}:\n", code);878 out_code.* = @truncate(u8, code);
864 printCmd(null, argv);879 return error.ExitCodeFailure;
865 std.os.exit(@truncate(u8, code));
866 }880 }
867 return stdout.toOwnedSlice();881 return stdout.toOwnedSlice();
868 },882 },
869 .Signal, .Stopped, .Unknown => |code| {883 .Signal, .Stopped, .Unknown => |code| {
884 out_code.* = @truncate(u8, code);
885 return error.ProcessTerminated;
886 },
887 }
888 }
889
890 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
891 assert(argv.len != 0);
892
893 if (self.verbose) {
894 printCmd(null, argv);
895 }
896
897 var code: u8 = undefined;
898 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
899 error.FileNotFound => {
900 warn("Unable to spawn the following command: file not found\n");
901 printCmd(null, argv);
902 std.os.exit(@truncate(u8, code));
903 },
904 error.ExitCodeFailure => {
905 warn("The following command exited with error code {}:\n", code);
906 printCmd(null, argv);
907 std.os.exit(@truncate(u8, code));
908 },
909 error.ProcessTerminated => {
870 warn("The following command terminated unexpectedly:\n");910 warn("The following command terminated unexpectedly:\n");
871 printCmd(null, argv);911 printCmd(null, argv);
872 std.os.exit(@truncate(u8, code));912 std.os.exit(@truncate(u8, code));
873 },913 },
874 }914 else => |e| return e,
875915 };
876 return stdout.toOwnedSlice();
877 }916 }
878917
879 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {918 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
...@@ -891,13 +930,45 @@ pub const Builder = struct {...@@ -891,13 +930,45 @@ pub const Builder = struct {
891 [_][]const u8{ base_dir, dest_rel_path },930 [_][]const u8{ base_dir, dest_rel_path },
892 ) catch unreachable;931 ) catch unreachable;
893 }932 }
933
934 fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg {
935 const stdout = try self.execAllowFail([_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
936 var list = ArrayList(PkgConfigPkg).init(self.allocator);
937 var line_it = mem.tokenize(stdout, "\r\n");
938 while (line_it.next()) |line| {
939 if (mem.trim(u8, line, " \t").len == 0) continue;
940 var tok_it = mem.tokenize(line, " \t");
941 try list.append(PkgConfigPkg{
942 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
943 .desc = tok_it.rest(),
944 });
945 }
946 return list.toSliceConst();
947 }
948
949 fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
950 if (self.pkg_config_pkg_list) |res| {
951 return res;
952 }
953 var code: u8 = undefined;
954 if (self.execPkgConfigList(&code)) |list| {
955 self.pkg_config_pkg_list = list;
956 return list;
957 } else |err| {
958 const result = switch (err) {
959 error.ProcessTerminated => error.PkgConfigCrashed,
960 error.ExitCodeFailure => error.PkgConfigFailed,
961 error.FileNotFound => error.PkgConfigNotInstalled,
962 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
963 else => return err,
964 };
965 self.pkg_config_pkg_list = result;
966 return result;
967 }
968 }
894};969};
895970
896test "builder.findProgram compiles" {971test "builder.findProgram compiles" {
897 //allocator: *Allocator,
898 //zig_exe: []const u8,
899 //build_root: []const u8,
900 //cache_root: []const u8,
901 const builder = try Builder.create(std.heap.direct_allocator, "zig", "zig-cache", "zig-cache");972 const builder = try Builder.create(std.heap.direct_allocator, "zig", "zig-cache", "zig-cache");
902 _ = builder.findProgram([_][]const u8{}, [_][]const u8{}) catch null;973 _ = builder.findProgram([_][]const u8{}, [_][]const u8{}) catch null;
903}974}
...@@ -1388,6 +1459,7 @@ pub const LibExeObjStep = struct {...@@ -1388,6 +1459,7 @@ pub const LibExeObjStep = struct {
13881459
1389 link_objects: ArrayList(LinkObject),1460 link_objects: ArrayList(LinkObject),
1390 include_dirs: ArrayList(IncludeDir),1461 include_dirs: ArrayList(IncludeDir),
1462 c_macros: ArrayList([]const u8),
1391 output_dir: ?[]const u8,1463 output_dir: ?[]const u8,
1392 need_system_paths: bool,1464 need_system_paths: bool,
1393 is_linking_libc: bool = false,1465 is_linking_libc: bool = false,
...@@ -1491,6 +1563,7 @@ pub const LibExeObjStep = struct {...@@ -1491,6 +1563,7 @@ pub const LibExeObjStep = struct {
1491 .packages = ArrayList(Pkg).init(builder.allocator),1563 .packages = ArrayList(Pkg).init(builder.allocator),
1492 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),1564 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
1493 .link_objects = ArrayList(LinkObject).init(builder.allocator),1565 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1566 .c_macros = ArrayList([]const u8).init(builder.allocator),
1494 .lib_paths = ArrayList([]const u8).init(builder.allocator),1567 .lib_paths = ArrayList([]const u8).init(builder.allocator),
1495 .framework_dirs = ArrayList([]const u8).init(builder.allocator),1568 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
1496 .object_src = undefined,1569 .object_src = undefined,
...@@ -1617,6 +1690,9 @@ pub const LibExeObjStep = struct {...@@ -1617,6 +1690,9 @@ pub const LibExeObjStep = struct {
16171690
1618 /// Returns whether the library, executable, or object depends on a particular system library.1691 /// Returns whether the library, executable, or object depends on a particular system library.
1619 pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {1692 pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
1693 if (isLibCLibrary(name)) {
1694 return self.is_linking_libc;
1695 }
1620 for (self.link_objects.toSliceConst()) |link_object| {1696 for (self.link_objects.toSliceConst()) |link_object| {
1621 switch (link_object) {1697 switch (link_object) {
1622 LinkObject.SystemLib => |n| if (mem.eql(u8, n, name)) return true,1698 LinkObject.SystemLib => |n| if (mem.eql(u8, n, name)) return true,
...@@ -1641,13 +1717,135 @@ pub const LibExeObjStep = struct {...@@ -1641,13 +1717,135 @@ pub const LibExeObjStep = struct {
1641 return self.isDynamicLibrary() or self.kind == .Exe;1717 return self.isDynamicLibrary() or self.kind == .Exe;
1642 }1718 }
16431719
1644 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {1720 pub fn linkLibC(self: *LibExeObjStep) void {
1721 if (!self.is_linking_libc) {
1722 self.is_linking_libc = true;
1723 self.link_objects.append(LinkObject{ .SystemLib = "c" }) catch unreachable;
1724 }
1725 }
1726
1727 /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
1728 pub fn defineCMacro(self: *LibExeObjStep, name_and_value: []const u8) void {
1729 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
1730 }
1731
1732 /// This one has no integration with anything, it just puts -lname on the command line.
1733 /// Prefer to use `linkSystemLibrary` instead.
1734 pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
1645 self.link_objects.append(LinkObject{ .SystemLib = self.builder.dupe(name) }) catch unreachable;1735 self.link_objects.append(LinkObject{ .SystemLib = self.builder.dupe(name) }) catch unreachable;
1736 self.need_system_paths = true;
1737 }
1738
1739 /// This links against a system library, exclusively using pkg-config to find the library.
1740 /// Prefer to use `linkSystemLibrary` instead.
1741 pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) !void {
1742 const pkg_name = match: {
1743 // First we have to map the library name to pkg config name. Unfortunately,
1744 // there are several examples where this is not straightforward:
1745 // -lSDL2 -> pkg-config sdl2
1746 // -lgdk-3 -> pkg-config gdk-3.0
1747 // -latk-1.0 -> pkg-config atk
1748 const pkgs = try self.builder.getPkgConfigList();
1749
1750 // Exact match means instant winner.
1751 for (pkgs) |pkg| {
1752 if (mem.eql(u8, pkg.name, lib_name)) {
1753 break :match pkg.name;
1754 }
1755 }
1756
1757 // Next we'll try ignoring case.
1758 for (pkgs) |pkg| {
1759 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
1760 break :match pkg.name;
1761 }
1762 }
1763
1764 // Now try appending ".0".
1765 for (pkgs) |pkg| {
1766 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
1767 if (pos != 0) continue;
1768 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
1769 break :match pkg.name;
1770 }
1771 }
1772 }
1773
1774 // Trimming "-1.0".
1775 if (mem.endsWith(u8, lib_name, "-1.0")) {
1776 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
1777 for (pkgs) |pkg| {
1778 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
1779 break :match pkg.name;
1780 }
1781 }
1782 }
1783
1784 return error.PackageNotFound;
1785 };
1786
1787 var code: u8 = undefined;
1788 const stdout = if (self.builder.execAllowFail([_][]const u8{
1789 "pkg-config",
1790 pkg_name,
1791 "--cflags",
1792 "--libs",
1793 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
1794 error.ProcessTerminated => return error.PkgConfigCrashed,
1795 error.ExitCodeFailure => return error.PkgConfigFailed,
1796 error.FileNotFound => return error.PkgConfigNotInstalled,
1797 else => return err,
1798 };
1799 var it = mem.tokenize(stdout, " \r\n\t");
1800 while (it.next()) |tok| {
1801 if (mem.eql(u8, tok, "-I")) {
1802 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
1803 self.addIncludeDir(dir);
1804 } else if (mem.startsWith(u8, tok, "-I")) {
1805 self.addIncludeDir(tok["-I".len..]);
1806 } else if (mem.eql(u8, tok, "-L")) {
1807 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
1808 self.addLibPath(dir);
1809 } else if (mem.startsWith(u8, tok, "-L")) {
1810 self.addLibPath(tok["-L".len..]);
1811 } else if (mem.eql(u8, tok, "-l")) {
1812 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
1813 self.linkSystemLibraryName(lib);
1814 } else if (mem.startsWith(u8, tok, "-l")) {
1815 self.linkSystemLibraryName(tok["-l".len..]);
1816 } else if (mem.eql(u8, tok, "-D")) {
1817 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
1818 self.defineCMacro(macro);
1819 } else if (mem.startsWith(u8, tok, "-D")) {
1820 self.defineCMacro(tok["-D".len..]);
1821 } else if (mem.eql(u8, tok, "-pthread")) {
1822 self.linkLibC();
1823 } else if (self.builder.verbose) {
1824 warn("Ignoring pkg-config flag '{}'\n", tok);
1825 }
1826 }
1827 }
1828
1829 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
1646 if (isLibCLibrary(name)) {1830 if (isLibCLibrary(name)) {
1647 self.is_linking_libc = true;1831 self.linkLibC();
1648 } else {1832 return;
1649 self.need_system_paths = true;1833 }
1834 if (self.linkSystemLibraryPkgConfigOnly(name)) |_| {
1835 // pkg-config worked, so nothing further needed to do.
1836 return;
1837 } else |err| switch (err) {
1838 error.PkgConfigInvalidOutput,
1839 error.PkgConfigCrashed,
1840 error.PkgConfigFailed,
1841 error.PkgConfigNotInstalled,
1842 error.PackageNotFound,
1843 => {},
1844
1845 else => unreachable,
1650 }1846 }
1847
1848 self.linkSystemLibraryName(name);
1651 }1849 }
16521850
1653 pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {1851 pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
...@@ -2072,6 +2270,11 @@ pub const LibExeObjStep = struct {...@@ -2072,6 +2270,11 @@ pub const LibExeObjStep = struct {
2072 }2270 }
2073 }2271 }
20742272
2273 for (self.c_macros.toSliceConst()) |c_macro| {
2274 try zig_args.append("-D");
2275 try zig_args.append(c_macro);
2276 }
2277
2075 if (self.target.isDarwin()) {2278 if (self.target.isDarwin()) {
2076 for (self.framework_dirs.toSliceConst()) |dir| {2279 for (self.framework_dirs.toSliceConst()) |dir| {
2077 try zig_args.append("-F");2280 try zig_args.append("-F");