| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const ConfigHeader = @This(); | 2 | const ConfigHeader = @This(); |
| 3 | const Step = std.Build.Step; | 3 | const Step = std.Build.Step; |
| | 4 | const Allocator = std.mem.Allocator; |
| 4 | | 5 | |
| 5 | pub const Style = union(enum) { | 6 | pub const Style = union(enum) { |
| 6 | /// The configure format supported by autotools. It uses `#undef foo` to | 7 | /// The configure format supported by autotools. It uses `#undef foo` to |
| ... | @@ -292,13 +293,27 @@ fn render_cmake( | ... | @@ -292,13 +293,27 @@ fn render_cmake( |
| 292 | values: std.StringArrayHashMap(Value), | 293 | values: std.StringArrayHashMap(Value), |
| 293 | src_path: []const u8, | 294 | src_path: []const u8, |
| 294 | ) !void { | 295 | ) !void { |
| | 296 | var build = step.owner; |
| | 297 | var allocator = build.allocator; |
| | 298 | |
| 295 | var values_copy = try values.clone(); | 299 | var values_copy = try values.clone(); |
| 296 | defer values_copy.deinit(); | 300 | defer values_copy.deinit(); |
| 297 | | 301 | |
| 298 | var any_errors = false; | 302 | var any_errors = false; |
| 299 | var line_index: u32 = 0; | 303 | var line_index: u32 = 0; |
| 300 | var line_it = std.mem.splitScalar(u8, contents, '\n'); | 304 | var line_it = std.mem.splitScalar(u8, contents, '\n'); |
| 301 | while (line_it.next()) |line| : (line_index += 1) { | 305 | while (line_it.next()) |raw_line| : (line_index += 1) { |
| | 306 | // if we reached the end of the buffer there is nothing worth doing anymore |
| | 307 | if (line_it.index == line_it.buffer.len) { |
| | 308 | continue; |
| | 309 | } |
| | 310 | |
| | 311 | const first_pass = replace_variables(allocator, raw_line, values, "@", "@") catch @panic("Failed to substitute"); |
| | 312 | const line = replace_variables(allocator, first_pass, values, "${", "}") catch @panic("Failed to substitute"); |
| | 313 | |
| | 314 | allocator.free(first_pass); |
| | 315 | defer allocator.free(line); |
| | 316 | |
| 302 | if (!std.mem.startsWith(u8, line, "#")) { | 317 | if (!std.mem.startsWith(u8, line, "#")) { |
| 303 | try output.appendSlice(line); | 318 | try output.appendSlice(line); |
| 304 | try output.appendSlice("\n"); | 319 | try output.appendSlice("\n"); |
| ... | @@ -313,6 +328,9 @@ fn render_cmake( | ... | @@ -313,6 +328,9 @@ fn render_cmake( |
| 313 | try output.appendSlice("\n"); | 328 | try output.appendSlice("\n"); |
| 314 | continue; | 329 | continue; |
| 315 | } | 330 | } |
| | 331 | |
| | 332 | const booldefine = std.mem.eql(u8, cmakedefine, "cmakedefine01"); |
| | 333 | |
| 316 | const name = it.next() orelse { | 334 | const name = it.next() orelse { |
| 317 | try step.addError("{s}:{d}: error: missing define name", .{ | 335 | try step.addError("{s}:{d}: error: missing define name", .{ |
| 318 | src_path, line_index + 1, | 336 | src_path, line_index + 1, |
| ... | @@ -320,19 +338,66 @@ fn render_cmake( | ... | @@ -320,19 +338,66 @@ fn render_cmake( |
| 320 | any_errors = true; | 338 | any_errors = true; |
| 321 | continue; | 339 | continue; |
| 322 | }; | 340 | }; |
| 323 | const kv = values_copy.fetchSwapRemove(name) orelse { | 341 | var value = values_copy.get(name) orelse blk: { |
| 324 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ | 342 | if (booldefine) { |
| 325 | src_path, line_index + 1, name, | 343 | break :blk Value{ .int = 0 }; |
| 326 | }); | 344 | } |
| 327 | any_errors = true; | 345 | break :blk Value.undef; |
| 328 | continue; | | |
| 329 | }; | 346 | }; |
| 330 | try renderValueC(output, name, kv.value); | | |
| 331 | } | | |
| 332 | | 347 | |
| 333 | for (values_copy.keys()) |name| { | 348 | value = blk: { |
| 334 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); | 349 | switch (value) { |
| 335 | any_errors = true; | 350 | .boolean => |b| { |
| | 351 | if (!b) { |
| | 352 | break :blk Value.undef; |
| | 353 | } |
| | 354 | }, |
| | 355 | .int => |i| { |
| | 356 | if (i == 0) { |
| | 357 | break :blk Value.undef; |
| | 358 | } |
| | 359 | }, |
| | 360 | .string => |string| { |
| | 361 | if (string.len == 0) { |
| | 362 | break :blk Value.undef; |
| | 363 | } |
| | 364 | }, |
| | 365 | |
| | 366 | else => { |
| | 367 | break :blk value; |
| | 368 | }, |
| | 369 | } |
| | 370 | }; |
| | 371 | |
| | 372 | if (booldefine) { |
| | 373 | value = blk: { |
| | 374 | switch (value) { |
| | 375 | .undef => { |
| | 376 | break :blk Value{ .boolean = false }; |
| | 377 | }, |
| | 378 | .defined => { |
| | 379 | break :blk Value{ .boolean = false }; |
| | 380 | }, |
| | 381 | .boolean => |b| { |
| | 382 | break :blk Value{ .boolean = b }; |
| | 383 | }, |
| | 384 | .int => |i| { |
| | 385 | break :blk Value{ .boolean = i != 0 }; |
| | 386 | }, |
| | 387 | .string => |string| { |
| | 388 | break :blk Value{ .boolean = string.len != 0 }; |
| | 389 | }, |
| | 390 | |
| | 391 | else => { |
| | 392 | break :blk Value{ .boolean = false }; |
| | 393 | }, |
| | 394 | } |
| | 395 | }; |
| | 396 | } else if (value != Value.undef) { |
| | 397 | value = Value{ .ident = it.rest() }; |
| | 398 | } |
| | 399 | |
| | 400 | try renderValueC(output, name, value); |
| 336 | } | 401 | } |
| 337 | | 402 | |
| 338 | if (any_errors) { | 403 | if (any_errors) { |
| ... | @@ -392,8 +457,7 @@ fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !voi | ... | @@ -392,8 +457,7 @@ fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !voi |
| 392 | .boolean => |b| { | 457 | .boolean => |b| { |
| 393 | try output.appendSlice("#define "); | 458 | try output.appendSlice("#define "); |
| 394 | try output.appendSlice(name); | 459 | try output.appendSlice(name); |
| 395 | try output.appendSlice(" "); | 460 | try output.appendSlice(if (b) " 1\n" else " 0\n"); |
| 396 | try output.appendSlice(if (b) "true\n" else "false\n"); | | |
| 397 | }, | 461 | }, |
| 398 | .int => |i| { | 462 | .int => |i| { |
| 399 | try output.writer().print("#define {s} {d}\n", .{ name, i }); | 463 | 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) ! | ... | @@ -437,3 +501,65 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! |
| 437 | }, | 501 | }, |
| 438 | } | 502 | } |
| 439 | } | 503 | } |
| | 504 | |
| | 505 | fn replace_variables( |
| | 506 | allocator: Allocator, |
| | 507 | contents: []const u8, |
| | 508 | values: std.StringArrayHashMap(Value), |
| | 509 | prefix: []const u8, |
| | 510 | suffix: []const u8, |
| | 511 | ) ![]const u8 { |
| | 512 | var content_buf = allocator.dupe(u8, contents) catch @panic("OOM"); |
| | 513 | |
| | 514 | var last_index: usize = 0; |
| | 515 | while (std.mem.indexOfPos(u8, content_buf, last_index, prefix)) |prefix_index| { |
| | 516 | const start_index = prefix_index + prefix.len; |
| | 517 | if (std.mem.indexOfPos(u8, content_buf, start_index, suffix)) |suffix_index| { |
| | 518 | const end_index = suffix_index + suffix.len; |
| | 519 | |
| | 520 | const beginline = content_buf[0..prefix_index]; |
| | 521 | const endline = content_buf[end_index..]; |
| | 522 | const key = content_buf[start_index..suffix_index]; |
| | 523 | const value = values.get(key) orelse .undef; |
| | 524 | |
| | 525 | switch (value) { |
| | 526 | .boolean => |b| { |
| | 527 | const buf = try std.fmt.allocPrint(allocator, "{s}{}{s}", .{ beginline, @intFromBool(b), endline }); |
| | 528 | last_index = start_index + 1; |
| | 529 | |
| | 530 | allocator.free(content_buf); |
| | 531 | content_buf = buf; |
| | 532 | }, |
| | 533 | .int => |i| { |
| | 534 | const buf = try std.fmt.allocPrint(allocator, "{s}{}{s}", .{ beginline, i, endline }); |
| | 535 | const isNegative = i < 0; |
| | 536 | const digits = (if (0 < i) std.math.log10(std.math.absCast(i)) else 0) + 1; |
| | 537 | last_index = start_index + @intFromBool(isNegative) + digits + 1; |
| | 538 | |
| | 539 | allocator.free(content_buf); |
| | 540 | content_buf = buf; |
| | 541 | }, |
| | 542 | .string => |string| { |
| | 543 | const buf = try std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ beginline, string, endline }); |
| | 544 | last_index = start_index + string.len + 1; |
| | 545 | |
| | 546 | allocator.free(content_buf); |
| | 547 | content_buf = buf; |
| | 548 | }, |
| | 549 | |
| | 550 | else => { |
| | 551 | const buf = try std.fmt.allocPrint(allocator, "{s}{s}", .{ beginline, endline }); |
| | 552 | last_index = start_index + 1; |
| | 553 | |
| | 554 | allocator.free(content_buf); |
| | 555 | content_buf = buf; |
| | 556 | }, |
| | 557 | } |
| | 558 | continue; |
| | 559 | } |
| | 560 | |
| | 561 | last_index = start_index + 1; |
| | 562 | } |
| | 563 | |
| | 564 | return content_buf; |
| | 565 | } |