Zig Cross-Compilation
Cross-compilation is building a program for a target different from the machine doing the build. This lesson builds the same program for Windows and for Arm Linux.
Building for Windows
One flag builds the program for Windows, and the result lands in zig-out/bin as hello.exe.
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "hello",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
Building for Arm Linux
The same script with a different triple builds for Arm Linux instead.
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "hello",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
Target plus size setting
A target flag combines with the usual size and speed setting in one command.
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "hello",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
Example
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "hello",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
Key points
- Target: a target is the machine and system a program is built for.
- Target triple: a target triple is a string naming the processor, the system, and the binary interface, like x86_64-windows-gnu.
- Cross-compilation: cross-compilation is building a program for a target different from the machine doing the build.
- standardTargetOptions reads the -Dtarget flag, so one build script serves every target.
- A foreign binary is not run on the build machine, so only the build itself is checked here.
- The built file lands in zig-out/bin, with a .exe ending for Windows targets.
The build script is the same one used for a native build. The -Dtarget flag points it at Arm Linux instead of the build machine. The build checks out and the binary lands in zig-out/bin. Nothing here runs the foreign binary, since it belongs to another machine.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which command builds for Windows from this machine?