trenta3
trenta3

Reputation: 143

Using zig compiler as a library

Is there a way to use zig compiler as a library inside zig? After looking both in zig documentation, issues and on the internet, I can't find an answer to this question.

In one of the issues it is said that this can be done at the current time, but I couldn't find any examples of how to do it.

Upvotes: 4

Views: 1275

Answers (1)

pfg
pfg

Reputation: 3577

It is currently non-trivial to use the zig compiler as a library due to the difficulty in including libraries with complicated build requirements. Once the package manager is released, it will likely become much easier.

For now, I would recommend calling the zig compiler as a system command

var zig = std.ChildProcess.init(&.{"zig", "build-exe", "demo.zig"}, allocator);
try zig.spawn();
try zig.wait();

However, if you really want to, here is how to use zig 0.9.0-dev.1611+f3ba72cf5 as a library

Reasons not to do this before the package manager is released:

  • This requires making a mess of your build.zig file, and your build.zig file has to be kept up to date with the compiler's build.zig file
  • This will make your project build really slowly due to the current slow speed of the stage1 zig compiler
  • This example does not show how to enable stage1 and the LLVM extensions in the zig package it makes. Enabling stage1 and LLVM extensions will require looking at zig's build.zig file and copying in stuff about linking libraries and include directories and c source and stuff.

This build.zig was made by finding all the instances of .addOption in the zig compiler's build.zig and recreating them in our own build.zig file.

// build.zig

const std = @import("std");

pub fn build(b: *std.build.Builder) !void {
    const target = b.standardTargetOptions(.{});

    const mode = b.standardReleaseOptions();

    const exe_options = b.addOptions();

    const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse 4;
    const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;

    const enable_logging = b.option(bool, "log", "Whether to enable logging") orelse false;
    const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;

    exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
    exe_options.addOption(bool, "skip_non_native", skip_non_native);
    exe_options.addOption(bool, "have_llvm", false);
    exe_options.addOption(bool, "llvm_has_m68k", false);
    exe_options.addOption(bool, "llvm_has_csky", false);
    exe_options.addOption(bool, "llvm_has_ve", false);
    exe_options.addOption(bool, "llvm_has_arc", false);

    const version = "0.0.0";

    exe_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));

    const semver = try std.SemanticVersion.parse(version);
    exe_options.addOption(std.SemanticVersion, "semver", semver);

    exe_options.addOption(bool, "enable_logging", enable_logging);
    exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
    exe_options.addOption(bool, "enable_tracy", false);
    exe_options.addOption(bool, "enable_tracy_callstack", false);
    exe_options.addOption(bool, "enable_tracy_allocation", false);
    exe_options.addOption(bool, "is_stage1", false);
    exe_options.addOption(bool, "omit_stage2", false);

    const exe = b.addExecutable("tmp", "sample.zig");
    exe.setTarget(target);
    exe.setBuildMode(mode);
    exe.addOptions("build_options", exe_options);
    exe.addPackage(.{
        .name = "zig",
        .path = .{ .path = "src/main.zig" },
        .dependencies = &[_]std.build.Pkg{
            .{ .name = "build_options", .path = exe_options.getSource() },
        },
    });
    exe.install();

    const run_cmd = exe.run();
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}
// sample.zig

const zig = @import("zig");

pub fn main() !void {
    return zig.main(); // just calls into the zig compiler main function. you'll have to look into how https://github.com/ziglang/zig/blob/master/src/main.zig works in order to do more complicated stuff.
}

note: I recommend building this with -Dskip-non-native otherwise it will take a very long time to build due to the speed of the current stage1 compiler

zig build run -Dskip-non-native -- build-exe demo.zig

Upvotes: 3

Related Questions