#Choose (at build time) to use zig or provided C implementation

1 messages · Page 1 of 1 (latest)

vivid dune
#

I want to make a BLAS implementation but want to be able to use a provided implementation if I decide to. So, I would like to have a function

const c = @import("c.zig");

pub fn dasum(n: isize, x: [*]f64, incx: isize) f64 {
    return c.cblas_dasum(n, x, incx);
    return some_zig_implementation(n, x, incx);
}

where c.zig is something like:

pub usingnamespace @cImport({
    @cInclude("cblas.h");
});

The idea is that dasum would choose either the provided implementation (c included file) or the zig implementation depending on whether that external implementation (in the form of a shared object most probably) was provided or not. How can I do this?

limpid panther
#

you'd probably need to provide an option from your build.zig (like has_c_impl or something) to your module so that it knows what to do

vivid dune
#

And then how do I communicate that to the functions?

#

Like, the function would have some if that changes with that build option?

limpid panther
#

you can do something like const options = @import("options"); then use options.has_c_impl which would resolve at compile time

#
const options = b.addOptions();
options.addOption(bool, "has_c_impl", false);
exe.root_module.addImport("options", options.createModule());
#

something like this in build.zig

vivid dune
#

When compiling, how do I set this option?

severe monolith
#
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const opts = b.addOptions();
    var use_c = true;
    std.fs.cwd().access("src/greet.c", .{}) catch {
        use_c = false;
    };
    opts.addOption(bool, "use_c", use_c);

    const exe = b.addExecutable(.{
        .name = "hw12",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    exe.root_module.addImport("options", opts.createModule());
    if (use_c) exe.addCSourceFile(.{ .file = b.path("src/greet.c") });
    b.installArtifact(exe);
}

that's a bit awkward, but it adds a C source file if it's present, and supplies an "options" module with a bool that's true if that C source file is present.

So, first, the build system decides whether to include the C or not.

#

then your code can decide based on that bool:

const std = @import("std");
const opts = @import("options");
const greet = if (opts.use_c) @import("greetc.zig") else @import("greet.zig");
#

where those two options are just

// greetc.zig
pub extern fn msg() [*:0]const u8;

// greet.zig
pub fn msg() []const u8 {
    return "hello";
}
#

if you'd rather set an option when running zig build, than have the build system decide what to do based on whether files exist, you can get flags with b.option

#

what @lod said plus

const opt_usec: ?bool = b.option(bool, "use_c", "use C implementation?");
#

and then

options.addOption(bool, "has_c_impl", opt_usec orelse false);
#

so you can pass -Dusec=true, -Dusec=false, or leave it unprovided to get defaulted to false