const std = @import("std"); const Builder = std.build.Builder; pub fn Sdk(comptime deps: anytype) type { return struct { pub const Options = struct { /// Defaults to true on Windows d3d12: ?bool = null, /// Defaults to true on Darwin metal: ?bool = null, /// Defaults to true on Linux, Fuchsia // TODO(build-system): enable on Windows if we can cross compile Vulkan vulkan: ?bool = null, /// Defaults to true on Linux desktop_gl: ?bool = null, /// Defaults to true on Android, Linux, Windows, Emscripten // TODO(build-system): not respected at all currently opengl_es: ?bool = null, /// Whether or not minimal debug symbols should be emitted. This is -g1 in most cases, enough to /// produce stack traces but omitting debug symbols for locals. For spirv-tools and tint in /// specific, -g0 will be used (no debug symbols at all) to save an additional ~39M. debug: bool = false, /// Whether or not to produce separate static libraries for each component of Dawn (reduces /// iteration times when building from source / testing changes to Dawn source code.) separate_libs: bool = false, /// Whether to build Dawn from source or not. from_source: bool = false, /// Produce static libraries at zig-out/lib install_libs: bool = false, /// The binary release version to use from https://github.com/hexops/mach-gpu-dawn/releases binary_version: []const u8 = "release-4d79835", /// Detects the default options to use for the given target. pub fn detectDefaults(self: Options, target: std.Target) Options { const tag = target.os.tag; var options = self; if (options.d3d12 == null) options.d3d12 = tag == .windows; if (options.metal == null) options.metal = tag.isDarwin(); if (options.vulkan == null) options.vulkan = tag == .fuchsia or isLinuxDesktopLike(tag); // TODO(build-system): technically Dawn itself defaults desktop_gl to true on Windows. if (options.desktop_gl == null) options.desktop_gl = isLinuxDesktopLike(tag); // TODO(build-system): OpenGL ES options.opengl_es = false; // if (options.opengl_es == null) options.opengl_es = tag == .windows or tag == .emscripten or target.isAndroid() or linux_desktop_like; return options; } }; pub fn link(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !void { const opt = options.detectDefaults(step.target_info.target); try if (options.from_source) linkFromSource(b, step, opt) else linkFromBinary(b, step, opt); } fn linkFromSource(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !void { try ensureGitRepoCloned(b.allocator, "https://github.com/hexops/dawn", "generated-2023-01-12.1673546526", sdkPath("/libs/dawn")); // branch: mach try ensureGitRepoCloned(b.allocator, "https://github.com/hexops/DirectXShaderCompiler", "cff9a6f0b7f961748b822e1d313a7205dfdecf9d", sdkPath("/libs/DirectXShaderCompiler")); step.addIncludePath(sdkPath("/libs/dawn/out/Debug/gen/include")); step.addIncludePath(sdkPath("/libs/dawn/include")); step.addIncludePath(sdkPath("/src/dawn")); if (options.separate_libs) { const lib_mach_dawn_native = try buildLibMachDawnNative(b, step, options); step.linkLibrary(lib_mach_dawn_native); const lib_dawn_common = try buildLibDawnCommon(b, step, options); step.linkLibrary(lib_dawn_common); const lib_dawn_platform = try buildLibDawnPlatform(b, step, options); step.linkLibrary(lib_dawn_platform); // dawn-native const lib_abseil_cpp = try buildLibAbseilCpp(b, step, options); step.linkLibrary(lib_abseil_cpp); const lib_dawn_native = try buildLibDawnNative(b, step, options); step.linkLibrary(lib_dawn_native); if (options.d3d12.?) { const lib_dxcompiler = try buildLibDxcompiler(b, step, options); step.linkLibrary(lib_dxcompiler); } const lib_dawn_wire = try buildLibDawnWire(b, step, options); step.linkLibrary(lib_dawn_wire); const lib_dawn_utils = try buildLibDawnUtils(b, step, options); step.linkLibrary(lib_dawn_utils); const lib_spirv_tools = try buildLibSPIRVTools(b, step, options); step.linkLibrary(lib_spirv_tools); const lib_tint = try buildLibTint(b, step, options); step.linkLibrary(lib_tint); return; } const lib_dawn = b.addStaticLibrary("dawn", null); lib_dawn.setBuildMode(if (options.debug) .Debug else .ReleaseFast); lib_dawn.setTarget(step.target); lib_dawn.linkLibCpp(); if (options.install_libs) lib_dawn.install(); step.linkLibrary(lib_dawn); _ = try buildLibMachDawnNative(b, lib_dawn, options); _ = try buildLibDawnCommon(b, lib_dawn, options); _ = try buildLibDawnPlatform(b, lib_dawn, options); _ = try buildLibAbseilCpp(b, lib_dawn, options); _ = try buildLibDawnNative(b, lib_dawn, options); _ = try buildLibDawnWire(b, lib_dawn, options); _ = try buildLibDawnUtils(b, lib_dawn, options); _ = try buildLibSPIRVTools(b, lib_dawn, options); _ = try buildLibTint(b, lib_dawn, options); if (options.d3d12.?) _ = try buildLibDxcompiler(b, lib_dawn, options); } fn ensureGitRepoCloned(allocator: std.mem.Allocator, clone_url: []const u8, revision: []const u8, dir: []const u8) !void { if (isEnvVarTruthy(allocator, "NO_ENSURE_SUBMODULES") or isEnvVarTruthy(allocator, "NO_ENSURE_GIT")) { return; } ensureGit(allocator); if (std.fs.openDirAbsolute(dir, .{})) |_| { const current_revision = try getCurrentGitRevision(allocator, dir); if (!std.mem.eql(u8, current_revision, revision)) { // Reset to the desired revision exec(allocator, &[_][]const u8{ "git", "fetch" }, dir) catch |err| std.debug.print("warning: failed to 'git fetch' in {s}: {s}\n", .{ dir, @errorName(err) }); try exec(allocator, &[_][]const u8{ "git", "reset", "--quiet", "--hard", revision }, dir); try exec(allocator, &[_][]const u8{ "git", "submodule", "update", "--init", "--recursive" }, dir); } return; } else |err| return switch (err) { error.FileNotFound => { std.log.info("cloning required dependency..\ngit clone {s} {s}..\n", .{ clone_url, dir }); try exec(allocator, &[_][]const u8{ "git", "clone", "-c", "core.longpaths=true", clone_url, dir }, sdkPath("/")); try exec(allocator, &[_][]const u8{ "git", "reset", "--quiet", "--hard", revision }, dir); try exec(allocator, &[_][]const u8{ "git", "submodule", "update", "--init", "--recursive" }, dir); return; }, else => err, }; } fn exec(allocator: std.mem.Allocator, argv: []const []const u8, cwd: []const u8) !void { var child = std.ChildProcess.init(argv, allocator); child.cwd = cwd; _ = try child.spawnAndWait(); } fn getCurrentGitRevision(allocator: std.mem.Allocator, cwd: []const u8) ![]const u8 { const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "rev-parse", "HEAD" }, .cwd = cwd }); allocator.free(result.stderr); if (result.stdout.len > 0) return result.stdout[0 .. result.stdout.len - 1]; // trim newline return result.stdout; } fn ensureGit(allocator: std.mem.Allocator) void { const argv = &[_][]const u8{ "git", "--version" }; const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv, .cwd = ".", }) catch { // e.g. FileNotFound std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{}); std.process.exit(1); }; defer { allocator.free(result.stderr); allocator.free(result.stdout); } if (result.term.Exited != 0) { std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{}); std.process.exit(1); } } fn isEnvVarTruthy(allocator: std.mem.Allocator, name: []const u8) bool { if (std.process.getEnvVarOwned(allocator, name)) |truthy| { defer allocator.free(truthy); if (std.mem.eql(u8, truthy, "true")) return true; return false; } else |_| { return false; } } fn getGitHubBaseURLOwned(allocator: std.mem.Allocator) ![]const u8 { if (std.process.getEnvVarOwned(allocator, "MACH_GITHUB_BASE_URL")) |base_url| { std.log.info("mach: respecting MACH_GITHUB_BASE_URL: {s}\n", .{base_url}); return base_url; } else |_| { return allocator.dupe(u8, "https://github.com"); } } pub fn linkFromBinary(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !void { const target = step.target_info.target; const binaries_available = switch (target.os.tag) { .windows => target.abi.isGnu(), .linux => (target.cpu.arch.isX86() or target.cpu.arch.isAARCH64()) and (target.abi.isGnu() or target.abi.isMusl()), .macos => blk: { if (!target.cpu.arch.isX86() and !target.cpu.arch.isAARCH64()) break :blk false; // If min. target macOS version is lesser than the min version we have available, then // our binary is incompatible with the target. const min_available = std.builtin.Version{ .major = 12, .minor = 0 }; if (target.os.version_range.semver.min.order(min_available) == .lt) break :blk false; break :blk true; }, else => false, }; if (!binaries_available) { const zig_triple = try target.zigTriple(b.allocator); defer b.allocator.free(zig_triple); std.log.err("gpu-dawn binaries for {s} not available.", .{zig_triple}); std.log.err("-> open an issue: https://github.com/hexops/mach/issues", .{}); std.log.err("-> build from source (takes 5-15 minutes):", .{}); std.log.err(" use -Ddawn-from-source=true or set `Options.from_source = true`\n", .{}); if (target.os.tag == .macos) { std.log.err("", .{}); if (target.cpu.arch.isX86()) std.log.err("-> Did you mean to use -Dtarget=x86_64-macos.12 ?", .{}); if (target.cpu.arch.isAARCH64()) std.log.err("-> Did you mean to use -Dtarget=aarch64-macos.12 ?", .{}); } std.process.exit(1); } // Remove OS version range / glibc version from triple (we do not include that in our download // URLs.) var binary_target = std.zig.CrossTarget.fromTarget(target); binary_target.os_version_min = .{ .none = undefined }; binary_target.os_version_max = .{ .none = undefined }; binary_target.glibc_version = null; const zig_triple = try binary_target.zigTriple(b.allocator); defer b.allocator.free(zig_triple); try ensureBinaryDownloaded(b.allocator, zig_triple, options.debug, target.os.tag == .windows, options.binary_version); const base_cache_dir_rel = try std.fs.path.join(b.allocator, &.{ "zig-cache", "mach", "gpu-dawn" }); try std.fs.cwd().makePath(base_cache_dir_rel); const base_cache_dir = try std.fs.cwd().realpathAlloc(b.allocator, base_cache_dir_rel); const commit_cache_dir = try std.fs.path.join(b.allocator, &.{ base_cache_dir, options.binary_version }); const release_tag = if (options.debug) "debug" else "release-fast"; const target_cache_dir = try std.fs.path.join(b.allocator, &.{ commit_cache_dir, zig_triple, release_tag }); const include_dir = try std.fs.path.join(b.allocator, &.{ commit_cache_dir, "include" }); defer { b.allocator.free(base_cache_dir); b.allocator.free(commit_cache_dir); b.allocator.free(target_cache_dir); b.allocator.free(include_dir); } deps.system_sdk.include(b, step, .{}); step.addLibraryPath(target_cache_dir); step.linkSystemLibraryName("dawn"); step.linkLibCpp(); step.addIncludePath(include_dir); step.addIncludePath(sdkPath("/src/dawn")); if (isLinuxDesktopLike(step.target_info.target.os.tag)) { step.linkSystemLibraryName("X11"); } if (options.metal.?) { step.linkFramework("Metal"); step.linkFramework("CoreFoundation"); step.linkFramework("CoreGraphics"); step.linkFramework("Foundation"); step.linkFramework("IOKit"); step.linkFramework("IOSurface"); step.linkFramework("QuartzCore"); step.linkSystemLibraryName("objc"); } if (options.d3d12.?) { step.linkSystemLibraryName("ole32"); step.linkSystemLibraryName("dxguid"); } } pub fn ensureBinaryDownloaded( allocator: std.mem.Allocator, zig_triple: []const u8, is_debug: bool, is_windows: bool, version: []const u8, ) !void { // If zig-cache/mach/gpu-dawn/ does not exist: // If on a commit in the main branch => rm -r zig-cache/mach/gpu-dawn/ // else => noop // If zig-cache/mach/gpu-dawn// exists: // noop // else: // Download archive to zig-cache/mach/gpu-dawn/download/macos-aarch64 // Extract to zig-cache/mach/gpu-dawn//macos-aarch64/libgpu.a // Remove zig-cache/mach/gpu-dawn/download const base_cache_dir_rel = try std.fs.path.join(allocator, &.{ "zig-cache", "mach", "gpu-dawn" }); try std.fs.cwd().makePath(base_cache_dir_rel); const base_cache_dir = try std.fs.cwd().realpathAlloc(allocator, base_cache_dir_rel); const commit_cache_dir = try std.fs.path.join(allocator, &.{ base_cache_dir, version }); defer { allocator.free(base_cache_dir_rel); allocator.free(base_cache_dir); allocator.free(commit_cache_dir); } if (!dirExists(commit_cache_dir)) { // Commit cache dir does not exist. If the commit we're on is in the main branch, we're // probably moving to a newer commit and so we should cleanup older cached binaries. const current_git_commit = try getCurrentGitCommit(allocator); if (gitBranchContainsCommit(allocator, "main", current_git_commit) catch false) { std.fs.deleteTreeAbsolute(base_cache_dir) catch {}; } } const release_tag = if (is_debug) "debug" else "release-fast"; const target_cache_dir = try std.fs.path.join(allocator, &.{ commit_cache_dir, zig_triple, release_tag }); defer allocator.free(target_cache_dir); if (dirExists(target_cache_dir)) { return; // nothing to do, already have the binary } downloadBinary(allocator, commit_cache_dir, release_tag, target_cache_dir, zig_triple, is_windows, version) catch |err| { // A download failed, or extraction failed, so wipe out the directory to ensure we correctly // try again next time. std.fs.deleteTreeAbsolute(base_cache_dir) catch {}; std.log.err("mach/gpu-dawn: prebuilt binary download failed: {s}", .{@errorName(err)}); std.process.exit(1); }; } fn downloadBinary( allocator: std.mem.Allocator, commit_cache_dir: []const u8, release_tag: []const u8, target_cache_dir: []const u8, zig_triple: []const u8, is_windows: bool, version: []const u8, ) !void { ensureCanDownloadFiles(allocator); const download_dir = try std.fs.path.join(allocator, &.{ target_cache_dir, "download" }); defer allocator.free(download_dir); try std.fs.cwd().makePath(download_dir); // Replace "..." with "---" because GitHub releases has very weird restrictions on file names. // https://twitter.com/slimsag/status/1498025997987315713 const github_triple = try std.mem.replaceOwned(u8, allocator, zig_triple, "...", "---"); defer allocator.free(github_triple); // Compose the download URL, e.g.: // https://github.com/hexops/mach-gpu-dawn/releases/download/release-6b59025/libdawn_x86_64-macos-none_debug.a.gz const github_base_url = try getGitHubBaseURLOwned(allocator); defer allocator.free(github_base_url); const lib_prefix = if (is_windows) "dawn_" else "libdawn_"; const lib_ext = if (is_windows) ".lib" else ".a"; const lib_file_name = if (is_windows) "dawn.lib" else "libdawn.a"; const download_url = try std.mem.concat(allocator, u8, &.{ github_base_url, "/hexops/mach-gpu-dawn/releases/download/", version, "/", lib_prefix, github_triple, "_", release_tag, lib_ext, ".gz", }); defer allocator.free(download_url); // Download and decompress libdawn const gz_target_file = try std.fs.path.join(allocator, &.{ download_dir, "compressed.gz" }); defer allocator.free(gz_target_file); try downloadFile(allocator, gz_target_file, download_url); const target_file = try std.fs.path.join(allocator, &.{ target_cache_dir, lib_file_name }); defer allocator.free(target_file); try gzipDecompress(allocator, gz_target_file, target_file); // If we don't yet have the headers (these are shared across architectures), download them. const include_dir = try std.fs.path.join(allocator, &.{ commit_cache_dir, "include" }); defer allocator.free(include_dir); if (!dirExists(include_dir)) { // Compose the headers download URL, e.g.: // https://github.com/hexops/mach-gpu-dawn/releases/download/release-6b59025/headers.json.gz const headers_download_url = try std.mem.concat(allocator, u8, &.{ github_base_url, "/hexops/mach-gpu-dawn/releases/download/", version, "/headers.json.gz", }); defer allocator.free(headers_download_url); // Download and decompress headers.json.gz const headers_gz_target_file = try std.fs.path.join(allocator, &.{ download_dir, "headers.json.gz" }); defer allocator.free(headers_gz_target_file); try downloadFile(allocator, headers_gz_target_file, headers_download_url); const headers_target_file = try std.fs.path.join(allocator, &.{ target_cache_dir, "headers.json" }); defer allocator.free(headers_target_file); try gzipDecompress(allocator, headers_gz_target_file, headers_target_file); // Extract headers JSON archive. try extractHeaders(allocator, headers_target_file, commit_cache_dir); } try std.fs.deleteTreeAbsolute(download_dir); } fn extractHeaders(allocator: std.mem.Allocator, json_file: []const u8, out_dir: []const u8) !void { const contents = try std.fs.cwd().readFileAlloc(allocator, json_file, std.math.maxInt(usize)); defer allocator.free(contents); var parser = std.json.Parser.init(allocator, false); defer parser.deinit(); var tree = try parser.parse(contents); defer tree.deinit(); var iter = tree.root.Object.iterator(); while (iter.next()) |f| { const out_path = try std.fs.path.join(allocator, &.{ out_dir, f.key_ptr.* }); defer allocator.free(out_path); try std.fs.cwd().makePath(std.fs.path.dirname(out_path).?); var new_file = try std.fs.createFileAbsolute(out_path, .{}); defer new_file.close(); try new_file.writeAll(f.value_ptr.*.String); } } fn dirExists(path: []const u8) bool { var dir = std.fs.openDirAbsolute(path, .{}) catch return false; dir.close(); return true; } fn gzipDecompress(allocator: std.mem.Allocator, src_absolute_path: []const u8, dst_absolute_path: []const u8) !void { var file = try std.fs.openFileAbsolute(src_absolute_path, .{ .mode = .read_only }); defer file.close(); var buf_stream = std.io.bufferedReader(file.reader()); var gzip_stream = try std.compress.gzip.gzipStream(allocator, buf_stream.reader()); defer gzip_stream.deinit(); // Read and decompress the whole file const buf = try gzip_stream.reader().readAllAlloc(allocator, std.math.maxInt(usize)); defer allocator.free(buf); var new_file = try std.fs.createFileAbsolute(dst_absolute_path, .{}); defer new_file.close(); try new_file.writeAll(buf); } fn gitBranchContainsCommit(allocator: std.mem.Allocator, branch: []const u8, commit: []const u8) !bool { const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "branch", branch, "--contains", commit }, .cwd = sdkPath("/"), }); defer { allocator.free(result.stdout); allocator.free(result.stderr); } return result.term.Exited == 0; } fn getCurrentGitCommit(allocator: std.mem.Allocator) ![]const u8 { const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "rev-parse", "HEAD" }, .cwd = sdkPath("/"), }); defer allocator.free(result.stderr); if (result.stdout.len > 0) return result.stdout[0 .. result.stdout.len - 1]; // trim newline return result.stdout; } fn gitClone(allocator: std.mem.Allocator, repository: []const u8, dir: []const u8) !bool { const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "clone", repository, dir }, .cwd = sdkPath("/"), }); defer { allocator.free(result.stdout); allocator.free(result.stderr); } return result.term.Exited == 0; } fn downloadFile(allocator: std.mem.Allocator, target_file: []const u8, url: []const u8) !void { std.debug.print("downloading {s}..\n", .{url}); // Some Windows users experience `SSL certificate problem: unable to get local issuer certificate` // so we give them the option to disable SSL if they desire / don't want to debug the issue. var child = if (isEnvVarTruthy(allocator, "CURL_INSECURE")) std.ChildProcess.init(&.{ "curl", "--insecure", "-L", "-o", target_file, url }, allocator) else std.ChildProcess.init(&.{ "curl", "-L", "-o", target_file, url }, allocator); child.cwd = sdkPath("/"); child.stderr = std.io.getStdErr(); child.stdout = std.io.getStdOut(); _ = try child.spawnAndWait(); } fn ensureCanDownloadFiles(allocator: std.mem.Allocator) void { const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "curl", "--version" }, .cwd = sdkPath("/"), }) catch { // e.g. FileNotFound std.log.err("mach: error: 'curl --version' failed. Is curl not installed?", .{}); std.process.exit(1); }; defer { allocator.free(result.stderr); allocator.free(result.stdout); } if (result.term.Exited != 0) { std.log.err("mach: error: 'curl --version' failed. Is curl not installed?", .{}); std.process.exit(1); } } fn isLinuxDesktopLike(tag: std.Target.Os.Tag) bool { return switch (tag) { .linux, .freebsd, .kfreebsd, .openbsd, .dragonfly, => true, else => false, }; } pub fn appendFlags(step: *std.build.LibExeObjStep, flags: *std.ArrayList([]const u8), debug_symbols: bool, is_cpp: bool) !void { if (debug_symbols) try flags.append("-g1") else try flags.append("-g0"); if (is_cpp) try flags.append("-std=c++17"); if (isLinuxDesktopLike(step.target_info.target.os.tag)) { try flags.append("-DDAWN_USE_X11"); try flags.append("-DDAWN_USE_WAYLAND"); } } fn buildLibMachDawnNative(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dawn-native-mach", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try appendFlags(step, &cpp_flags, options.debug, true); try appendDawnEnableBackendTypeFlags(&cpp_flags, options); try cpp_flags.appendSlice(&.{ "-I" ++ deps.glfw_include_dir, include("libs/dawn/out/Debug/gen/include"), include("libs/dawn/out/Debug/gen/src"), include("libs/dawn/include"), include("libs/dawn/src"), }); if (step.target_info.target.os.tag == .windows) { try cpp_flags.appendSlice(&.{ "-D_DEBUG", "-D_MT", "-D_DLL", }); } return lib; } // Builds common sources; derived from src/common/BUILD.gn fn buildLibDawnCommon(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dawn-common", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(&.{ include("libs/dawn/src"), include("libs/dawn/out/Debug/gen/include"), include("libs/dawn/out/Debug/gen/src"), }); try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/dawn/common/", "libs/dawn/out/Debug/gen/src/dawn/common/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock", "WindowsUtils.cpp", }, }); var cpp_sources = std.ArrayList([]const u8).init(b.allocator); if (step.target_info.target.os.tag == .macos) { // TODO(build-system): pass system SDK options through deps.system_sdk.include(b, lib, .{}); lib.linkFramework("Foundation"); const abs_path = sdkPath("/libs/dawn/src/dawn/common/SystemUtils_mac.mm"); try cpp_sources.append(abs_path); } if (step.target_info.target.os.tag == .windows) { const abs_path = sdkPath("/libs/dawn/src/dawn/common/WindowsUtils.cpp"); try cpp_sources.append(abs_path); } var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try cpp_flags.appendSlice(flags.items); try appendFlags(step, &cpp_flags, options.debug, true); lib.addCSourceFiles(cpp_sources.items, cpp_flags.items); return lib; } // Build dawn platform sources; derived from src/dawn/platform/BUILD.gn fn buildLibDawnPlatform(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dawn-platform", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try appendFlags(step, &cpp_flags, options.debug, true); try cpp_flags.appendSlice(&.{ include("libs/dawn/src"), include("libs/dawn/include"), include("libs/dawn/out/Debug/gen/include"), }); var cpp_sources = std.ArrayList([]const u8).init(b.allocator); inline for ([_][]const u8{ "src/dawn/platform/DawnPlatform.cpp", "src/dawn/platform/WorkerThread.cpp", "src/dawn/platform/tracing/EventTracer.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } lib.addCSourceFiles(cpp_sources.items, cpp_flags.items); return lib; } fn appendDawnEnableBackendTypeFlags(flags: *std.ArrayList([]const u8), options: Options) !void { const d3d12 = "-DDAWN_ENABLE_BACKEND_D3D12"; const metal = "-DDAWN_ENABLE_BACKEND_METAL"; const vulkan = "-DDAWN_ENABLE_BACKEND_VULKAN"; const opengl = "-DDAWN_ENABLE_BACKEND_OPENGL"; const desktop_gl = "-DDAWN_ENABLE_BACKEND_DESKTOP_GL"; const opengl_es = "-DDAWN_ENABLE_BACKEND_OPENGLES"; const backend_null = "-DDAWN_ENABLE_BACKEND_NULL"; try flags.append(backend_null); if (options.d3d12.?) try flags.append(d3d12); if (options.metal.?) try flags.append(metal); if (options.vulkan.?) try flags.append(vulkan); if (options.desktop_gl.?) try flags.appendSlice(&.{ opengl, desktop_gl }); if (options.opengl_es.?) try flags.appendSlice(&.{ opengl, opengl_es }); } const dawn_d3d12_flags = &[_][]const u8{ "-DDAWN_NO_WINDOWS_UI", "-D__EMULATE_UUID=1", "-Wno-nonportable-include-path", "-Wno-extern-c-compat", "-Wno-invalid-noreturn", "-Wno-pragma-pack", "-Wno-microsoft-template-shadow", "-Wno-unused-command-line-argument", "-Wno-microsoft-exception-spec", "-Wno-implicit-exception-spec-mismatch", "-Wno-unknown-attributes", "-Wno-c++20-extensions", "-D_CRT_SECURE_NO_WARNINGS", "-DWIN32_LEAN_AND_MEAN", "-DD3D10_ARBITRARY_HEADER_ORDERING", "-DNOMINMAX", }; // Builds dawn native sources; derived from src/dawn/native/BUILD.gn fn buildLibDawnNative(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dawn-native", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; deps.system_sdk.include(b, lib, .{}); var flags = std.ArrayList([]const u8).init(b.allocator); try appendDawnEnableBackendTypeFlags(&flags, options); try flags.appendSlice(&.{ include("libs/dawn"), include("libs/dawn/src"), include("libs/dawn/include"), include("libs/dawn/third_party/vulkan-deps/spirv-tools/src/include"), include("libs/dawn/third_party/abseil-cpp"), include("libs/dawn/third_party/khronos"), // TODO(build-system): make these optional "-DTINT_BUILD_SPV_READER=1", "-DTINT_BUILD_SPV_WRITER=1", "-DTINT_BUILD_WGSL_READER=1", "-DTINT_BUILD_WGSL_WRITER=1", "-DTINT_BUILD_MSL_WRITER=1", "-DTINT_BUILD_HLSL_WRITER=1", "-DTINT_BUILD_GLSL_WRITER=1", include("libs/dawn/"), include("libs/dawn/include/tint"), include("libs/dawn/third_party/vulkan-deps/vulkan-tools/src/"), include("libs/dawn/out/Debug/gen/include"), include("libs/dawn/out/Debug/gen/src"), }); if (options.d3d12.?) try flags.appendSlice(dawn_d3d12_flags); try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/out/Debug/gen/src/dawn/", "libs/dawn/src/dawn/native/", "libs/dawn/src/dawn/native/utils/", "libs/dawn/src/dawn/native/stream/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock", "SpirvValidation.cpp", "XlibXcbFunctions.cpp", "dawn_proc.c", }, }); // dawn_native_gen try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/out/Debug/gen/src/dawn/native/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock", "webgpu_dawn_native_proc.cpp" }, }); // TODO(build-system): could allow enable_vulkan_validation_layers here. See src/dawn/native/BUILD.gn // TODO(build-system): allow use_angle here. See src/dawn/native/BUILD.gn // TODO(build-system): could allow use_swiftshader here. See src/dawn/native/BUILD.gn var cpp_sources = std.ArrayList([]const u8).init(b.allocator); if (options.d3d12.?) { lib.linkSystemLibraryName("dxgi"); lib.linkSystemLibraryName("dxguid"); inline for ([_][]const u8{ "src/dawn/mingw_helpers.cpp", }) |path| { const abs_path = sdkPath("/" ++ path); try cpp_sources.append(abs_path); } try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/dawn/native/d3d12/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock" }, }); } if (options.metal.?) { lib.linkFramework("Metal"); lib.linkFramework("CoreGraphics"); lib.linkFramework("Foundation"); lib.linkFramework("IOKit"); lib.linkFramework("IOSurface"); lib.linkFramework("QuartzCore"); try appendLangScannedSources(b, lib, .{ .objc = true, .rel_dirs = &.{ "libs/dawn/src/dawn/native/metal/", "libs/dawn/src/dawn/native/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock" }, }); } const tag = step.target_info.target.os.tag; if (isLinuxDesktopLike(tag)) { lib.linkSystemLibraryName("X11"); inline for ([_][]const u8{ "src/dawn/native/XlibXcbFunctions.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } inline for ([_][]const u8{ "src/dawn/native/null/DeviceNull.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } if (options.desktop_gl.? or options.vulkan.?) { inline for ([_][]const u8{ "src/dawn/native/SpirvValidation.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } if (options.desktop_gl.?) { try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/out/Debug/gen/src/dawn/native/opengl/", "libs/dawn/src/dawn/native/opengl/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock" }, }); } if (options.vulkan.?) { try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/dawn/native/vulkan/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock" }, }); try cpp_sources.append(sdkPath("/libs/dawn/" ++ "src/dawn/native/vulkan/external_memory/MemoryService.cpp")); if (isLinuxDesktopLike(tag)) { inline for ([_][]const u8{ "src/dawn/native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp", "src/dawn/native/vulkan/external_semaphore/SemaphoreServiceFD.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } else if (tag == .fuchsia) { inline for ([_][]const u8{ "src/dawn/native/vulkan/external_memory/MemoryServiceZirconHandle.cpp", "src/dawn/native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } else if (step.target_info.target.isAndroid()) { inline for ([_][]const u8{ "src/dawn/native/vulkan/external_memory/MemoryServiceAHardwareBuffer.cpp", "src/dawn/native/vulkan/external_semaphore/SemaphoreServiceFD.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } try cpp_sources.append("-DDAWN_USE_SYNC_FDS"); } else { inline for ([_][]const u8{ "src/dawn/native/vulkan/external_memory/MemoryServiceNull.cpp", "src/dawn/native/vulkan/external_semaphore/SemaphoreServiceNull.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } } // TODO(build-system): fuchsia: add is_fuchsia here from upstream source file if (options.vulkan.?) { // TODO(build-system): vulkan // if (enable_vulkan_validation_layers) { // defines += [ // "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS", // "DAWN_VK_DATA_DIR=\"$vulkan_data_subdir\"", // ] // } // if (enable_vulkan_loader) { // data_deps += [ "${dawn_vulkan_loader_dir}:libvulkan" ] // defines += [ "DAWN_ENABLE_VULKAN_LOADER" ] // } } // TODO(build-system): swiftshader // if (use_swiftshader) { // data_deps += [ // "${dawn_swiftshader_dir}/src/Vulkan:icd_file", // "${dawn_swiftshader_dir}/src/Vulkan:swiftshader_libvulkan", // ] // defines += [ // "DAWN_ENABLE_SWIFTSHADER", // "DAWN_SWIFTSHADER_VK_ICD_JSON=\"${swiftshader_icd_file_name}\"", // ] // } // } if (options.opengl_es.?) { // TODO(build-system): gles // if (use_angle) { // data_deps += [ // "${dawn_angle_dir}:libEGL", // "${dawn_angle_dir}:libGLESv2", // ] // } // } } inline for ([_][]const u8{ "src/dawn/native/null/NullBackend.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } if (options.d3d12.?) { inline for ([_][]const u8{ "src/dawn/native/d3d12/D3D12Backend.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } if (options.desktop_gl.?) { inline for ([_][]const u8{ "src/dawn/native/opengl/OpenGLBackend.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } if (options.vulkan.?) { inline for ([_][]const u8{ "src/dawn/native/vulkan/VulkanBackend.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } // TODO(build-system): vulkan // if (enable_vulkan_validation_layers) { // data_deps = // [ "${dawn_vulkan_validation_layers_dir}:vulkan_validation_layers" ] // if (!is_android) { // data_deps += // [ "${dawn_vulkan_validation_layers_dir}:vulkan_gen_json_files" ] // } // } } var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try cpp_flags.appendSlice(flags.items); try appendFlags(step, &cpp_flags, options.debug, true); lib.addCSourceFiles(cpp_sources.items, cpp_flags.items); return lib; } // Builds tint sources; derived from src/tint/BUILD.gn fn buildLibTint(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("tint", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(&.{ // TODO(build-system): make these optional "-DTINT_BUILD_SPV_READER=1", "-DTINT_BUILD_SPV_WRITER=1", "-DTINT_BUILD_WGSL_READER=1", "-DTINT_BUILD_WGSL_WRITER=1", "-DTINT_BUILD_MSL_WRITER=1", "-DTINT_BUILD_HLSL_WRITER=1", "-DTINT_BUILD_GLSL_WRITER=1", include("libs/dawn/"), include("libs/dawn/include/tint"), // Required for TINT_BUILD_SPV_READER=1 and TINT_BUILD_SPV_WRITER=1, if specified include("libs/dawn/third_party/vulkan-deps"), include("libs/dawn/third_party/vulkan-deps/spirv-tools/src"), include("libs/dawn/third_party/vulkan-deps/spirv-tools/src/include"), include("libs/dawn/third_party/vulkan-deps/spirv-headers/src/include"), include("libs/dawn/out/Debug/gen/third_party/vulkan-deps/spirv-tools/src"), include("libs/dawn/out/Debug/gen/third_party/vulkan-deps/spirv-tools/src/include"), include("libs/dawn/include"), }); // libtint_core_all_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint", "libs/dawn/src/tint/constant/", "libs/dawn/src/tint/diagnostic/", "libs/dawn/src/tint/inspector/", "libs/dawn/src/tint/reader/", "libs/dawn/src/tint/resolver/", "libs/dawn/src/tint/utils/", "libs/dawn/src/tint/text/", "libs/dawn/src/tint/type/", "libs/dawn/src/tint/transform/", "libs/dawn/src/tint/transform/utils", "libs/dawn/src/tint/writer/", "libs/dawn/src/tint/ast/", "libs/dawn/src/tint/val/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench", "printer_windows", "printer_posix", "printer_other", "glsl.cc" }, }); var cpp_sources = std.ArrayList([]const u8).init(b.allocator); const tag = step.target_info.target.os.tag; if (tag == .windows) { try cpp_sources.append(sdkPath("/libs/dawn/src/tint/diagnostic/printer_windows.cc")); } else if (tag.isDarwin() or isLinuxDesktopLike(tag)) { try cpp_sources.append(sdkPath("/libs/dawn/src/tint/diagnostic/printer_posix.cc")); } else { try cpp_sources.append(sdkPath("/libs/dawn/src/tint/diagnostic/printer_other.cc")); } // libtint_sem_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/sem/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark" }, }); // libtint_spv_reader_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/reader/spirv/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark" }, }); // libtint_spv_writer_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/writer/spirv/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench" }, }); // TODO(build-system): make optional // libtint_wgsl_reader_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/reader/wgsl/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench" }, }); // TODO(build-system): make optional // libtint_wgsl_writer_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/writer/wgsl/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench" }, }); // TODO(build-system): make optional // libtint_msl_writer_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/writer/msl/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench" }, }); // TODO(build-system): make optional // libtint_hlsl_writer_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/writer/hlsl/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench" }, }); // TODO(build-system): make optional // libtint_glsl_writer_src try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/src/tint/writer/glsl/", }, .flags = flags.items, .excluding_contains = &.{ "test", "bench" }, }); var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try cpp_flags.appendSlice(flags.items); try appendFlags(step, &cpp_flags, options.debug, true); lib.addCSourceFiles(cpp_sources.items, cpp_flags.items); return lib; } // Builds third_party/vulkan-deps/spirv-tools sources; derived from third_party/vulkan-deps/spirv-tools/src/BUILD.gn fn buildLibSPIRVTools(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("spirv-tools", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(&.{ include("libs/dawn"), include("libs/dawn/third_party/vulkan-deps/spirv-tools/src"), include("libs/dawn/third_party/vulkan-deps/spirv-tools/src/include"), include("libs/dawn/third_party/vulkan-deps/spirv-headers/src/include"), include("libs/dawn/out/Debug/gen/third_party/vulkan-deps/spirv-tools/src"), include("libs/dawn/out/Debug/gen/third_party/vulkan-deps/spirv-tools/src/include"), include("libs/dawn/third_party/vulkan-deps/spirv-headers/src/include/spirv/unified1"), }); // spvtools try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/third_party/vulkan-deps/spirv-tools/src/source/", "libs/dawn/third_party/vulkan-deps/spirv-tools/src/source/util/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark" }, }); // spvtools_val try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/third_party/vulkan-deps/spirv-tools/src/source/val/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark" }, }); // spvtools_opt try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/third_party/vulkan-deps/spirv-tools/src/source/opt/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark" }, }); // spvtools_link try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/third_party/vulkan-deps/spirv-tools/src/source/link/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark" }, }); return lib; } // Builds third_party/abseil sources; derived from: // // ``` // $ find third_party/abseil-cpp/absl | grep '\.cc' | grep -v 'test' | grep -v 'benchmark' | grep -v gaussian_distribution_gentables | grep -v print_hash_of | grep -v chi_square // ``` // fn buildLibAbseilCpp(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("abseil-cpp-common", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; deps.system_sdk.include(b, lib, .{}); const target = step.target_info.target; if (target.os.tag == .macos) lib.linkFramework("CoreFoundation"); if (target.os.tag == .windows) lib.linkSystemLibraryName("bcrypt"); var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(&.{ include("libs/dawn"), include("libs/dawn/third_party/abseil-cpp"), }); if (target.os.tag == .windows) try flags.appendSlice(&.{ "-DABSL_FORCE_THREAD_IDENTITY_MODE=2", "-DWIN32_LEAN_AND_MEAN", "-DD3D10_ARBITRARY_HEADER_ORDERING", "-D_CRT_SECURE_NO_WARNINGS", "-DNOMINMAX", include("src/dawn/zig_mingw_pthread"), }); // absl try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/third_party/abseil-cpp/absl/strings/", "libs/dawn/third_party/abseil-cpp/absl/strings/internal/", "libs/dawn/third_party/abseil-cpp/absl/strings/internal/str_format/", "libs/dawn/third_party/abseil-cpp/absl/types/", "libs/dawn/third_party/abseil-cpp/absl/flags/internal/", "libs/dawn/third_party/abseil-cpp/absl/flags/", "libs/dawn/third_party/abseil-cpp/absl/synchronization/", "libs/dawn/third_party/abseil-cpp/absl/synchronization/internal/", "libs/dawn/third_party/abseil-cpp/absl/hash/internal/", "libs/dawn/third_party/abseil-cpp/absl/debugging/", "libs/dawn/third_party/abseil-cpp/absl/debugging/internal/", "libs/dawn/third_party/abseil-cpp/absl/status/", "libs/dawn/third_party/abseil-cpp/absl/time/internal/cctz/src/", "libs/dawn/third_party/abseil-cpp/absl/time/", "libs/dawn/third_party/abseil-cpp/absl/container/internal/", "libs/dawn/third_party/abseil-cpp/absl/numeric/", "libs/dawn/third_party/abseil-cpp/absl/random/", "libs/dawn/third_party/abseil-cpp/absl/random/internal/", "libs/dawn/third_party/abseil-cpp/absl/base/internal/", "libs/dawn/third_party/abseil-cpp/absl/base/", }, .flags = flags.items, .excluding_contains = &.{ "_test", "_testing", "benchmark", "print_hash_of.cc", "gaussian_distribution_gentables.cc" }, }); return lib; } // Buids dawn wire sources; derived from src/dawn/wire/BUILD.gn fn buildLibDawnWire(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dawn-wire", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(&.{ include("libs/dawn"), include("libs/dawn/src"), include("libs/dawn/include"), include("libs/dawn/out/Debug/gen/include"), include("libs/dawn/out/Debug/gen/src"), }); try appendLangScannedSources(b, lib, .{ .rel_dirs = &.{ "libs/dawn/out/Debug/gen/src/dawn/wire/", "libs/dawn/out/Debug/gen/src/dawn/wire/client/", "libs/dawn/out/Debug/gen/src/dawn/wire/server/", "libs/dawn/src/dawn/wire/", "libs/dawn/src/dawn/wire/client/", "libs/dawn/src/dawn/wire/server/", }, .flags = flags.items, .excluding_contains = &.{ "test", "benchmark", "mock" }, }); return lib; } // Builds dawn utils sources; derived from src/dawn/utils/BUILD.gn fn buildLibDawnUtils(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dawn-utils", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; var flags = std.ArrayList([]const u8).init(b.allocator); try appendDawnEnableBackendTypeFlags(&flags, options); try flags.appendSlice(&.{ "-I" ++ deps.glfw_include_dir, include("libs/dawn/src"), include("libs/dawn/include"), include("libs/dawn/out/Debug/gen/include"), }); var cpp_sources = std.ArrayList([]const u8).init(b.allocator); inline for ([_][]const u8{ "src/dawn/utils/BackendBinding.cpp", "src/dawn/utils/NullBinding.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } if (options.d3d12.?) { inline for ([_][]const u8{ "src/dawn/utils/D3D12Binding.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } try flags.appendSlice(dawn_d3d12_flags); } if (options.metal.?) { inline for ([_][]const u8{ "src/dawn/utils/MetalBinding.mm", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } if (options.desktop_gl.?) { inline for ([_][]const u8{ "src/dawn/utils/OpenGLBinding.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } if (options.vulkan.?) { inline for ([_][]const u8{ "src/dawn/utils/VulkanBinding.cpp", }) |path| { const abs_path = sdkPath("/libs/dawn/" ++ path); try cpp_sources.append(abs_path); } } var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try cpp_flags.appendSlice(flags.items); try appendFlags(step, &cpp_flags, options.debug, true); lib.addCSourceFiles(cpp_sources.items, cpp_flags.items); return lib; } // Buids dxcompiler sources; derived from libs/DirectXShaderCompiler/CMakeLists.txt fn buildLibDxcompiler(b: *Builder, step: *std.build.LibExeObjStep, options: Options) !*std.build.LibExeObjStep { const lib = if (!options.separate_libs) step else blk: { const separate_lib = b.addStaticLibrary("dxcompiler", null); separate_lib.setBuildMode(if (options.debug) .Debug else .ReleaseFast); separate_lib.setTarget(step.target); separate_lib.linkLibCpp(); if (options.install_libs) separate_lib.install(); break :blk separate_lib; }; deps.system_sdk.include(b, lib, .{}); lib.linkSystemLibraryName("oleaut32"); lib.linkSystemLibraryName("ole32"); lib.linkSystemLibraryName("dbghelp"); lib.linkSystemLibraryName("dxguid"); lib.linkLibCpp(); var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(&.{ include("libs/"), include("libs/DirectXShaderCompiler/include/llvm/llvm_assert"), include("libs/DirectXShaderCompiler/include"), include("libs/DirectXShaderCompiler/build/include"), include("libs/DirectXShaderCompiler/build/lib/HLSL"), include("libs/DirectXShaderCompiler/build/lib/DxilPIXPasses"), include("libs/DirectXShaderCompiler/build/include"), "-DUNREFERENCED_PARAMETER(x)=", "-Wno-inconsistent-missing-override", "-Wno-missing-exception-spec", "-Wno-switch", "-Wno-deprecated-declarations", "-Wno-macro-redefined", // regex2.h and regcomp.c requires this for OUT redefinition "-DMSFT_SUPPORTS_CHILD_PROCESSES=1", "-DHAVE_LIBPSAPI=1", "-DHAVE_LIBSHELL32=1", "-DLLVM_ON_WIN32=1", }); try appendLangScannedSources(b, lib, .{ .debug_symbols = false, .rel_dirs = &.{ "libs/DirectXShaderCompiler/lib/Analysis/IPA", "libs/DirectXShaderCompiler/lib/Analysis", "libs/DirectXShaderCompiler/lib/AsmParser", "libs/DirectXShaderCompiler/lib/Bitcode/Writer", "libs/DirectXShaderCompiler/lib/DxcBindingTable", "libs/DirectXShaderCompiler/lib/DxcSupport", "libs/DirectXShaderCompiler/lib/DxilContainer", "libs/DirectXShaderCompiler/lib/DxilPIXPasses", "libs/DirectXShaderCompiler/lib/DxilRootSignature", "libs/DirectXShaderCompiler/lib/DXIL", "libs/DirectXShaderCompiler/lib/DxrFallback", "libs/DirectXShaderCompiler/lib/HLSL", "libs/DirectXShaderCompiler/lib/IRReader", "libs/DirectXShaderCompiler/lib/IR", "libs/DirectXShaderCompiler/lib/Linker", "libs/DirectXShaderCompiler/lib/Miniz", "libs/DirectXShaderCompiler/lib/Option", "libs/DirectXShaderCompiler/lib/PassPrinters", "libs/DirectXShaderCompiler/lib/Passes", "libs/DirectXShaderCompiler/lib/ProfileData", "libs/DirectXShaderCompiler/lib/Target", "libs/DirectXShaderCompiler/lib/Transforms/InstCombine", "libs/DirectXShaderCompiler/lib/Transforms/IPO", "libs/DirectXShaderCompiler/lib/Transforms/Scalar", "libs/DirectXShaderCompiler/lib/Transforms/Utils", "libs/DirectXShaderCompiler/lib/Transforms/Vectorize", }, .flags = flags.items, }); try appendLangScannedSources(b, lib, .{ .debug_symbols = false, .rel_dirs = &.{ "libs/DirectXShaderCompiler/lib/Support", }, .flags = flags.items, .excluding_contains = &.{ "DynamicLibrary.cpp", // ignore, HLSL_IGNORE_SOURCES "PluginLoader.cpp", // ignore, HLSL_IGNORE_SOURCES "Path.cpp", // ignore, LLVM_INCLUDE_TESTS "DynamicLibrary.cpp", // ignore }, }); try appendLangScannedSources(b, lib, .{ .debug_symbols = false, .rel_dirs = &.{ "libs/DirectXShaderCompiler/lib/Bitcode/Reader", }, .flags = flags.items, .excluding_contains = &.{ "BitReader.cpp", // ignore }, }); return lib; } fn appendLangScannedSources( b: *Builder, step: *std.build.LibExeObjStep, args: struct { debug_symbols: bool = false, flags: []const []const u8, rel_dirs: []const []const u8 = &.{}, objc: bool = false, excluding: []const []const u8 = &.{}, excluding_contains: []const []const u8 = &.{}, }, ) !void { var cpp_flags = std.ArrayList([]const u8).init(b.allocator); try cpp_flags.appendSlice(args.flags); try appendFlags(step, &cpp_flags, args.debug_symbols, true); const cpp_extensions: []const []const u8 = if (args.objc) &.{".mm"} else &.{ ".cpp", ".cc" }; try appendScannedSources(b, step, .{ .flags = cpp_flags.items, .rel_dirs = args.rel_dirs, .extensions = cpp_extensions, .excluding = args.excluding, .excluding_contains = args.excluding_contains, }); var flags = std.ArrayList([]const u8).init(b.allocator); try flags.appendSlice(args.flags); try appendFlags(step, &flags, args.debug_symbols, false); const c_extensions: []const []const u8 = if (args.objc) &.{".m"} else &.{".c"}; try appendScannedSources(b, step, .{ .flags = flags.items, .rel_dirs = args.rel_dirs, .extensions = c_extensions, .excluding = args.excluding, .excluding_contains = args.excluding_contains, }); } fn appendScannedSources(b: *Builder, step: *std.build.LibExeObjStep, args: struct { flags: []const []const u8, rel_dirs: []const []const u8 = &.{}, extensions: []const []const u8, excluding: []const []const u8 = &.{}, excluding_contains: []const []const u8 = &.{}, }) !void { var sources = std.ArrayList([]const u8).init(b.allocator); for (args.rel_dirs) |rel_dir| { try scanSources(b, &sources, rel_dir, args.extensions, args.excluding, args.excluding_contains); } step.addCSourceFiles(sources.items, args.flags); } /// Scans rel_dir for sources ending with one of the provided extensions, excluding relative paths /// listed in the excluded list. /// Results are appended to the dst ArrayList. fn scanSources( b: *Builder, dst: *std.ArrayList([]const u8), rel_dir: []const u8, extensions: []const []const u8, excluding: []const []const u8, excluding_contains: []const []const u8, ) !void { const abs_dir = try std.fs.path.join(b.allocator, &.{ sdkPath("/"), rel_dir }); defer b.allocator.free(abs_dir); var dir = try std.fs.openIterableDirAbsolute(abs_dir, .{}); defer dir.close(); var dir_it = dir.iterate(); while (try dir_it.next()) |entry| { if (entry.kind != .File) continue; var abs_path = try std.fs.path.join(b.allocator, &.{ abs_dir, entry.name }); abs_path = try std.fs.realpathAlloc(b.allocator, abs_path); const allowed_extension = blk: { const ours = std.fs.path.extension(entry.name); for (extensions) |ext| { if (std.mem.eql(u8, ours, ext)) break :blk true; } break :blk false; }; if (!allowed_extension) continue; const excluded = blk: { for (excluding) |excluded| { if (std.mem.eql(u8, entry.name, excluded)) break :blk true; } break :blk false; }; if (excluded) continue; const excluded_contains = blk: { for (excluding_contains) |contains| { if (std.mem.containsAtLeast(u8, entry.name, 1, contains)) break :blk true; } break :blk false; }; if (excluded_contains) continue; try dst.append(abs_path); } } fn include(comptime rel: []const u8) []const u8 { return comptime "-I" ++ sdkPath("/" ++ rel); } fn sdkPath(comptime suffix: []const u8) []const u8 { if (suffix[0] != '/') @compileError("suffix must be an absolute path"); return comptime blk: { const root_dir = std.fs.path.dirname(@src().file) orelse "."; break :blk root_dir ++ suffix; }; } }; }