I think my first approach would be to just encode the options as a suffix of the executable name like so:
const strip = b.option(
bool,
"strip",
"Strip debug info to reduce binary size, defaults to true in release modes",
) orelse false;
const Mode = enum { normal, fuzz, debug, interactive };
const mode = b.option(
Mode,
"mode",
"compile in special modes",
) orelse .normal;
const name = try std.fmt.allocPrint(b.allocator, "example-{s}{s}", .{ if (strip) "s" else "u", @tagName(mode) });
const exe = b.addExecutable(.{
.name = name,
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
So you end up with a bunch of executables in the zig-out/bin
like this:
.
βββ bin
β βββ example-sfuzz
β βββ example-snormal
β βββ example-ufuzz
β βββ example-uinteractive
β βββ example-unormal
Or if you have a more complex situation you could create separate directories like this:
const strip = b.option(
bool,
"strip",
"Strip debug info to reduce binary size, defaults to true in release modes",
) orelse false;
const Mode = enum { normal, fuzz, debug, interactive };
const mode = b.option(
Mode,
"mode",
"compile in special modes",
) orelse .normal;
const fuzzer = try std.fmt.allocPrint(b.allocator, "{s}{s}", .{ if (strip) "s" else "u", @tagName(mode) });
const exe = b.addExecutable(.{
.name = "example",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const install_wf = b.addWriteFiles();
_ = install_wf.addCopyFile(exe.getEmittedBin(), "example");
// can even add meta info files besides the executable
var string = std.ArrayList(u8).init(b.allocator);
try std.json.stringify(.{
.strip = strip,
.mode = mode,
}, .{}, string.writer());
_ = install_wf.add("config.json", string.items);
const install = b.addInstallDirectory(.{
.source_dir = install_wf.getDirectory(),
.install_dir = .prefix,
.install_subdir = b.pathJoin(&.{ "fuzzers", fuzzer }),
});
b.getInstallStep().dependOn(&install.step);
And end up with this:
.
βββ fuzzers
βββ sfuzz
β βββ config.json
β βββ example
βββ sinteractive
β βββ config.json
β βββ example
βββ snormal
β βββ config.json
β βββ example
βββ udebug
β βββ config.json
β βββ example
βββ ufuzz
β βββ config.json
β βββ example
βββ uinteractive
βββ config.json
βββ example
Where config contains something like this:
{"strip":false,"mode":"fuzz"}
So I donβt think you need to give up on using installation directories, just create your own directory structure for them.
Or is there any downside to this that I havenβt realized?
If you donβt want to encode the options you could just have an option -Dfuzzer-name=someuniqueid
and use that as the name of the folder in the second example and then your process can create the path zig-out/fuzzer/someuniqueid/example
to invoke the executable.