| ... | ... | @@ -4,9 +4,11 @@ const Step = std.Build.Step; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | |
| 6 | 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 | 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 | 12 | /// The configure format supported by CMake. It uses `@FOO@`, `${}` and |
| 11 | 13 | /// `#cmakedefine` for template substitution. |
| 12 | 14 | cmake: std.Build.LazyPath, |
| ... | ... | @@ -17,7 +19,7 @@ pub const Style = union(enum) { |
| 17 | 19 | |
| 18 | 20 | pub fn getPath(style: Style) ?std.Build.LazyPath { |
| 19 | 21 | switch (style) { |
| 20 | | .autoconf, .cmake => |s| return s, |
| 22 | .autoconf_undef, .autoconf_at, .cmake => |s| return s, |
| 21 | 23 | .blank, .nasm => return null, |
| 22 | 24 | } |
| 23 | 25 | } |
| ... | ... | @@ -191,7 +193,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 191 | 193 | const asm_generated_line = "; " ++ header_text ++ "\n"; |
| 192 | 194 | |
| 193 | 195 | switch (config_header.style) { |
| 194 | | .autoconf => |file_source| { |
| 196 | .autoconf_undef, .autoconf_at => |file_source| { |
| 195 | 197 | try output.appendSlice(c_generated_line); |
| 196 | 198 | const src_path = file_source.getPath2(b, step); |
| 197 | 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 | 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 | 210 | .cmake => |file_source| { |
| 205 | 211 | try output.appendSlice(c_generated_line); |
| ... | ... | @@ -257,7 +263,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 257 | 263 | try man.writeManifest(); |
| 258 | 264 | } |
| 259 | 265 | |
| 260 | | fn render_autoconf( |
| 266 | fn render_autoconf_undef( |
| 261 | 267 | step: *Step, |
| 262 | 268 | contents: []const u8, |
| 263 | 269 | output: *std.ArrayList(u8), |
| ... | ... | @@ -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 | 369 | fn render_cmake( |
| 308 | 370 | step: *Step, |
| 309 | 371 | contents: []const u8, |
| ... | ... | @@ -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 | 654 | fn expand_variables_cmake( |
| 540 | 655 | allocator: Allocator, |
| 541 | 656 | contents: []const u8, |