mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Merge pull request #16037 from Jan200101/PR/cmakedefine-fix
Correct cmakedefine implementation
This commit is contained in:
commit
b129f1b046
@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const ConfigHeader = @This();
|
||||
const Step = std.Build.Step;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const Style = union(enum) {
|
||||
/// The configure format supported by autotools. It uses `#undef foo` to
|
||||
@ -292,13 +293,27 @@ fn render_cmake(
|
||||
values: std.StringArrayHashMap(Value),
|
||||
src_path: []const u8,
|
||||
) !void {
|
||||
var build = step.owner;
|
||||
var allocator = build.allocator;
|
||||
|
||||
var values_copy = try values.clone();
|
||||
defer values_copy.deinit();
|
||||
|
||||
var any_errors = false;
|
||||
var line_index: u32 = 0;
|
||||
var line_it = std.mem.splitScalar(u8, contents, '\n');
|
||||
while (line_it.next()) |line| : (line_index += 1) {
|
||||
while (line_it.next()) |raw_line| : (line_index += 1) {
|
||||
// if we reached the end of the buffer there is nothing worth doing anymore
|
||||
if (line_it.index == line_it.buffer.len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const first_pass = replace_variables(allocator, raw_line, values, "@", "@") catch @panic("Failed to substitute");
|
||||
const line = replace_variables(allocator, first_pass, values, "${", "}") catch @panic("Failed to substitute");
|
||||
|
||||
allocator.free(first_pass);
|
||||
defer allocator.free(line);
|
||||
|
||||
if (!std.mem.startsWith(u8, line, "#")) {
|
||||
try output.appendSlice(line);
|
||||
try output.appendSlice("\n");
|
||||
@ -313,6 +328,9 @@ fn render_cmake(
|
||||
try output.appendSlice("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
const booldefine = std.mem.eql(u8, cmakedefine, "cmakedefine01");
|
||||
|
||||
const name = it.next() orelse {
|
||||
try step.addError("{s}:{d}: error: missing define name", .{
|
||||
src_path, line_index + 1,
|
||||
@ -320,19 +338,66 @@ fn render_cmake(
|
||||
any_errors = true;
|
||||
continue;
|
||||
};
|
||||
const kv = values_copy.fetchSwapRemove(name) orelse {
|
||||
try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{
|
||||
src_path, line_index + 1, name,
|
||||
});
|
||||
any_errors = true;
|
||||
continue;
|
||||
var value = values_copy.get(name) orelse blk: {
|
||||
if (booldefine) {
|
||||
break :blk Value{ .int = 0 };
|
||||
}
|
||||
break :blk Value.undef;
|
||||
};
|
||||
try renderValueC(output, name, kv.value);
|
||||
}
|
||||
|
||||
for (values_copy.keys()) |name| {
|
||||
try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name });
|
||||
any_errors = true;
|
||||
value = blk: {
|
||||
switch (value) {
|
||||
.boolean => |b| {
|
||||
if (!b) {
|
||||
break :blk Value.undef;
|
||||
}
|
||||
},
|
||||
.int => |i| {
|
||||
if (i == 0) {
|
||||
break :blk Value.undef;
|
||||
}
|
||||
},
|
||||
.string => |string| {
|
||||
if (string.len == 0) {
|
||||
break :blk Value.undef;
|
||||
}
|
||||
},
|
||||
|
||||
else => {
|
||||
break :blk value;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if (booldefine) {
|
||||
value = blk: {
|
||||
switch (value) {
|
||||
.undef => {
|
||||
break :blk Value{ .boolean = false };
|
||||
},
|
||||
.defined => {
|
||||
break :blk Value{ .boolean = false };
|
||||
},
|
||||
.boolean => |b| {
|
||||
break :blk Value{ .boolean = b };
|
||||
},
|
||||
.int => |i| {
|
||||
break :blk Value{ .boolean = i != 0 };
|
||||
},
|
||||
.string => |string| {
|
||||
break :blk Value{ .boolean = string.len != 0 };
|
||||
},
|
||||
|
||||
else => {
|
||||
break :blk Value{ .boolean = false };
|
||||
},
|
||||
}
|
||||
};
|
||||
} else if (value != Value.undef) {
|
||||
value = Value{ .ident = it.rest() };
|
||||
}
|
||||
|
||||
try renderValueC(output, name, value);
|
||||
}
|
||||
|
||||
if (any_errors) {
|
||||
@ -392,8 +457,7 @@ fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !voi
|
||||
.boolean => |b| {
|
||||
try output.appendSlice("#define ");
|
||||
try output.appendSlice(name);
|
||||
try output.appendSlice(" ");
|
||||
try output.appendSlice(if (b) "true\n" else "false\n");
|
||||
try output.appendSlice(if (b) " 1\n" else " 0\n");
|
||||
},
|
||||
.int => |i| {
|
||||
try output.writer().print("#define {s} {d}\n", .{ name, i });
|
||||
@ -437,3 +501,65 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_variables(
|
||||
allocator: Allocator,
|
||||
contents: []const u8,
|
||||
values: std.StringArrayHashMap(Value),
|
||||
prefix: []const u8,
|
||||
suffix: []const u8,
|
||||
) ![]const u8 {
|
||||
var content_buf = allocator.dupe(u8, contents) catch @panic("OOM");
|
||||
|
||||
var last_index: usize = 0;
|
||||
while (std.mem.indexOfPos(u8, content_buf, last_index, prefix)) |prefix_index| {
|
||||
const start_index = prefix_index + prefix.len;
|
||||
if (std.mem.indexOfPos(u8, content_buf, start_index, suffix)) |suffix_index| {
|
||||
const end_index = suffix_index + suffix.len;
|
||||
|
||||
const beginline = content_buf[0..prefix_index];
|
||||
const endline = content_buf[end_index..];
|
||||
const key = content_buf[start_index..suffix_index];
|
||||
const value = values.get(key) orelse .undef;
|
||||
|
||||
switch (value) {
|
||||
.boolean => |b| {
|
||||
const buf = try std.fmt.allocPrint(allocator, "{s}{}{s}", .{ beginline, @intFromBool(b), endline });
|
||||
last_index = start_index + 1;
|
||||
|
||||
allocator.free(content_buf);
|
||||
content_buf = buf;
|
||||
},
|
||||
.int => |i| {
|
||||
const buf = try std.fmt.allocPrint(allocator, "{s}{}{s}", .{ beginline, i, endline });
|
||||
const isNegative = i < 0;
|
||||
const digits = (if (0 < i) std.math.log10(std.math.absCast(i)) else 0) + 1;
|
||||
last_index = start_index + @intFromBool(isNegative) + digits + 1;
|
||||
|
||||
allocator.free(content_buf);
|
||||
content_buf = buf;
|
||||
},
|
||||
.string => |string| {
|
||||
const buf = try std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ beginline, string, endline });
|
||||
last_index = start_index + string.len + 1;
|
||||
|
||||
allocator.free(content_buf);
|
||||
content_buf = buf;
|
||||
},
|
||||
|
||||
else => {
|
||||
const buf = try std.fmt.allocPrint(allocator, "{s}{s}", .{ beginline, endline });
|
||||
last_index = start_index + 1;
|
||||
|
||||
allocator.free(content_buf);
|
||||
content_buf = buf;
|
||||
},
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
last_index = start_index + 1;
|
||||
}
|
||||
|
||||
return content_buf;
|
||||
}
|
||||
|
||||
@ -226,6 +226,10 @@ pub const build_cases = [_]BuildCase{
|
||||
.build_root = "test/standalone/strip_empty_loop",
|
||||
.import = @import("standalone/strip_empty_loop/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/standalone/cmakedefine",
|
||||
.import = @import("standalone/cmakedefine/build.zig"),
|
||||
},
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
56
test/standalone/cmakedefine/build.zig
Normal file
56
test/standalone/cmakedefine/build.zig
Normal file
@ -0,0 +1,56 @@
|
||||
const std = @import("std");
|
||||
const ConfigHeader = std.Build.Step.ConfigHeader;
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const config_header = b.addConfigHeader(
|
||||
.{
|
||||
.style = .{ .cmake = .{ .path = "config.h.cmake" } },
|
||||
},
|
||||
.{
|
||||
.noval = null,
|
||||
.trueval = true,
|
||||
.falseval = false,
|
||||
.zeroval = 0,
|
||||
.oneval = 1,
|
||||
.tenval = 10,
|
||||
.stringval = "test",
|
||||
|
||||
.boolnoval = void{},
|
||||
.booltrueval = true,
|
||||
.boolfalseval = false,
|
||||
.boolzeroval = 0,
|
||||
.booloneval = 1,
|
||||
.booltenval = 10,
|
||||
.boolstringval = "test",
|
||||
},
|
||||
);
|
||||
|
||||
const test_step = b.step("test", "Test it");
|
||||
test_step.makeFn = compare_headers;
|
||||
test_step.dependOn(&config_header.step);
|
||||
}
|
||||
|
||||
fn compare_headers(step: *std.Build.Step, prog_node: *std.Progress.Node) !void {
|
||||
_ = prog_node;
|
||||
const allocator = step.owner.allocator;
|
||||
const cmake_header_path = "expected.h";
|
||||
|
||||
const config_header_step = step.dependencies.getLast();
|
||||
const config_header = @fieldParentPtr(ConfigHeader, "step", config_header_step);
|
||||
|
||||
const zig_header_path = config_header.output_file.path orelse @panic("Could not locate header file");
|
||||
|
||||
const cwd = std.fs.cwd();
|
||||
|
||||
const cmake_header = try cwd.readFileAlloc(allocator, cmake_header_path, config_header.max_bytes);
|
||||
defer allocator.free(cmake_header);
|
||||
|
||||
const zig_header = try cwd.readFileAlloc(allocator, zig_header_path, config_header.max_bytes);
|
||||
defer allocator.free(zig_header);
|
||||
|
||||
const header_text_index = std.mem.indexOf(u8, zig_header, "\n") orelse @panic("Could not find comment in header filer");
|
||||
|
||||
if (!std.mem.eql(u8, zig_header[header_text_index + 1 ..], cmake_header)) {
|
||||
@panic("processed cmakedefine header does not match expected output");
|
||||
}
|
||||
}
|
||||
93
test/standalone/cmakedefine/config.h.cmake
Normal file
93
test/standalone/cmakedefine/config.h.cmake
Normal file
@ -0,0 +1,93 @@
|
||||
// cmakedefine
|
||||
// undefined
|
||||
#cmakedefine noval unreachable
|
||||
|
||||
// 1
|
||||
#cmakedefine trueval 1
|
||||
|
||||
// undefined
|
||||
#cmakedefine falseval unreachable
|
||||
|
||||
// undefined
|
||||
#cmakedefine zeroval unreachable
|
||||
|
||||
// 1
|
||||
#cmakedefine oneval 1
|
||||
|
||||
// 1
|
||||
#cmakedefine tenval 1
|
||||
|
||||
// 1
|
||||
#cmakedefine stringval 1
|
||||
|
||||
|
||||
// cmakedefine01
|
||||
// 0
|
||||
#cmakedefine01 boolnoval
|
||||
|
||||
// 1
|
||||
#cmakedefine01 booltrueval
|
||||
|
||||
// 0
|
||||
#cmakedefine01 boolfalseval
|
||||
|
||||
// 0
|
||||
#cmakedefine01 boolzeroval
|
||||
|
||||
// 1
|
||||
#cmakedefine01 booloneval
|
||||
|
||||
// 1
|
||||
#cmakedefine01 booltenval
|
||||
|
||||
// 1
|
||||
#cmakedefine01 boolstringval
|
||||
|
||||
|
||||
// @ substition
|
||||
|
||||
// no substition
|
||||
// @noval@
|
||||
|
||||
// 1
|
||||
// @trueval@
|
||||
|
||||
// 0
|
||||
// @falseval@
|
||||
|
||||
// 0
|
||||
// @zeroval@
|
||||
|
||||
// 1
|
||||
// @oneval@
|
||||
|
||||
// 10
|
||||
// @tenval@
|
||||
|
||||
// test
|
||||
// @stringval@
|
||||
|
||||
|
||||
// ${} substition
|
||||
|
||||
// removal
|
||||
// ${noval}
|
||||
|
||||
// 1
|
||||
// ${trueval}
|
||||
|
||||
// 0
|
||||
// ${falseval}
|
||||
|
||||
// 0
|
||||
// ${zeroval}
|
||||
|
||||
// 1
|
||||
// ${oneval}
|
||||
|
||||
// 10
|
||||
// ${tenval}
|
||||
|
||||
// test
|
||||
// ${stringval}
|
||||
|
||||
93
test/standalone/cmakedefine/expected.h
Normal file
93
test/standalone/cmakedefine/expected.h
Normal file
@ -0,0 +1,93 @@
|
||||
// cmakedefine
|
||||
// undefined
|
||||
/* #undef noval */
|
||||
|
||||
// 1
|
||||
#define trueval 1
|
||||
|
||||
// undefined
|
||||
/* #undef falseval */
|
||||
|
||||
// undefined
|
||||
/* #undef zeroval */
|
||||
|
||||
// 1
|
||||
#define oneval 1
|
||||
|
||||
// 1
|
||||
#define tenval 1
|
||||
|
||||
// 1
|
||||
#define stringval 1
|
||||
|
||||
|
||||
// cmakedefine01
|
||||
// 0
|
||||
#define boolnoval 0
|
||||
|
||||
// 1
|
||||
#define booltrueval 1
|
||||
|
||||
// 0
|
||||
#define boolfalseval 0
|
||||
|
||||
// 0
|
||||
#define boolzeroval 0
|
||||
|
||||
// 1
|
||||
#define booloneval 1
|
||||
|
||||
// 1
|
||||
#define booltenval 1
|
||||
|
||||
// 1
|
||||
#define boolstringval 1
|
||||
|
||||
|
||||
// @ substition
|
||||
|
||||
// no substition
|
||||
//
|
||||
|
||||
// 1
|
||||
// 1
|
||||
|
||||
// 0
|
||||
// 0
|
||||
|
||||
// 0
|
||||
// 0
|
||||
|
||||
// 1
|
||||
// 1
|
||||
|
||||
// 10
|
||||
// 10
|
||||
|
||||
// test
|
||||
// test
|
||||
|
||||
|
||||
// substition
|
||||
|
||||
// removal
|
||||
//
|
||||
|
||||
// 1
|
||||
// 1
|
||||
|
||||
// 0
|
||||
// 0
|
||||
|
||||
// 0
|
||||
// 0
|
||||
|
||||
// 1
|
||||
// 1
|
||||
|
||||
// 10
|
||||
// 10
|
||||
|
||||
// test
|
||||
// test
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user