| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const ConfigHeader = @This(); |
| 3 | 3 | const Step = std.Build.Step; |
| 4 | const Allocator = std.mem.Allocator; |
| 4 | 5 | |
| 5 | 6 | pub const Style = union(enum) { |
| 6 | 7 | /// The configure format supported by autotools. It uses `#undef foo` to |
| ... | ... | @@ -292,13 +293,27 @@ fn render_cmake( |
| 292 | 293 | values: std.StringArrayHashMap(Value), |
| 293 | 294 | src_path: []const u8, |
| 294 | 295 | ) !void { |
| 296 | var build = step.owner; |
| 297 | var allocator = build.allocator; |
| 298 | |
| 295 | 299 | var values_copy = try values.clone(); |
| 296 | 300 | defer values_copy.deinit(); |
| 297 | 301 | |
| 298 | 302 | var any_errors = false; |
| 299 | 303 | var line_index: u32 = 0; |
| 300 | 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 | 317 | if (!std.mem.startsWith(u8, line, "#")) { |
| 303 | 318 | try output.appendSlice(line); |
| 304 | 319 | try output.appendSlice("\n"); |
| ... | ... | @@ -313,6 +328,9 @@ fn render_cmake( |
| 313 | 328 | try output.appendSlice("\n"); |
| 314 | 329 | continue; |
| 315 | 330 | } |
| 331 | |
| 332 | const booldefine = std.mem.eql(u8, cmakedefine, "cmakedefine01"); |
| 333 | |
| 316 | 334 | const name = it.next() orelse { |
| 317 | 335 | try step.addError("{s}:{d}: error: missing define name", .{ |
| 318 | 336 | src_path, line_index + 1, |
| ... | ... | @@ -320,19 +338,66 @@ fn render_cmake( |
| 320 | 338 | any_errors = true; |
| 321 | 339 | continue; |
| 322 | 340 | }; |
| 323 | | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 324 | | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 325 | | src_path, line_index + 1, name, |
| 326 | | }); |
| 327 | | any_errors = true; |
| 328 | | continue; |
| 341 | var value = values_copy.get(name) orelse blk: { |
| 342 | if (booldefine) { |
| 343 | break :blk Value{ .int = 0 }; |
| 344 | } |
| 345 | break :blk Value.undef; |
| 329 | 346 | }; |
| 330 | | try renderValueC(output, name, kv.value); |
| 331 | | } |
| 332 | 347 | |
| 333 | | for (values_copy.keys()) |name| { |
| 334 | | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 335 | | any_errors = true; |
| 348 | value = blk: { |
| 349 | switch (value) { |
| 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 | 403 | if (any_errors) { |
| ... | ... | @@ -392,8 +457,7 @@ fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !voi |
| 392 | 457 | .boolean => |b| { |
| 393 | 458 | try output.appendSlice("#define "); |
| 394 | 459 | try output.appendSlice(name); |
| 395 | | try output.appendSlice(" "); |
| 396 | | try output.appendSlice(if (b) "true\n" else "false\n"); |
| 460 | try output.appendSlice(if (b) " 1\n" else " 0\n"); |
| 397 | 461 | }, |
| 398 | 462 | .int => |i| { |
| 399 | 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 | 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 | } |