| ... | @@ -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, |
| 57 | | 57 | |
| | 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 | } |
| 835 | | 849 | |
| 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); |
| 838 | | 857 | |
| 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(); |
| 846 | | 861 | |
| 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; |
| 850 | | 865 | |
| 851 | try child.spawn(); | 866 | try child.spawn(); |
| 852 | | 867 | |
| ... | @@ -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); |
| 858 | | 873 | |
| 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, |
| 875 | | 915 | }; |
| 876 | return stdout.toOwnedSlice(); | | |
| 877 | } | 916 | } |
| 878 | | 917 | |
| 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 | }; |
| 895 | | 970 | |
| 896 | test "builder.findProgram compiles" { | 971 | test "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 { |
| 1388 | | 1459 | |
| 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 { |
| 1617 | | 1690 | |
| 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 | } |
| 1643 | | 1719 | |
| 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 | } |
| 1652 | | 1850 | |
| 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 | } |
| 2074 | | 2272 | |
| | 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"); |