mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-08 17:40:59 -04:00
185 lines
6.2 KiB
Zig
185 lines
6.2 KiB
Zig
const ConfigHeader = @This();
|
|
|
|
const std = @import("std");
|
|
const Io = std.Io;
|
|
const Step = std.Build.Step;
|
|
const Allocator = std.mem.Allocator;
|
|
const Configuration = std.Build.Configuration;
|
|
|
|
step: Step,
|
|
values: std.array_hash_map.String(Value),
|
|
/// This directory contains the generated file under the name `include_path`.
|
|
generated_dir: Configuration.GeneratedFileIndex,
|
|
|
|
style: Style,
|
|
max_bytes: usize,
|
|
include_path: []const u8,
|
|
include_guard_override: ?[]const u8,
|
|
|
|
pub const base_tag: Step.Tag = .config_header;
|
|
|
|
pub const Style = union(enum) {
|
|
/// A configure format supported by autotools that uses `#undef foo` to
|
|
/// mark lines that can be substituted with different values.
|
|
autoconf_undef: std.Build.LazyPath,
|
|
/// A configure format supported by autotools that uses `@FOO@` output variables.
|
|
autoconf_at: std.Build.LazyPath,
|
|
/// The configure format supported by CMake. It uses `@FOO@`, `${}` and
|
|
/// `#cmakedefine` for template substitution.
|
|
cmake: std.Build.LazyPath,
|
|
/// Instead of starting with an input file, start with nothing.
|
|
blank,
|
|
/// Start with nothing, like blank, and output a nasm .asm file.
|
|
nasm,
|
|
|
|
pub fn getPath(style: Style) ?std.Build.LazyPath {
|
|
switch (style) {
|
|
.autoconf_undef, .autoconf_at, .cmake => |s| return s,
|
|
.blank, .nasm => return null,
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const Value = union(enum) {
|
|
undef,
|
|
defined,
|
|
boolean: bool,
|
|
int: i64,
|
|
ident: []const u8,
|
|
string: []const u8,
|
|
};
|
|
|
|
pub const Options = struct {
|
|
style: Style = .blank,
|
|
max_bytes: usize = 2 * 1024 * 1024,
|
|
include_path: ?[]const u8 = null,
|
|
first_ret_addr: ?usize = null,
|
|
include_guard_override: ?[]const u8 = null,
|
|
};
|
|
|
|
pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
|
|
const graph = owner.graph;
|
|
const arena = graph.arena;
|
|
const config_header = arena.create(ConfigHeader) catch @panic("OOM");
|
|
|
|
var include_path: []const u8 = "config.h";
|
|
|
|
if (options.style.getPath()) |s| default_include_path: {
|
|
const wc = &graph.wip_configuration;
|
|
const sub_path = switch (s) {
|
|
.src_path => |sp| sp.sub_path,
|
|
.generated => break :default_include_path,
|
|
.cwd_relative => |sub_path| sub_path,
|
|
.relative => |r| wc.stringSlice(r.sub_path),
|
|
.dependency => |dependency| dependency.sub_path,
|
|
};
|
|
const basename = std.fs.path.basename(sub_path);
|
|
if (std.mem.endsWith(u8, basename, ".h.in")) {
|
|
include_path = basename[0 .. basename.len - 3];
|
|
}
|
|
}
|
|
|
|
if (options.include_path) |p| {
|
|
include_path = p;
|
|
}
|
|
|
|
const name = if (options.style.getPath()) |s|
|
|
owner.fmt("configure {t} header {f} to {s}", .{ options.style, s.fmt(graph), include_path })
|
|
else
|
|
owner.fmt("configure {t} header to {s}", .{ options.style, include_path });
|
|
|
|
config_header.* = .{
|
|
.step = .init(.{
|
|
.tag = base_tag,
|
|
.name = name,
|
|
.owner = owner,
|
|
.first_ret_addr = options.first_ret_addr orelse @returnAddress(),
|
|
}),
|
|
.style = options.style,
|
|
.values = .empty,
|
|
|
|
.max_bytes = options.max_bytes,
|
|
.include_path = graph.dupeString(include_path),
|
|
.include_guard_override = options.include_guard_override,
|
|
.generated_dir = graph.addGeneratedFile(&config_header.step),
|
|
};
|
|
|
|
if (options.style.getPath()) |s| {
|
|
s.addStepDependencies(&config_header.step);
|
|
}
|
|
return config_header;
|
|
}
|
|
|
|
pub fn addIdent(config_header: *ConfigHeader, name: []const u8, value: []const u8) void {
|
|
const arena = config_header.step.owner.allocator;
|
|
config_header.values.put(arena, name, .{ .ident = value }) catch @panic("OOM");
|
|
}
|
|
|
|
pub fn addValue(config_header: *ConfigHeader, name: []const u8, comptime T: type, value: T) void {
|
|
return addValueInner(config_header, name, T, value) catch @panic("OOM");
|
|
}
|
|
|
|
fn addValueInner(config_header: *ConfigHeader, name: []const u8, comptime T: type, value: T) !void {
|
|
const arena = config_header.step.owner.allocator;
|
|
switch (@typeInfo(T)) {
|
|
.null => {
|
|
try config_header.values.put(arena, name, .undef);
|
|
},
|
|
.void => {
|
|
try config_header.values.put(arena, name, .defined);
|
|
},
|
|
.bool => {
|
|
try config_header.values.put(arena, name, .{ .boolean = value });
|
|
},
|
|
.int => {
|
|
try config_header.values.put(arena, name, .{ .int = value });
|
|
},
|
|
.comptime_int => {
|
|
try config_header.values.put(arena, name, .{ .int = value });
|
|
},
|
|
.@"enum", .enum_literal => {
|
|
try config_header.values.put(arena, name, .{ .ident = @tagName(value) });
|
|
},
|
|
.optional => {
|
|
if (value) |x| {
|
|
return addValueInner(config_header, name, @TypeOf(x), x);
|
|
} else {
|
|
try config_header.values.put(arena, name, .undef);
|
|
}
|
|
},
|
|
.pointer => |ptr| {
|
|
switch (@typeInfo(ptr.child)) {
|
|
.array => |array| {
|
|
if (ptr.size == .one and array.child == u8) {
|
|
try config_header.values.put(arena, name, .{ .string = value });
|
|
return;
|
|
}
|
|
},
|
|
.int => {
|
|
if (ptr.size == .slice and ptr.child == u8) {
|
|
try config_header.values.put(arena, name, .{ .string = value });
|
|
return;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
@compileError("unsupported ConfigHeader value type: " ++ @typeName(T));
|
|
},
|
|
else => @compileError("unsupported ConfigHeader value type: " ++ @typeName(T)),
|
|
}
|
|
}
|
|
|
|
pub fn addValues(config_header: *ConfigHeader, values: anytype) void {
|
|
inline for (@typeInfo(@TypeOf(values)).@"struct".fields) |field| {
|
|
addValue(config_header, field.name, field.type, @field(values, field.name));
|
|
}
|
|
}
|
|
|
|
pub fn getOutputDir(ch: *ConfigHeader) std.Build.LazyPath {
|
|
return .{ .generated = .{ .index = ch.generated_dir } };
|
|
}
|
|
pub fn getOutputFile(ch: *ConfigHeader) std.Build.LazyPath {
|
|
return ch.getOutputDir().path(ch.step.owner, ch.include_path);
|
|
}
|