From 74fb625a342f369b729720073518ff146b1b5d9f Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Wed, 30 Apr 2025 07:46:29 +0900 Subject: [PATCH 1/7] Changes to build for 0.14 0.14 needs enum names. Add this. --- build.zig.zon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index f84e40a..c137b8b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,7 @@ .{ - .name = "go", + .name = .go, .version = "0.0.0", .dependencies = .{}, .paths = .{""}, + .fingerprint = 0xb668935686b9cf3d, } From 611b320d63bb7c2bf21377060f2c3d7f8d7eef79 Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Thu, 1 May 2025 21:12:32 +0900 Subject: [PATCH 2/7] Sufficient for building a simple Go program --- build.zig | 80 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/build.zig b/build.zig index 2724a75..0bc77f2 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); -pub fn addExecutable(b: *std.Build, options: BuildStep.Options) *BuildStep { - return BuildStep.create(b, options); +pub fn addExecutable(b: *std.Build, options: GoBuildStep.Options) *GoBuildStep { + return GoBuildStep.create(b, options); } pub fn build(b: *std.Build) void { @@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void { } /// Runs `go build` with relevant flags -pub const BuildStep = struct { +pub const GoBuildStep = struct { step: std.Build.Step, generated_bin: ?*std.Build.GeneratedFile, opts: Options, @@ -23,8 +23,8 @@ pub const BuildStep = struct { }; /// Create a GoBuildStep - pub fn create(b: *std.Build, options: Options) *BuildStep { - const self = b.allocator.create(BuildStep) catch unreachable; + pub fn create(b: *std.Build, options: Options) *GoBuildStep { + const self = b.allocator.create(GoBuildStep) catch unreachable; self.* = .{ .opts = options, .generated_bin = null, @@ -32,14 +32,14 @@ pub const BuildStep = struct { .id = .custom, .name = "go build", .owner = b, - .makeFn = BuildStep.make, + .makeFn = GoBuildStep.make, }), }; return self; } - pub fn make(step: *std.Build.Step, progress: std.Progress.Node) !void { - const self: *BuildStep = @fieldParentPtr("step", step); + pub fn make(step: *std.Build.Step, mkoptions: std.Build.Step.MakeOptions) !void { + const self: *GoBuildStep = @fieldParentPtr("step", step); const b = step.owner; var go_args = std.ArrayList([]const u8).init(b.allocator); defer go_args.deinit(); @@ -59,11 +59,18 @@ pub const BuildStep = struct { var env = try std.process.getEnvMap(b.allocator); + // GO cross compilation. + // TODO(rjk): Rewrite this in terms of enums. + const target = self.opts.target; + const goarch = try isa_to_goarch(@tagName(target.result.cpu.arch)); + try env.put("GOARCH", goarch); + const goos = try ostag_to_goos(@tagName(target.result.os.tag)); + try env.put("GOOS", goos); + // CGO - if (self.opts.cgo_enabled) { + if (self.opts.cgo_enabled) { try env.put("CGO_ENABLED", "1"); // Set zig as the CGO compiler - const target = self.opts.target; const cc = b.fmt( "zig cc -target {s}-{s}-{s}", .{ @tagName(target.result.cpu.arch), @tagName(target.result.os.tag), @tagName(target.result.abi) }, @@ -74,19 +81,22 @@ pub const BuildStep = struct { .{ @tagName(target.result.cpu.arch), @tagName(target.result.os.tag), @tagName(target.result.abi) }, ); try env.put("CXX", cxx); - try env.put("GOOS", @tagName(target.result.os.tag)); - // Tell the linker we are statically linking + + // Tell the linker that we are statically linking. + // TODO(rjk): Maybe not right? go_args.appendSlice(&.{ "--ldflags", "-linkmode=external -extldflags=-static" }) catch @panic("OOM"); } else { try env.put("CGO_ENABLED", "0"); } + + // Output file always needs to be added last try go_args.append(self.opts.package_path.getPath(b)); const cmd = std.mem.join(b.allocator, " ", go_args.items) catch @panic("OOM"); - const node = progress.start(cmd, 1); + const node = mkoptions.progress_node.start(cmd, 1); defer node.end(); // run the command @@ -101,7 +111,7 @@ pub const BuildStep = struct { } /// Return the LazyPath of the generated binary - pub fn getEmittedBin(self: *BuildStep) std.Build.LazyPath { + pub fn getEmittedBin(self: *GoBuildStep) std.Build.LazyPath { if (self.generated_bin) |generated_bin| return .{ .generated = .{ .file = generated_bin } }; @@ -113,7 +123,7 @@ pub const BuildStep = struct { } /// Add a run step which depends on the GoBuildStep - pub fn addRunStep(self: *BuildStep) *std.Build.Step.Run { + pub fn addRunStep(self: *GoBuildStep) *std.Build.Step.Run { const b = self.step.owner; const run_step = std.Build.Step.Run.create(b, b.fmt("run {s}", .{self.opts.name})); run_step.step.dependOn(&self.step); @@ -124,7 +134,7 @@ pub const BuildStep = struct { } // Add an install step which depends on the GoBuildStep - pub fn addInstallStep(self: *BuildStep) void { + pub fn addInstallStep(self: *GoBuildStep) void { const b = self.step.owner; const bin_file = self.getEmittedBin(); const install_step = b.addInstallBinFile(bin_file, self.opts.name); @@ -132,7 +142,7 @@ pub const BuildStep = struct { b.getInstallStep().dependOn(&install_step.step); } - fn evalChildProcess(self: *BuildStep, argv: []const []const u8, env: *const std.process.EnvMap) !void { + fn evalChildProcess(self: *GoBuildStep, argv: []const []const u8, env: *const std.process.EnvMap) !void { const s = &self.step; const arena = s.owner.allocator; @@ -152,3 +162,39 @@ pub const BuildStep = struct { try std.Build.Step.handleChildProcessTerm(s, result.term, null, argv); } }; + +const GoEnvError = error{ + UNSUPPORTED_GOARCH, + UNSUPPORTED_GOOS, +}; + + +fn isa_to_goarch(isa: [:0]const u8) ![:0]const u8 { + if (std.mem.eql(u8, isa, "x86_64")) { + return "amd64"; + } else if (std.mem.eql(u8, isa, "arm")) { + return "arm"; + } else if (std.mem.eql(u8, isa, "aarch64")) { + return "arm64"; + } +// TODO(rjk): Consider adding some less popular processors that might +// work. + +return error.UNSUPPORTED_GOARCH; +} + + + +fn ostag_to_goos(os: [:0]const u8) ![:0]const u8 { + + if (std.mem.startsWith(u8, os, "macos") ) { + return "darwin"; + } else if (std.mem.startsWith(u8, os, "linux") ) { + return "linux"; + } else if (std.mem.startsWith(u8, os, "windows") ) { + return "windows"; + } +// TODO(rjk): Add less common OS. In particular, support Plan9. + return error.UNSUPPORTED_GOOS; +} + From 1834a0b7623841fbc06e4366b552018171a86c31 Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Sat, 3 May 2025 15:47:48 +0900 Subject: [PATCH 3/7] Changes for CGO support on MacOS Mac's requirement for the native resolver adds some complexity. Support Mac CGO builds by using the native platforn on Mac. Remove some unnecessary code. --- build.zig | 89 +++++++++++++++++++++++++++++++-------------------- build.zig.zon | 9 ++++-- 2 files changed, 62 insertions(+), 36 deletions(-) diff --git a/build.zig b/build.zig index 0bc77f2..ab91595 100644 --- a/build.zig +++ b/build.zig @@ -20,6 +20,7 @@ pub const GoBuildStep = struct { optimize: std.builtin.OptimizeMode, package_path: std.Build.LazyPath, cgo_enabled: bool = true, + // TODO(rjk): Add Mac target sysroot support. }; /// Create a GoBuildStep @@ -59,39 +60,38 @@ pub const GoBuildStep = struct { var env = try std.process.getEnvMap(b.allocator); - // GO cross compilation. - // TODO(rjk): Rewrite this in terms of enums. - const target = self.opts.target; - const goarch = try isa_to_goarch(@tagName(target.result.cpu.arch)); - try env.put("GOARCH", goarch); - const goos = try ostag_to_goos(@tagName(target.result.os.tag)); - try env.put("GOOS", goos); + // GOOS and GOARCH fields are not the same as triple fields on some + // platforms. Adjust them appropriately. + const target = self.opts.target; + const goarch = try isa_to_goarch(@tagName(target.result.cpu.arch)); + try env.put("GOARCH", goarch); + const goos = try ostag_to_goos(@tagName(target.result.os.tag)); + try env.put("GOOS", goos); + + // Cross compilling Go to MacOS requires providing a sysroot. The default + // sysroot exists when the target is macos, a MacOS SDK is installed and + // the Zig target is native. Otherwise, a sysroot *might* be present in + // some fashion. Add support for specifying the system root. // CGO - if (self.opts.cgo_enabled) { + if (self.opts.cgo_enabled) { try env.put("CGO_ENABLED", "1"); // Set zig as the CGO compiler + const ts = try self.mktargetstring(b); const cc = b.fmt( - "zig cc -target {s}-{s}-{s}", - .{ @tagName(target.result.cpu.arch), @tagName(target.result.os.tag), @tagName(target.result.abi) }, + "zig cc -target {s}", + .{ts}, ); try env.put("CC", cc); const cxx = b.fmt( - "zig c++ -target {s}-{s}-{s}", - .{ @tagName(target.result.cpu.arch), @tagName(target.result.os.tag), @tagName(target.result.abi) }, + "zig c++ -target {s}", + .{ts}, ); try env.put("CXX", cxx); - - - // Tell the linker that we are statically linking. - // TODO(rjk): Maybe not right? - go_args.appendSlice(&.{ "--ldflags", "-linkmode=external -extldflags=-static" }) catch @panic("OOM"); } else { try env.put("CGO_ENABLED", "0"); } - - // Output file always needs to be added last try go_args.append(self.opts.package_path.getPath(b)); @@ -99,7 +99,7 @@ pub const GoBuildStep = struct { const node = mkoptions.progress_node.start(cmd, 1); defer node.end(); - // run the command + // Run the command try self.evalChildProcess(go_args.items, &env); if (self.generated_bin == null) { @@ -133,7 +133,7 @@ pub const GoBuildStep = struct { return run_step; } - // Add an install step which depends on the GoBuildStep + /// Add an install step which depends on the GoBuildStep pub fn addInstallStep(self: *GoBuildStep) void { const b = self.step.owner; const bin_file = self.getEmittedBin(); @@ -161,6 +161,26 @@ pub const GoBuildStep = struct { try std.Build.Step.handleChildProcessTerm(s, result.term, null, argv); } + + /// Create a target that can build the Go code. Uses "native" on MacOS + /// so that it has a sysroot. + // TODO(rjk): Add some kind f completed sysroot support. + fn mktargetstring(self: *GoBuildStep, b: *std.Build) ![]const u8 { + const target = self.opts.target; + if (target.result.os.tag == .ios or target.result.os.tag == .macos) { + return "native"; + } else { + return b.fmt( + "{s}-{s}-{s}", + .{ + @tagName(target.result.cpu.arch), + @tagName(target.result.os.tag), + @tagName(target.result.abi), + }, + ); + } + return error.NOTIMPL_CUSTOM_MACOS_SYSROOT; + } }; const GoEnvError = error{ @@ -168,33 +188,34 @@ const GoEnvError = error{ UNSUPPORTED_GOOS, }; - +/// Convert a Zig arch triple member to the corresponding GOARCH value. fn isa_to_goarch(isa: [:0]const u8) ![:0]const u8 { if (std.mem.eql(u8, isa, "x86_64")) { return "amd64"; } else if (std.mem.eql(u8, isa, "arm")) { - return "arm"; + return "arm"; } else if (std.mem.eql(u8, isa, "aarch64")) { return "arm64"; } -// TODO(rjk): Consider adding some less popular processors that might -// work. + // TODO(rjk): Consider adding some less popular processors that might + // work. -return error.UNSUPPORTED_GOARCH; + return error.UNSUPPORTED_GOARCH; } - - +/// Convert a Zig os triple member to the corresponding GOOS value. fn ostag_to_goos(os: [:0]const u8) ![:0]const u8 { - - if (std.mem.startsWith(u8, os, "macos") ) { + if (std.mem.startsWith(u8, os, "macos")) { return "darwin"; - } else if (std.mem.startsWith(u8, os, "linux") ) { + } else if (std.mem.startsWith(u8, os, "linux")) { return "linux"; - } else if (std.mem.startsWith(u8, os, "windows") ) { + } else if (std.mem.startsWith(u8, os, "windows")) { return "windows"; - } -// TODO(rjk): Add less common OS. In particular, support Plan9. + } + // TODO(rjk): Add less common OS. In particular, support Plan9. return error.UNSUPPORTED_GOOS; } +const MacEnvError = error{ + NOTIMPL_CUSTOM_MACOS_SYSROOT, +}; diff --git a/build.zig.zon b/build.zig.zon index c137b8b..3af7d9a 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,12 @@ .{ .name = .go, .version = "0.0.0", - .dependencies = .{}, + .dependencies = .{ + .go = .{ + .url = "/Users/rjkroege/tools/_builds/zig4go", + .hash = "go-0.0.0-Pc-5hm6ztgQ9f0bk8a1X8qYupArIzWASlP6St1ZBWWnf", + }, + }, .paths = .{""}, - .fingerprint = 0xb668935686b9cf3d, + .fingerprint = 0xb668935686b9cf3d, } From 3b94f913d7da5bf61dd1bdbee68d02ce6639b450 Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Mon, 5 May 2025 05:04:20 -0400 Subject: [PATCH 4/7] Support building Go libraries Add the necessary code to build libraries in Go lang and an accessor for the include path. --- build.zig | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index ab91595..68b974e 100644 --- a/build.zig +++ b/build.zig @@ -4,6 +4,15 @@ pub fn addExecutable(b: *std.Build, options: GoBuildStep.Options) *GoBuildStep { return GoBuildStep.create(b, options); } +/// Convenience method to create a library. +pub fn addLibrary(b: *std.Build, options: GoBuildStep.Options) *GoBuildStep { + var library_options = options; + library_options.make_library = true; + // A library build requires CGO. + library_options.cgo_enabled = true; + return GoBuildStep.create(b, library_options); +} + pub fn build(b: *std.Build) void { _ = b; } @@ -15,11 +24,14 @@ pub const GoBuildStep = struct { opts: Options, pub const Options = struct { + // The name of generated target. name: []const u8, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, package_path: std.Build.LazyPath, + // Why is this true by default? Should be false by default. cgo_enabled: bool = true, + make_library: bool = false, // TODO(rjk): Add Mac target sysroot support. }; @@ -92,7 +104,12 @@ pub const GoBuildStep = struct { try env.put("CGO_ENABLED", "0"); } - // Output file always needs to be added last + if (self.opts.make_library) { + // Where does the .h file go? (next to it) + try go_args.appendSlice(&.{"--buildmode=c-archive"}); + } + + // Package path always needs to be added last try go_args.append(self.opts.package_path.getPath(b)); const cmd = std.mem.join(b.allocator, " ", go_args.items) catch @panic("OOM"); @@ -110,6 +127,11 @@ pub const GoBuildStep = struct { self.generated_bin.?.path = output_file; } + /// Return the LazyPath of the directory containing the Go-generated include file. + pub fn getIncludePath(self: *GoBuildStep) std.Build.LazyPath { + return self.getEmittedBin().dirname(); + } + /// Return the LazyPath of the generated binary pub fn getEmittedBin(self: *GoBuildStep) std.Build.LazyPath { if (self.generated_bin) |generated_bin| From 214b557fb38e4e9eeee45b63462ef46da220f39e Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Mon, 5 May 2025 05:49:23 -0400 Subject: [PATCH 5/7] Restore static linking support I'd deleted static linking support in error. Restore. --- build.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.zig b/build.zig index 68b974e..b283bda 100644 --- a/build.zig +++ b/build.zig @@ -100,6 +100,9 @@ pub const GoBuildStep = struct { .{ts}, ); try env.put("CXX", cxx); + + // Tell the linker we are statically linking + go_args.appendSlice(&.{ "--ldflags", "-linkmode=external -extldflags=-static" }) catch @panic("OOM"); } else { try env.put("CGO_ENABLED", "0"); } From 2b40d6a0b6f77acc7fdcad33d68f8b9972b412e6 Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Mon, 5 May 2025 06:15:28 -0400 Subject: [PATCH 6/7] Fixed a typo --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index b283bda..f6fc92a 100644 --- a/build.zig +++ b/build.zig @@ -189,7 +189,7 @@ pub const GoBuildStep = struct { /// Create a target that can build the Go code. Uses "native" on MacOS /// so that it has a sysroot. - // TODO(rjk): Add some kind f completed sysroot support. + // TODO(rjk): Add some kind of completed sysroot support. fn mktargetstring(self: *GoBuildStep, b: *std.Build) ![]const u8 { const target = self.opts.target; if (target.result.os.tag == .ios or target.result.os.tag == .macos) { From f04187c8e2c83c13d9ca4b5cfffd3f20d5590ee4 Mon Sep 17 00:00:00 2001 From: Robert Kroeger Date: Sun, 11 May 2025 15:16:34 -0400 Subject: [PATCH 7/7] Addressed review comments --- build.zig | 1 - build.zig.zon | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/build.zig b/build.zig index f6fc92a..ddc3245 100644 --- a/build.zig +++ b/build.zig @@ -29,7 +29,6 @@ pub const GoBuildStep = struct { target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, package_path: std.Build.LazyPath, - // Why is this true by default? Should be false by default. cgo_enabled: bool = true, make_library: bool = false, // TODO(rjk): Add Mac target sysroot support. diff --git a/build.zig.zon b/build.zig.zon index 3af7d9a..d2b5305 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,12 +1,7 @@ .{ .name = .go, .version = "0.0.0", - .dependencies = .{ - .go = .{ - .url = "/Users/rjkroege/tools/_builds/zig4go", - .hash = "go-0.0.0-Pc-5hm6ztgQ9f0bk8a1X8qYupArIzWASlP6St1ZBWWnf", - }, - }, + .dependencies = .{}, .paths = .{""}, .fingerprint = 0xb668935686b9cf3d, }