| ... | @@ -4,9 +4,11 @@ const Step = std.Build.Step; | ... | @@ -4,9 +4,11 @@ const Step = std.Build.Step; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | | 5 | |
| 6 | pub const Style = union(enum) { | 6 | pub const Style = union(enum) { |
| 7 | /// The configure format supported by autotools. It uses `#undef foo` to | 7 | /// A configure format supported by autotools that uses `#undef foo` to |
| 8 | /// mark lines that can be substituted with different values. | 8 | /// mark lines that can be substituted with different values. |
| 9 | autoconf: std.Build.LazyPath, | 9 | autoconf_undef: std.Build.LazyPath, |
| | 10 | /// A configure format supported by autotools that uses `@FOO@` output variables. |
| | 11 | autoconf_at: std.Build.LazyPath, |
| 10 | /// The configure format supported by CMake. It uses `@FOO@`, `${}` and | 12 | /// The configure format supported by CMake. It uses `@FOO@`, `${}` and |
| 11 | /// `#cmakedefine` for template substitution. | 13 | /// `#cmakedefine` for template substitution. |
| 12 | cmake: std.Build.LazyPath, | 14 | cmake: std.Build.LazyPath, |
| ... | @@ -17,7 +19,7 @@ pub const Style = union(enum) { | ... | @@ -17,7 +19,7 @@ pub const Style = union(enum) { |
| 17 | | 19 | |
| 18 | pub fn getPath(style: Style) ?std.Build.LazyPath { | 20 | pub fn getPath(style: Style) ?std.Build.LazyPath { |
| 19 | switch (style) { | 21 | switch (style) { |
| 20 | .autoconf, .cmake => |s| return s, | 22 | .autoconf_undef, .autoconf_at, .cmake => |s| return s, |
| 21 | .blank, .nasm => return null, | 23 | .blank, .nasm => return null, |
| 22 | } | 24 | } |
| 23 | } | 25 | } |
| ... | @@ -191,7 +193,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -191,7 +193,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 191 | const asm_generated_line = "; " ++ header_text ++ "\n"; | 193 | const asm_generated_line = "; " ++ header_text ++ "\n"; |
| 192 | | 194 | |
| 193 | switch (config_header.style) { | 195 | switch (config_header.style) { |
| 194 | .autoconf => |file_source| { | 196 | .autoconf_undef, .autoconf_at => |file_source| { |
| 195 | try output.appendSlice(c_generated_line); | 197 | try output.appendSlice(c_generated_line); |
| 196 | const src_path = file_source.getPath2(b, step); | 198 | const src_path = file_source.getPath2(b, step); |
| 197 | const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| { | 199 | const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| { |
| ... | @@ -199,7 +201,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -199,7 +201,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 199 | src_path, @errorName(err), | 201 | src_path, @errorName(err), |
| 200 | }); | 202 | }); |
| 201 | }; | 203 | }; |
| 202 | try render_autoconf(step, contents, &output, config_header.values, src_path); | 204 | switch (config_header.style) { |
| | 205 | .autoconf_undef => try render_autoconf_undef(step, contents, &output, config_header.values, src_path), |
| | 206 | .autoconf_at => try render_autoconf_at(step, contents, &output, config_header.values, src_path), |
| | 207 | else => unreachable, |
| | 208 | } |
| 203 | }, | 209 | }, |
| 204 | .cmake => |file_source| { | 210 | .cmake => |file_source| { |
| 205 | try output.appendSlice(c_generated_line); | 211 | try output.appendSlice(c_generated_line); |
| ... | @@ -257,7 +263,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -257,7 +263,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 257 | try man.writeManifest(); | 263 | try man.writeManifest(); |
| 258 | } | 264 | } |
| 259 | | 265 | |
| 260 | fn render_autoconf( | 266 | fn render_autoconf_undef( |
| 261 | step: *Step, | 267 | step: *Step, |
| 262 | contents: []const u8, | 268 | contents: []const u8, |
| 263 | output: *std.ArrayList(u8), | 269 | output: *std.ArrayList(u8), |
| ... | @@ -304,6 +310,62 @@ fn render_autoconf( | ... | @@ -304,6 +310,62 @@ fn render_autoconf( |
| 304 | } | 310 | } |
| 305 | } | 311 | } |
| 306 | | 312 | |
| | 313 | fn render_autoconf_at( |
| | 314 | step: *Step, |
| | 315 | contents: []const u8, |
| | 316 | output: *std.ArrayList(u8), |
| | 317 | values: std.StringArrayHashMap(Value), |
| | 318 | src_path: []const u8, |
| | 319 | ) !void { |
| | 320 | const build = step.owner; |
| | 321 | const allocator = build.allocator; |
| | 322 | |
| | 323 | const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); |
| | 324 | for (used) |*u| u.* = false; |
| | 325 | defer allocator.free(used); |
| | 326 | |
| | 327 | var any_errors = false; |
| | 328 | var line_index: u32 = 0; |
| | 329 | var line_it = std.mem.splitScalar(u8, contents, '\n'); |
| | 330 | while (line_it.next()) |line| : (line_index += 1) { |
| | 331 | const last_line = line_it.index == line_it.buffer.len; |
| | 332 | |
| | 333 | const old_len = output.items.len; |
| | 334 | expand_variables_autoconf_at(output, line, values, used) catch |err| switch (err) { |
| | 335 | error.MissingValue => { |
| | 336 | const name = output.items[old_len..]; |
| | 337 | defer output.shrinkRetainingCapacity(old_len); |
| | 338 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| | 339 | src_path, line_index + 1, name, |
| | 340 | }); |
| | 341 | any_errors = true; |
| | 342 | continue; |
| | 343 | }, |
| | 344 | else => { |
| | 345 | try step.addError("{s}:{d}: unable to substitute variable: error: {s}", .{ |
| | 346 | src_path, line_index + 1, @errorName(err), |
| | 347 | }); |
| | 348 | any_errors = true; |
| | 349 | continue; |
| | 350 | }, |
| | 351 | }; |
| | 352 | if (!last_line) { |
| | 353 | try output.append('\n'); |
| | 354 | } |
| | 355 | } |
| | 356 | |
| | 357 | for (values.unmanaged.entries.slice().items(.key), used) |name, u| { |
| | 358 | if (!u) { |
| | 359 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| | 360 | any_errors = true; |
| | 361 | } |
| | 362 | } |
| | 363 | |
| | 364 | if (any_errors) { |
| | 365 | return error.MakeFailed; |
| | 366 | } |
| | 367 | } |
| | 368 | |
| 307 | fn render_cmake( | 369 | fn render_cmake( |
| 308 | step: *Step, | 370 | step: *Step, |
| 309 | contents: []const u8, | 371 | contents: []const u8, |
| ... | @@ -536,6 +598,59 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! | ... | @@ -536,6 +598,59 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! |
| 536 | } | 598 | } |
| 537 | } | 599 | } |
| 538 | | 600 | |
| | 601 | fn expand_variables_autoconf_at( |
| | 602 | output: *std.ArrayList(u8), |
| | 603 | contents: []const u8, |
| | 604 | values: std.StringArrayHashMap(Value), |
| | 605 | used: []bool, |
| | 606 | ) !void { |
| | 607 | const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; |
| | 608 | |
| | 609 | var curr: usize = 0; |
| | 610 | var source_offset: usize = 0; |
| | 611 | while (curr < contents.len) : (curr += 1) { |
| | 612 | if (contents[curr] != '@') continue; |
| | 613 | if (std.mem.indexOfScalarPos(u8, contents, curr + 1, '@')) |close_pos| { |
| | 614 | if (close_pos == curr + 1) { |
| | 615 | // closed immediately, preserve as a literal |
| | 616 | continue; |
| | 617 | } |
| | 618 | const valid_varname_end = std.mem.indexOfNonePos(u8, contents, curr + 1, valid_varname_chars) orelse 0; |
| | 619 | if (valid_varname_end != close_pos) { |
| | 620 | // contains invalid characters, preserve as a literal |
| | 621 | continue; |
| | 622 | } |
| | 623 | |
| | 624 | const key = contents[curr + 1 .. close_pos]; |
| | 625 | const index = values.getIndex(key) orelse { |
| | 626 | // Report the missing key to the caller. |
| | 627 | try output.appendSlice(key); |
| | 628 | return error.MissingValue; |
| | 629 | }; |
| | 630 | const value = values.unmanaged.entries.slice().items(.value)[index]; |
| | 631 | used[index] = true; |
| | 632 | try output.appendSlice(contents[source_offset..curr]); |
| | 633 | switch (value) { |
| | 634 | .undef, .defined => {}, |
| | 635 | .boolean => |b| { |
| | 636 | try output.append(if (b) '1' else '0'); |
| | 637 | }, |
| | 638 | .int => |i| { |
| | 639 | try output.writer().print("{d}", .{i}); |
| | 640 | }, |
| | 641 | .ident, .string => |s| { |
| | 642 | try output.appendSlice(s); |
| | 643 | }, |
| | 644 | } |
| | 645 | |
| | 646 | curr = close_pos; |
| | 647 | source_offset = close_pos + 1; |
| | 648 | } |
| | 649 | } |
| | 650 | |
| | 651 | try output.appendSlice(contents[source_offset..]); |
| | 652 | } |
| | 653 | |
| 539 | fn expand_variables_cmake( | 654 | fn expand_variables_cmake( |
| 540 | allocator: Allocator, | 655 | allocator: Allocator, |
| 541 | contents: []const u8, | 656 | contents: []const u8, |