1 /* MIT License 2 * Copyright (c) 2025 Matheus C. França 3 * See LICENSE file for details 4 */ 5 6 /// Wraps compiler commands. 7 module builder; 8 9 import std.stdio; 10 import std.process; 11 import std.array; 12 import std.string; 13 import std.algorithm : canFind, filter, any, startsWith, endsWith; 14 import std.typecons : Nullable; 15 import std.path : extension; 16 import std.exception : enforce; 17 18 /// Stores build configuration options. 19 struct BuildOptions 20 { 21 /// Target triple (e.g., x86_64-linux-gnu). 22 static Nullable!string triple; 23 /// CPU features (e.g., generic). 24 static Nullable!string cpu; 25 } 26 27 /// Provides flag filtering and transformation utilities. 28 mixin template FlagChecks() 29 { 30 /// Transforms or skips flags for Zig compatibility. 31 static string[] processFlag(string arg) @safe pure nothrow 32 { 33 static immutable string[] skipExact = [ 34 "--exclude-libs", "ALL", "--no-as-needed", "/nologo", "/NOLOGO" 35 ]; 36 if (skipExact.canFind(arg)) 37 return []; 38 if (arg.endsWith("-group")) 39 return ["-Wl,--start-group", "-Wl,--end-group"]; 40 if (arg.endsWith("-dynamic")) 41 return ["-Wl,--export-dynamic"]; 42 return [arg]; 43 } 44 45 /// Checks if a flag is Clang-specific for -cflags. 46 static bool isClangFlag(string arg) @safe pure nothrow 47 { 48 return !arg.startsWith("-Wl,") && arg != "-Wl,--start-group" && 49 arg != "-Wl,--end-group" && arg != "-Wl,--export-dynamic"; 50 } 51 } 52 53 /// Builds and executes Zig subcommands. 54 class Builder 55 { 56 /// Minimum command length for sanitizer flags (zig, cc/c++, +1 arg). 57 private enum MIN_SANITIZE_COMMAND_LENGTH = 3; 58 /// Base command length (zig, cc/c++). 59 private enum BASE_COMMAND_LENGTH = 2; 60 /// Length of "arm64-apple" prefix. 61 private enum ARM64_APPLE_PREFIX_LENGTH = "arm64-apple".length; 62 /// Length of "x86_64-apple" prefix. 63 private enum X86_64_APPLE_PREFIX_LENGTH = "x86_64-apple".length; 64 /// Length of "-unknown-unknown" suffix. 65 private enum UNKNOWN_UNKNOWN_LENGTH = "-unknown-unknown".length; 66 /// Allowed DMD architectures. 67 private static immutable ALLOWED_DMD_ARCHES = ["x86_64", "i386", "i686"]; 68 //dfmt off 69 /// Supported Zig triples. 70 private static immutable SUPPORTED_ZIG_TRIPLES = [ 71 "arc-linux-gnu", "arm-freebsd-eabihf", "arm-linux-gnueabi", "arm-linux-gnueabihf", 72 "arm-linux-musleabi", "arm-linux-musleabihf", "armeb-linux-gnueabi", 73 "armeb-linux-gnueabihf", "armeb-linux-musleabi", "armeb-linux-musleabihf", 74 "thumb-freebsd-eabihf", "thumb-linux-musleabi", "thumb-linux-musleabihf", 75 "thumb-windows-gnu", "thumbeb-linux-musleabi", "thumbeb-linux-musleabihf", 76 "aarch64-freebsd-none", "aarch64-linux-gnu", "aarch64-linux-musl", 77 "aarch64-macos-none", "aarch64-windows-gnu", "aarch64_be-linux-gnu", 78 "aarch64_be-linux-musl", "csky-linux-gnueabi", "csky-linux-gnueabihf", 79 "hexagon-linux-musl", "loongarch64-linux-gnu", "loongarch64-linux-gnusf", 80 "loongarch64-linux-musl", "loongarch64-linux-muslsf", "m68k-linux-gnu", 81 "m68k-linux-musl", "mips-linux-gnueabi", "mips-linux-gnueabihf", 82 "mips-linux-musleabi", "mips-linux-musleabihf", "mipsel-linux-gnueabi", 83 "mipsel-linux-gnueabihf", "mipsel-linux-musleabi", "mipsel-linux-musleabihf", 84 "mips64-linux-gnuabi64", "mips64-linux-gnuabin32", "mips64-linux-muslabi64", 85 "mips64-linux-muslabin32", "mips64el-linux-gnuabi64", "mips64el-linux-gnuabin32", 86 "mips64el-linux-muslabi64", "mips64el-linux-muslabin32", "powerpc-freebsd-eabihf", 87 "powerpc-linux-gnueabi", "powerpc-linux-gnueabihf", "powerpc-linux-musleabi", 88 "powerpc-linux-musleabihf", "powerpc64-freebsd-none", "powerpc64-linux-gnu", 89 "powerpc64-linux-musl", "powerpc64le-freebsd-none", "powerpc64le-linux-gnu", 90 "powerpc64le-linux-musl", "riscv32-linux-gnu", "riscv32-linux-musl", 91 "riscv64-freebsd-none", "riscv64-linux-gnu", "riscv64-linux-musl", 92 "s390x-linux-gnu", "s390x-linux-musl", "sparc-linux-gnu", "sparc64-linux-gnu", 93 "wasm32-wasi-musl", "wasm32-wasi", "wasm32-emscripten", "x86-freebsd-none", 94 "x86-linux-gnu", "x86-linux-musl", "x86-windows-gnu", "x86_64-freebsd-none", 95 "x86_64-linux-gnu", "x86_64-linux-gnux32", "x86_64-linux-musl", 96 "x86_64-linux-muslx32", "x86_64-macos-none", "x86_64-windows-gnu" 97 ]; 98 //dfmt on 99 100 private Appender!(string[]) cmds; 101 private Appender!(string[]) sourceFiles; 102 private string targetTriple; 103 private string cpu; 104 private bool isCPlusPlus; 105 private string[] warnings; 106 107 /// Creates a builder for Zig cc or c++. 108 /// Params: 109 /// useCpp = Use C++ mode if true, C mode if false. 110 this(bool useCpp = false) @safe pure nothrow 111 { 112 cmds = appender!(string[]); 113 sourceFiles = appender!(string[]); 114 cmds.put("zig"); 115 cmds.put(useCpp ? "c++" : "cc"); 116 isCPlusPlus = useCpp; 117 } 118 119 /// Adds a compiler flag, ignoring source files and target options. 120 /// Params: 121 /// arg = Flag to add. 122 /// Returns: This builder for chaining. 123 Builder addArg(string arg) @safe pure 124 { 125 auto ext = extension(arg).toLower; 126 if (ext == ".c" || ext == ".o" || ext == ".obj" || ext == ".s" 127 || ext == ".cpp" || ext == ".cxx" || ext == ".cc" || ext == ".c++") 128 return this; 129 if (arg == "--target" || arg.startsWith("--target=")) 130 return this; 131 mixin FlagChecks; 132 cmds.put(processFlag(arg)); 133 return this; 134 } 135 136 /// Adds multiple compiler flags. 137 /// Params: 138 /// args = Flags to add. 139 /// Returns: This builder for chaining. 140 Builder addArgs(string[] args) @safe pure 141 { 142 foreach (arg; args) 143 addArg(arg); 144 return this; 145 } 146 147 /// Adds a source file, enabling C++ mode for .cpp/.cxx/.cc/.c++ files. 148 /// Params: 149 /// file = Source file path (.c, .cpp, .cxx, .cc, .c++, .o, .obj, .s). 150 /// Returns: This builder for chaining. 151 Builder file(string file) @safe pure 152 { 153 auto ext = extension(file).toLower; 154 if (ext == ".cpp" || ext == ".cxx" || ext == ".cc" || ext == ".c++") 155 { 156 if (!targetTriple.endsWith("msvc")) 157 { 158 isCPlusPlus = true; 159 cmds.data[1] = "c++"; 160 } 161 } 162 else if (ext != ".c" && ext != ".o" && ext != ".obj" && ext != ".s") 163 return this; 164 sourceFiles.put(file); 165 return this; 166 } 167 168 /// Adds multiple source files. 169 /// Params: 170 /// files = Source file paths. 171 /// Returns: This builder for chaining. 172 Builder files(string[] files) @safe pure 173 { 174 foreach (f; files) 175 file(f); 176 return this; 177 } 178 179 /// Sets the target triple, transforming RISC-V, ARM, WebAssembly, Apple, and GNU-style triples. 180 /// Params: 181 /// triple = Target triple (e.g., x86_64-linux-gnu). 182 /// Returns: This builder for chaining. 183 Builder setTargetTriple(string triple) @safe pure 184 { 185 string transformedTriple = triple; 186 187 // Rename RISC-V architectures (riscv64*, riscv32*) to riscv64-, riscv32- 188 if (triple.startsWith("riscv")) 189 { 190 auto hyphenIndex = triple.indexOf('-'); 191 if (hyphenIndex > 0) 192 { 193 auto prefix = triple[0 .. hyphenIndex]; 194 if (prefix.startsWith("riscv64")) 195 transformedTriple = "riscv64" ~ triple[hyphenIndex .. $]; 196 else if (prefix.startsWith("riscv32")) 197 transformedTriple = "riscv32" ~ triple[hyphenIndex .. $]; 198 } 199 else 200 warnings ~= "Warning: Malformed RISC-V triple " ~ triple; 201 } 202 // Rename ARM architectures (armv5*, armv6*, armv7*, armv8*) to arm- 203 else if (triple.startsWith("armv")) 204 { 205 auto hyphenIndex = triple.indexOf('-'); 206 if (hyphenIndex > 0) 207 { 208 auto prefix = triple[0 .. hyphenIndex]; 209 if (prefix.startsWith("armv5") || prefix.startsWith("armv6") || 210 prefix.startsWith("armv7") || prefix.startsWith("armv8")) 211 { 212 transformedTriple = "arm" ~ triple[hyphenIndex .. $]; 213 } 214 } 215 else 216 warnings ~= "Warning: Malformed ARM triple " ~ triple; 217 } 218 // Handle WebAssembly triples 219 else if (triple.startsWith("wasm32-")) 220 { 221 auto parts = triple.split('-'); 222 if (parts.length >= 3) 223 { 224 if (parts[$ - 1] == "wasm" && parts.length >= 4) 225 { 226 string abi = parts[$ - 2]; 227 if (abi == "emscripten") 228 transformedTriple = "wasm32-emscripten"; 229 else if (parts.length == 4 && parts[1] == "unknown" && parts[2] == "unknown") 230 transformedTriple = "wasm32-freestanding"; 231 else 232 transformedTriple = "wasm32-" ~ abi; 233 } 234 else if (parts[$ - 1] == "musl" && parts.length >= 3) 235 { 236 string abi = parts[$ - 2]; 237 transformedTriple = "wasm32-" ~ abi; 238 } 239 else 240 { 241 string abi = parts[$ - 1]; 242 transformedTriple = "wasm32-" ~ abi; 243 } 244 } 245 else 246 warnings ~= "Warning: Malformed WebAssembly triple " ~ triple; 247 } 248 249 // Apply existing transformations 250 if (transformedTriple.startsWith("arm64-apple")) 251 targetTriple = "aarch64" ~ transformedTriple[ARM64_APPLE_PREFIX_LENGTH .. $]; 252 else if (transformedTriple.startsWith("x86_64-apple")) 253 targetTriple = "x86_64" ~ transformedTriple[X86_64_APPLE_PREFIX_LENGTH .. $]; 254 else if (transformedTriple.endsWith("-unknown-unknown")) 255 targetTriple = transformedTriple[0 .. $ - UNKNOWN_UNKNOWN_LENGTH] ~ "-freestanding"; 256 else if (transformedTriple.canFind("-unknown-")) 257 { 258 auto parts = transformedTriple.split("-unknown-"); 259 if (parts.length == 2) 260 targetTriple = parts[0] ~ "-" ~ parts[1]; 261 else 262 targetTriple = transformedTriple; 263 } 264 else 265 targetTriple = transformedTriple; 266 267 // Store warning if triple is not supported by Zig 268 if (!SUPPORTED_ZIG_TRIPLES.canFind(targetTriple)) 269 warnings ~= "Warning: Target triple " ~ targetTriple ~ " is not in Zig's supported triple list"; 270 return this; 271 } 272 273 /// Sets CPU features. 274 /// Params: 275 /// cpu = CPU feature string (e.g., generic). 276 /// Returns: This builder for chaining. 277 Builder setCpu(string cpu) @safe pure nothrow 278 { 279 this.cpu = cpu; 280 return this; 281 } 282 283 /// Builds the Zig command with sanitizer flags if needed. 284 /// Returns: Command array for execution. 285 string[] build() @safe pure 286 { 287 auto result = cmds.data.dup ~ sourceFiles.data; 288 if (!targetTriple.empty) 289 result ~= ["-target", targetTriple]; 290 if (!cpu.empty) 291 result ~= ["-mcpu=" ~ cpu]; 292 if (result.length > MIN_SANITIZE_COMMAND_LENGTH) 293 result ~= "-fno-sanitize=all"; 294 return result; 295 } 296 297 /// Builds a static or dynamic library with Zig build-lib. 298 /// Params: 299 /// libpath = Output file path. 300 /// isShared = Build a dynamic library if true, static if false. 301 /// Returns: Exit status (0 for success). 302 int buildLibrary(string libpath, bool isShared = false) @trusted 303 { 304 if (sourceFiles.data.length == 0) 305 { 306 stderr.writeln("Error: No source files specified for library build"); 307 return 1; 308 } 309 auto cmd = ["zig", "build-lib"] ~ sourceFiles.data; 310 cmd ~= ["-femit-bin=" ~ libpath, "-OReleaseFast"]; // disble ubsan 311 if (isShared) 312 cmd ~= ["-dynamic"]; 313 if (!targetTriple.empty) 314 cmd ~= ["-target", targetTriple]; 315 if (!cpu.empty) 316 cmd ~= ["-mcpu=" ~ cpu]; 317 318 mixin FlagChecks; 319 auto clangFlags = cmds.data.filter!(isClangFlag).array; 320 if (clangFlags.length) 321 cmd ~= ["-cflags"] ~ clangFlags[2 .. $] ~ ["--"]; 322 cmd ~= isCPlusPlus && !targetTriple.endsWith("msvc") ? "-lc++" : "-lc"; 323 324 debug 325 { 326 write("[zig build-lib] flags: \""); 327 foreach (c; cmd[2 .. $]) 328 write(c, " "); 329 writeln("\""); 330 } 331 332 // Log warnings before execution 333 foreach (warning; warnings) 334 stderr.writeln(warning); 335 return executeCommand(cmd, "build-lib"); 336 } 337 338 /// Executes the Zig command, printing flags in debug mode. 339 /// Returns: Exit status (0 for success). 340 int execute() @trusted 341 { 342 auto cmd = build(); 343 if (cmd.length > BASE_COMMAND_LENGTH) 344 { 345 debug 346 { 347 write("[zig ", cmds.data[1], "] flags: \""); 348 foreach (c; cmd[BASE_COMMAND_LENGTH .. $]) 349 write(c, " "); 350 writeln("\""); 351 } 352 } 353 354 // Log warnings before execution 355 foreach (warning; warnings) 356 stderr.writeln(warning); 357 return executeCommand(cmd, cmds.data[1]); 358 } 359 360 /// Executes a command, enforcing DMD architecture restrictions. 361 /// Params: 362 /// cmd = Command array to execute. 363 /// mode = Command mode (e.g., cc, c++, build-lib). 364 /// Returns: Exit status (0 for success). 365 private int executeCommand(string[] cmd, string mode) @trusted 366 { 367 version (DMD) 368 { 369 if (!targetTriple.empty && targetTriple != "native-native" && 370 !ALLOWED_DMD_ARCHES.any!(arch => targetTriple.canFind(arch))) 371 { 372 stderr.writeln("Error: DMD only supports x86/x86_64 or -target native-native"); 373 return 1; 374 } 375 } 376 377 try 378 { 379 auto result = std.process.execute(cmd); 380 if (result.output.length) 381 write(result.output); 382 enforce(result.status == 0, format("Zig %s failed with exit code %d: %s", 383 mode, result.status, result.output)); 384 return result.status; 385 } 386 catch (ProcessException e) 387 { 388 stderr.writeln("Error executing zig ", mode, ": ", e.msg); 389 return 1; 390 } 391 } 392 } 393 394 /// Unit tests for Builder. 395 version (unittest) 396 { 397 import std.exception : assertThrown; 398 import std.algorithm : any; 399 400 @("Skip excluded flags") 401 unittest 402 { 403 auto builder = new Builder(); 404 builder.addArg("--no-as-needed").addArg("--exclude-libs").addArg("/nologo"); 405 assert(builder.build() == ["zig", "cc"]); 406 } 407 408 @("Transform -group and -dynamic flags") 409 unittest 410 { 411 auto builder = new Builder(); 412 builder.addArg("-group"); 413 assert(builder.build() == [ 414 "zig", "cc", "-Wl,--start-group", "-Wl,--end-group", 415 "-fno-sanitize=all" 416 ]); 417 } 418 419 @("Preserve explicit library flags") 420 unittest 421 { 422 auto builder = new Builder(); 423 builder.addArg("-lm"); 424 assert(builder.build() == ["zig", "cc", "-lm"]); 425 } 426 427 @("Set target triple and CPU") 428 unittest 429 { 430 auto builder = new Builder(); 431 builder.setTargetTriple("arm64-apple-macos").setCpu("generic"); 432 assert(builder.build() == [ 433 "zig", "cc", "-target", "aarch64-macos", "-mcpu=generic", 434 "-fno-sanitize=all" 435 ]); 436 } 437 438 @("Detect C++ mode from file extension") 439 unittest 440 { 441 auto builder = new Builder(); 442 builder.file("test.cpp"); 443 assert(builder.build() == ["zig", "c++", "test.cpp"]); 444 } 445 446 @("DMD rejects non-x86/x86_64 targets") 447 unittest 448 { 449 version (DMD) 450 { 451 auto builder = new Builder(); 452 builder.setTargetTriple("wasm32-wasi-musl"); 453 assert(builder.execute() == 1); 454 } 455 } 456 457 @("LDC allows all targets") 458 unittest 459 { 460 version (LDC) 461 { 462 auto builder = new Builder(); 463 builder.setTargetTriple("riscv64-linux-gnu"); 464 assert(builder.build().canFind("riscv64-linux-gnu")); 465 } 466 } 467 468 @("Pass through Clang flags") 469 unittest 470 { 471 auto builder = new Builder(); 472 builder.file("test.c").addArg("-Wall").addArg("-std=c99").addArg("-h"); 473 assert(builder.build() == [ 474 "zig", "cc", "-Wall", "-std=c99", "-h", "test.c", "-fno-sanitize=all" 475 ]); 476 } 477 478 @("DMD allows x86_64 target") 479 unittest 480 { 481 version (DMD) 482 { 483 auto builder = new Builder(); 484 builder.setTargetTriple("x86_64-linux-gnu"); 485 assert(builder.build().canFind("x86_64-linux-gnu")); 486 } 487 } 488 489 @("Pass --help to zig") 490 unittest 491 { 492 auto builder = new Builder(); 493 builder.addArg("--help"); 494 assert(builder.build() == ["zig", "cc", "--help"]); 495 } 496 497 @("Throw on failed execution") 498 unittest 499 { 500 auto builder = new Builder(); 501 builder.file("nonexistent.c"); 502 assertThrown!Exception(builder.execute()); 503 } 504 505 @("Detect C++ mode with any flag") 506 unittest 507 { 508 auto builder = new Builder(); 509 builder.file("test.cpp").addArg("-some-flag"); 510 assert(builder.build() == [ 511 "zig", "c++", "-some-flag", "test.cpp", "-fno-sanitize=all" 512 ]); 513 } 514 515 @("DMD allows native-native target") 516 unittest 517 { 518 version (DMD) 519 { 520 auto builder = new Builder(); 521 builder.setTargetTriple("native-native"); 522 assert(builder.build() == [ 523 "zig", "cc", "-target", "native-native", "-fno-sanitize=all" 524 ]); 525 } 526 } 527 528 @("Throw on invalid Clang flag") 529 unittest 530 { 531 auto builder = new Builder(); 532 builder.file("test.c").addArg("-invalid-flag"); 533 assertThrown!Exception(builder.execute()); 534 } 535 536 @("Pass -h to zig") 537 unittest 538 { 539 auto builder = new Builder(); 540 builder.addArg("-h"); 541 assert(builder.build() == ["zig", "cc", "-h"]); 542 } 543 544 @("MSVC target avoids zig c++") 545 unittest 546 { 547 auto builder = new Builder(); 548 builder.setTargetTriple("x86_64-windows-msvc").file("test.cc"); 549 assert(builder.build() == [ 550 "zig", "cc", "test.cc", "-target", "x86_64-windows-msvc", 551 "-fno-sanitize=all" 552 ]); 553 } 554 555 @("Transform arm64-apple-ios to aarch64-ios") 556 unittest 557 { 558 auto builder = new Builder(); 559 builder.setTargetTriple("arm64-apple-ios").file("test.c"); 560 assert(builder.build() == [ 561 "zig", "cc", "test.c", "-target", "aarch64-ios", "-fno-sanitize=all" 562 ]); 563 } 564 565 @("MSVC target with native-windows-msvc") 566 unittest 567 { 568 auto builder = new Builder(); 569 builder.setTargetTriple("native-windows-msvc").file("test.cc"); 570 assert(builder.build() == [ 571 "zig", "cc", "test.cc", "-target", "native-windows-msvc", 572 "-fno-sanitize=all" 573 ]); 574 } 575 576 @("Build library with C source") 577 unittest 578 { 579 auto builder = new Builder(); 580 builder.file("test.c").addArg("-Wall"); 581 assertThrown!Exception(builder.buildLibrary("test.lib")); 582 } 583 584 @("Build library with C++ source") 585 unittest 586 { 587 auto builder = new Builder(); 588 builder.file("test.cpp").addArg("-std=c++11"); 589 assertThrown!Exception(builder.buildLibrary("libtest.a")); 590 } 591 592 @("Build library with extra flags") 593 unittest 594 { 595 auto builder = new Builder(); 596 builder.file("test.c").addArg("-Wall").file("rc.s"); 597 assertThrown!Exception(builder.buildLibrary("test.dylib")); 598 } 599 600 @("Add object file") 601 unittest 602 { 603 auto builder = new Builder(); 604 builder.file("test.o"); 605 assert(builder.build() == ["zig", "cc", "test.o"]); 606 } 607 608 @("Add assembly file") 609 unittest 610 { 611 auto builder = new Builder(); 612 builder.file("test.s"); 613 assert(builder.build() == ["zig", "cc", "test.s"]); 614 } 615 616 @("Transform armv5te-linux-gnu to arm-linux-gnu") 617 unittest 618 { 619 auto builder = new Builder(); 620 builder.setTargetTriple("armv5te-linux-gnu"); 621 assert(builder.build() == [ 622 "zig", "cc", "-target", "arm-linux-gnu", "-fno-sanitize=all" 623 ]); 624 } 625 626 @("Transform armv6-linux-musl to arm-linux-musl") 627 unittest 628 { 629 auto builder = new Builder(); 630 builder.setTargetTriple("armv6-linux-musl"); 631 assert(builder.build() == [ 632 "zig", "cc", "-target", "arm-linux-musl", "-fno-sanitize=all" 633 ]); 634 } 635 636 @("Transform armv7-linux-musl to arm-linux-musl") 637 unittest 638 { 639 auto builder = new Builder(); 640 builder.setTargetTriple("armv7-linux-musl"); 641 assert(builder.build() == [ 642 "zig", "cc", "-target", "arm-linux-musl", "-fno-sanitize=all" 643 ]); 644 } 645 646 @("Transform armv8a-freestanding to arm-freestanding") 647 unittest 648 { 649 auto builder = new Builder(); 650 builder.setTargetTriple("armv8a-freestanding"); 651 assert(builder.build() == [ 652 "zig", "cc", "-target", "arm-freestanding", "-fno-sanitize=all" 653 ]); 654 } 655 656 @("Transform riscv64gc-linux-gnu to riscv64-linux-gnu") 657 unittest 658 { 659 auto builder = new Builder(); 660 builder.setTargetTriple("riscv64gc-linux-gnu"); 661 assert(builder.build() == [ 662 "zig", "cc", "-target", "riscv64-linux-gnu", "-fno-sanitize=all" 663 ]); 664 } 665 666 @("Transform riscv32i-linux-musl to riscv32-linux-musl") 667 unittest 668 { 669 auto builder = new Builder(); 670 builder.setTargetTriple("riscv32i-linux-musl"); 671 assert(builder.build() == [ 672 "zig", "cc", "-target", "riscv32-linux-musl", "-fno-sanitize=all" 673 ]); 674 } 675 676 @("Transform wasm32-unknown-unknown-wasm to wasm32-freestanding") 677 unittest 678 { 679 auto builder = new Builder(); 680 builder.setTargetTriple("wasm32-unknown-unknown-wasm"); 681 assert(builder.build() == [ 682 "zig", "cc", "-target", "wasm32-freestanding", "-fno-sanitize=all" 683 ]); 684 } 685 686 @("Transform wasm32-unknown-emscripten-wasm to wasm32-emscripten") 687 unittest 688 { 689 auto builder = new Builder(); 690 builder.setTargetTriple("wasm32-unknown-emscripten-wasm"); 691 assert(builder.build() == [ 692 "zig", "cc", "-target", "wasm32-emscripten", "-fno-sanitize=all" 693 ]); 694 } 695 696 @("Support wasm32-wasi-musl triple") 697 unittest 698 { 699 auto builder = new Builder(); 700 builder.setTargetTriple("wasm32-wasi-musl"); 701 assert(builder.build() == [ 702 "zig", "cc", "-target", "wasm32-wasi", "-fno-sanitize=all" 703 ]); 704 } 705 }