diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index 0b8ddca93..df9bcaac7 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -84,6 +84,42 @@ jobs: run: | sentry-cli dif upload --project ghostty --wait dsym.zip + source-tarball: + if: | + ${{ + github.event_name == 'workflow_dispatch' || + ( + github.event.workflow_run.conclusion == 'success' && + github.repository_owner == 'ghostty-org' && + github.ref_name == 'main' + ) + }} + runs-on: namespace-profile-ghostty-md + steps: + - uses: actions/checkout@v4 + - name: Create Tarball + run: git archive --format=tgz -o ghostty-source.tar.gz HEAD + - name: Sign Tarball + env: + MINISIGN_PASSWORD: ${{ secrets.MINISIGN_PASSWORD }} + MINISIGN_KEY: ${{ secrets.MINISIGN_KEY }} + run: | + echo $MINISIGN_KEY > minisign.key + echo $MINISIGN_PASSWORD | minisign -S \ + -m ghostty-source.tar.gz \ + -s minisign.key + - name: Update Release + uses: softprops/action-gh-release@v2 + with: + name: 'Ghostty Tip ("Nightly")' + prerelease: true + tag_name: tip + target_commitish: ${{ github.sha }} + files: | + ghostty-source.tar.gz + ghostty-source.tar.gz.minisig + token: ${{ secrets.GH_RELEASE_TOKEN }} + build-macos: if: | ${{ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0be64e3b..409d6b075 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,7 +50,7 @@ jobs: # Cross-compile the binary. We always use static building for this # because its the only way to access the headers. - name: Test Build - run: nix develop -c zig build -Dstatic=true -Dapp-runtime=glfw -Dtarget=${{ matrix.target }} + run: nix develop -c zig build -Dapp-runtime=glfw -Dtarget=${{ matrix.target }} build-bench: # We build benchmarks on large because it uses ReleaseFast @@ -166,7 +166,7 @@ jobs: # GhosttyKit is the framework that is built from Zig for our native # Mac app to access. - name: Build GhosttyKit - run: nix develop -c zig build -Dstatic=true + run: nix develop -c zig build # The native app is built with native XCode tooling. This also does # codesigning. IMPORTANT: this must NOT run in a Nix environment. @@ -332,8 +332,9 @@ jobs: - name: Test GLFW Build run: nix develop -c zig build -Dapp-runtime=glfw - - name: Test Dynamic Build - run: nix develop -c zig build -Dstatic=false + # This relies on the cache being populated by the commands above. + - name: Test System Build + run: nix develop -c zig build --system ${ZIG_GLOBAL_CACHE_DIR}/p test-macos: runs-on: namespace-profile-ghostty-macos diff --git a/PACKAGING.md b/PACKAGING.md new file mode 100644 index 000000000..5e6560c34 --- /dev/null +++ b/PACKAGING.md @@ -0,0 +1,93 @@ +# Packaging Ghostty for Distribution + +Ghostty relies on downstream package maintainers to distribute Ghostty to +end-users. This document provides guidance to package maintainers on how to +package Ghostty for distribution. + +> [!NOTE] +> +> While Ghostty went through an extensive private beta testing period, +> packaging Ghostty is immature and may require additional build script +> tweaks and documentation improvement. I'm extremely motivated to work with +> package maintainers to improve the packaging process. Please open issues +> to discuss any packaging issues you encounter. + +## Source Tarballs + +Source tarballs with stable checksums are available on the +[GitHub releases page](https://github.com/ghostty-org/ghostty/releases). +Use the `ghostty-source.tar.gz` asset and _not the GitHub auto-generated +source tarball_. + +Signature files are signed with [minisign](https://jedisct1.github.io/minisign/) using the following public key: + +``` +RWQlAjJC23149WL2sEpT/l0QKy7hMIFhYdQOFy0Z7z7PbneUgvlsnYcV +``` + +## Zig Version + +[Zig](https://ziglang.org) is required to build Ghostty. Prior to Zig 1.0, +Zig releases often have breaking changes. Ghostty requires specific Zig versions +depending on the Ghostty version in order to build. To make things easier for +package maintainers, Ghostty always uses some _released_ version of Zig. + +To find the version of Zig required to build Ghostty, check the `required_zig` +constant in `build.zig`. You don't need to know Zig to extract this information. +This version will always be an official released version of Zig. + +For example, at the time of writing this document, Ghostty requires Zig 0.13.0. + +## Building Ghostty + +The following is a standard example of how to build Ghostty _for system +packages_. This is not the recommended way to build Ghostty for your +own system. For that, see the primary README. + +1. First, we fetch our dependencies from the internet into a cached directory. + This is the only step that requires internet access: + +```sh +ZIG_GLOBAL_CACHE_DIR=/tmp/offline-cache ./nix/build-support/fetch-zig-cache.sh +``` + +2. Next, we build Ghostty. This step requires no internet access: + +```sh +DESTDIR=/tmp/ghostty \ +zig build \ + --prefix /usr \ + --system /tmp/offline-cache/p \ + -Doptimize=ReleaseFast \ + -Dcpu=baseline +``` + +The build options are covered in the next section, but this will build +and install Ghostty to `/tmp/ghostty` with the prefix `/usr` (i.e. the +binary will be at `/tmp/ghostty/usr/bin/ghostty`). This style is common +for system packages which separate a build and install step, since the +install step can then be done with a `mv` or `cp` command (from `/tmp/ghostty` +to wherever the package manager expects it). + +### Build Options + +Ghostty uses the Zig build system. You can see all available build options by +running `zig build --help`. The following are options that are particularly +relevant to package maintainers: + +- `--prefix`: The installation prefix. Combine with the `DESTDIR` environment + variable to install to a temporary directory for packaging. + +- `--system`: The path to the offline cache directory. This disables + any package fetching from the internet. This flag also triggers all + dependencies to be dynamically linked by default. + +- `-Doptimize=ReleaseFast`: Build with optimizations enabled and safety checks + disabled. This is the recommended build mode for distribution. I'd prefer + a safe build but terminal emulators are performance-sensitive and the + safe build is currently too slow. I plan to improve this in the future. + Other build modes are available: `Debug`, `ReleaseSafe`, and `ReleaseSmall`. + +- `-Dcpu=baseline`: Build for the "baseline" CPU of the target architecture. + This avoids building for newer CPU features that may not be available on + all target machines. diff --git a/README.md b/README.md index c8241684e..3b1077795 100644 --- a/README.md +++ b/README.md @@ -566,8 +566,8 @@ the configuration file. ### Linux Installation Tips On Linux, you'll need to install header packages for Ghostty's dependencies -before building it. Typically, these are only gtk4 and libadwaita (unless -building with `-Dstatic=false`). On Ubuntu and Debian, use +before building it. Typically, these are only gtk4 and libadwaita, since +Ghostty will build everything else static by default. On Ubuntu and Debian, use ``` sudo apt install libgtk-4-dev libadwaita-1-dev git diff --git a/build.zig b/build.zig index aa7bd54bd..f581bb96c 100644 --- a/build.zig +++ b/build.zig @@ -94,12 +94,6 @@ pub fn build(b: *std.Build) !void { "Enables the use of libadwaita when using the gtk rendering backend.", ) orelse true; - config.static = b.option( - bool, - "static", - "Statically build as much as possible for the exe", - ) orelse true; - const conformance = b.option( []const u8, "conformance", @@ -221,6 +215,44 @@ pub fn build(b: *std.Build) !void { }; }; + // These are all our dependencies that can be used with system + // packages if they exist. We set them up here so that we can set + // their defaults early. The first call configures the integration and + // subsequent calls just return the configured value. + { + // These dependencies we want to default false if we're on macOS. + // On macOS we don't want to use system libraries because we + // generally want a fat binary. This can be overridden with the + // `-fsys` flag. + for (&[_][]const u8{ + "freetype", + "harfbuzz", + "fontconfig", + "libpng", + "zlib", + "oniguruma", + }) |dep| { + _ = b.systemIntegrationOption( + dep, + .{ + // If we're not on darwin we want to use whatever the + // default is via the system package mode + .default = if (target.result.isDarwin()) false else null, + }, + ); + } + + // These default to false because they're rarely available as + // system packages so we usually want to statically link them. + for (&[_][]const u8{ + "glslang", + "spirv-cross", + "simdutf", + }) |dep| { + _ = b.systemIntegrationOption(dep, .{ .default = false }); + } + } + // We can use wasmtime to test wasm b.enable_wasmtime = true; @@ -653,10 +685,6 @@ pub fn build(b: *std.Build) !void { const wasm_config: BuildConfig = config: { var copy = config; - // Always static for the wasm app because we want all of our - // dependencies in a fat static library. - copy.static = true; - // Backends that are fixed for wasm copy.font_backend = .web_canvas; @@ -748,12 +776,7 @@ pub fn build(b: *std.Build) !void { { if (emit_test_exe) b.installArtifact(main_test); - _ = try addDeps(b, main_test, config: { - var copy = config; - copy.static = true; - break :config copy; - }); - + _ = try addDeps(b, main_test, config); const test_run = b.addRunArtifact(main_test); test_step.dependOn(&test_run.step); } @@ -807,17 +830,6 @@ fn createMacOSLib( optimize: std.builtin.OptimizeMode, config: BuildConfig, ) !struct { *std.Build.Step, std.Build.LazyPath } { - // Modify our build configuration for macOS builds. - const macos_config: BuildConfig = config: { - var copy = config; - - // Always static for the macOS app because we want all of our - // dependencies in a fat static library. - copy.static = true; - - break :config copy; - }; - const static_lib_aarch64 = lib: { const lib = b.addStaticLibrary(.{ .name = "ghostty", @@ -829,7 +841,7 @@ fn createMacOSLib( lib.linkLibC(); // Create a single static lib with all our dependencies merged - var lib_list = try addDeps(b, lib, macos_config); + var lib_list = try addDeps(b, lib, config); try lib_list.append(lib.getEmittedBin()); const libtool = LibtoolStep.create(b, .{ .name = "ghostty", @@ -853,7 +865,7 @@ fn createMacOSLib( lib.linkLibC(); // Create a single static lib with all our dependencies merged - var lib_list = try addDeps(b, lib, macos_config); + var lib_list = try addDeps(b, lib, config); try lib_list.append(lib.getEmittedBin()); const libtool = LibtoolStep.create(b, .{ .name = "ghostty", @@ -888,12 +900,6 @@ fn createIOSLib( optimize: std.builtin.OptimizeMode, config: BuildConfig, ) !struct { *std.Build.Step, std.Build.LazyPath } { - const ios_config: BuildConfig = config: { - var copy = config; - copy.static = true; - break :config copy; - }; - const lib = b.addStaticLibrary(.{ .name = "ghostty", .root_source_file = b.path("src/main_c.zig"), @@ -909,7 +915,7 @@ fn createIOSLib( lib.linkLibC(); // Create a single static lib with all our dependencies merged - var lib_list = try addDeps(b, lib, ios_config); + var lib_list = try addDeps(b, lib, config); try lib_list.append(lib.getEmittedBin()); const libtool = LibtoolStep.create(b, .{ .name = "ghostty", @@ -938,9 +944,14 @@ fn addDeps( try config.addOptions(exe_options); step.root_module.addOptions("build_options", exe_options); + // We maintain a list of our static libraries and return it so that + // we can build a single fat static library for the final app. var static_libs = LazyPathList.init(b.allocator); errdefer static_libs.deinit(); + const target = step.root_module.resolved_target.?; + const optimize = step.root_module.optimize.?; + // For dynamic linking, we prefer dynamic linking and to search by // mode first. Mode first will search all paths for a dynamic library // before falling back to static. @@ -949,106 +960,167 @@ fn addDeps( .search_strategy = .mode_first, }; - const target = step.root_module.resolved_target.?; - const optimize = step.root_module.optimize.?; + // Freetype + _ = b.systemIntegrationOption("freetype", .{}); // Shows it in help + if (config.font_backend.hasFreetype()) { + const freetype_dep = b.dependency("freetype", .{ + .target = target, + .optimize = optimize, + .@"enable-libpng" = true, + }); + step.root_module.addImport("freetype", freetype_dep.module("freetype")); - // Dependencies - const cimgui_dep = b.dependency("cimgui", .{ - .target = target, - .optimize = optimize, - }); - const js_dep = b.dependency("zig_js", .{ - .target = target, - .optimize = optimize, - }); - const libxev_dep = b.dependency("libxev", .{ - .target = target, - .optimize = optimize, - }); - const objc_dep = b.dependency("zig_objc", .{ - .target = target, - .optimize = optimize, - }); + if (b.systemIntegrationOption("freetype", .{})) { + step.linkSystemLibrary2("bzip2", dynamic_link_opts); + step.linkSystemLibrary2("freetype", dynamic_link_opts); + } else { + step.linkLibrary(freetype_dep.artifact("freetype")); + try static_libs.append(freetype_dep.artifact("freetype").getEmittedBin()); + } + } - const fontconfig_dep = b.dependency("fontconfig", .{ - .target = target, - .optimize = optimize, - }); - const freetype_dep = b.dependency("freetype", .{ - .target = target, - .optimize = optimize, - .@"enable-libpng" = true, - }); - const glslang_dep = b.dependency("glslang", .{ - .target = target, - .optimize = optimize, - }); - const spirv_cross_dep = b.dependency("spirv_cross", .{ - .target = target, - .optimize = optimize, - }); - const highway_dep = b.dependency("highway", .{ - .target = target, - .optimize = optimize, - }); - const simdutf_dep = b.dependency("simdutf", .{ - .target = target, - .optimize = optimize, - }); - const utfcpp_dep = b.dependency("utfcpp", .{ - .target = target, - .optimize = optimize, - }); - const libpng_dep = b.dependency("libpng", .{ - .target = target, - .optimize = optimize, - }); - const macos_dep = b.dependency("macos", .{ - .target = target, - .optimize = optimize, - }); + // Harfbuzz + _ = b.systemIntegrationOption("harfbuzz", .{}); // Shows it in help + if (config.font_backend.hasHarfbuzz()) { + const harfbuzz_dep = b.dependency("harfbuzz", .{ + .target = target, + .optimize = optimize, + .@"enable-freetype" = true, + .@"enable-coretext" = config.font_backend.hasCoretext(), + }); + + step.root_module.addImport( + "harfbuzz", + harfbuzz_dep.module("harfbuzz"), + ); + if (b.systemIntegrationOption("harfbuzz", .{})) { + step.linkSystemLibrary2("harfbuzz", dynamic_link_opts); + } else { + step.linkLibrary(harfbuzz_dep.artifact("harfbuzz")); + try static_libs.append(harfbuzz_dep.artifact("harfbuzz").getEmittedBin()); + } + } + + // Fontconfig + _ = b.systemIntegrationOption("fontconfig", .{}); // Shows it in help + if (config.font_backend.hasFontconfig()) { + const fontconfig_dep = b.dependency("fontconfig", .{ + .target = target, + .optimize = optimize, + }); + step.root_module.addImport( + "fontconfig", + fontconfig_dep.module("fontconfig"), + ); + + if (b.systemIntegrationOption("fontconfig", .{})) { + step.linkSystemLibrary2("fontconfig", dynamic_link_opts); + } else { + step.linkLibrary(fontconfig_dep.artifact("fontconfig")); + try static_libs.append(fontconfig_dep.artifact("fontconfig").getEmittedBin()); + } + } + + // Libpng - Ghostty doesn't actually use this directly, its only used + // through dependencies, so we only need to add it to our static + // libs list if we're not using system integration. The dependencies + // will handle linking it. + if (!b.systemIntegrationOption("libpng", .{})) { + const libpng_dep = b.dependency("libpng", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(libpng_dep.artifact("png")); + try static_libs.append(libpng_dep.artifact("png").getEmittedBin()); + } + + // Zlib - same as libpng, only used through dependencies. + if (!b.systemIntegrationOption("zlib", .{})) { + const zlib_dep = b.dependency("zlib", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(zlib_dep.artifact("z")); + try static_libs.append(zlib_dep.artifact("z").getEmittedBin()); + } + + // Oniguruma const oniguruma_dep = b.dependency("oniguruma", .{ .target = target, .optimize = optimize, }); - const opengl_dep = b.dependency("opengl", .{}); + step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma")); + if (b.systemIntegrationOption("oniguruma", .{})) { + step.linkSystemLibrary2("oniguruma", dynamic_link_opts); + } else { + step.linkLibrary(oniguruma_dep.artifact("oniguruma")); + try static_libs.append(oniguruma_dep.artifact("oniguruma").getEmittedBin()); + } + + // Glslang + const glslang_dep = b.dependency("glslang", .{ + .target = target, + .optimize = optimize, + }); + step.root_module.addImport("glslang", glslang_dep.module("glslang")); + if (b.systemIntegrationOption("glslang", .{})) { + step.linkSystemLibrary2("glslang", dynamic_link_opts); + } else { + step.linkLibrary(glslang_dep.artifact("glslang")); + try static_libs.append(glslang_dep.artifact("glslang").getEmittedBin()); + } + + // Spirv-cross + const spirv_cross_dep = b.dependency("spirv_cross", .{ + .target = target, + .optimize = optimize, + }); + step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross")); + if (b.systemIntegrationOption("spirv-cross", .{})) { + step.linkSystemLibrary2("spirv-cross", dynamic_link_opts); + } else { + step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); + try static_libs.append(spirv_cross_dep.artifact("spirv_cross").getEmittedBin()); + } + + // Simdutf + if (b.systemIntegrationOption("simdutf", .{})) { + step.linkSystemLibrary2("simdutf", dynamic_link_opts); + } else { + const simdutf_dep = b.dependency("simdutf", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(simdutf_dep.artifact("simdutf")); + try static_libs.append(simdutf_dep.artifact("simdutf").getEmittedBin()); + } + + // Sentry const sentry_dep = b.dependency("sentry", .{ .target = target, .optimize = optimize, .backend = .breakpad, }); - const zlib_dep = b.dependency("zlib", .{ - .target = target, - .optimize = optimize, - }); - const harfbuzz_dep = b.dependency("harfbuzz", .{ - .target = target, - .optimize = optimize, - .@"enable-freetype" = true, - .@"enable-coretext" = config.font_backend.hasCoretext(), - }); - const ziglyph_dep = b.dependency("ziglyph", .{ - .target = target, - .optimize = optimize, - }); - const vaxis_dep = b.dependency("vaxis", .{ - .target = target, - .optimize = optimize, - .libxev = false, - .images = false, - }); - const wuffs_dep = b.dependency("wuffs", .{ - .target = target, - .optimize = optimize, - }); - const zf_dep = b.dependency("zf", .{ - .target = target, - .optimize = optimize, - }); - const z2d_dep = b.dependency("z2d", .{}); + step.root_module.addImport("sentry", sentry_dep.module("sentry")); + if (target.result.os.tag != .windows) { + // Sentry + step.linkLibrary(sentry_dep.artifact("sentry")); + try static_libs.append(sentry_dep.artifact("sentry").getEmittedBin()); + + // We also need to include breakpad in the static libs. + const breakpad_dep = sentry_dep.builder.dependency("breakpad", .{ + .target = target, + .optimize = optimize, + }); + try static_libs.append(breakpad_dep.artifact("breakpad").getEmittedBin()); + } // Wasm we do manually since it is such a different build. if (step.rootModuleTarget().cpu.arch == .wasm32) { + const js_dep = b.dependency("zig_js", .{ + .target = target, + .optimize = optimize, + }); step.root_module.addImport("zig-js", js_dep.module("zig-js")); return static_libs; @@ -1100,9 +1172,6 @@ fn addDeps( }); } - // If we're building a lib we have some different deps - const lib = step.kind == .lib; - // We always require the system SDK so that our system headers are available. // This makes things like `os/log.h` available for cross-compiling. if (step.rootModuleTarget().isDarwin()) { @@ -1110,36 +1179,50 @@ fn addDeps( try addMetallib(b, step); } - // We always need the Zig packages - // TODO: This can't be the right way to use the new Zig modules system, - // so take a closer look at this again later. - if (config.font_backend.hasFontconfig()) step.root_module.addImport( - "fontconfig", - fontconfig_dep.module("fontconfig"), - ); - if (config.font_backend.hasHarfbuzz()) step.root_module.addImport( - "harfbuzz", - harfbuzz_dep.module("harfbuzz"), - ); - step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma")); - step.root_module.addImport("freetype", freetype_dep.module("freetype")); - step.root_module.addImport("glslang", glslang_dep.module("glslang")); - step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross")); - step.root_module.addImport("xev", libxev_dep.module("xev")); - step.root_module.addImport("opengl", opengl_dep.module("opengl")); - step.root_module.addImport("sentry", sentry_dep.module("sentry")); - step.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph")); - step.root_module.addImport("vaxis", vaxis_dep.module("vaxis")); - step.root_module.addImport("wuffs", wuffs_dep.module("wuffs")); - step.root_module.addImport("zf", zf_dep.module("zf")); + // Other dependencies, mostly pure Zig + step.root_module.addImport("opengl", b.dependency( + "opengl", + .{}, + ).module("opengl")); + step.root_module.addImport("vaxis", b.dependency("vaxis", .{ + .target = target, + .optimize = optimize, + .libxev = false, + .images = false, + }).module("vaxis")); + step.root_module.addImport("wuffs", b.dependency("wuffs", .{ + .target = target, + .optimize = optimize, + }).module("wuffs")); + step.root_module.addImport("xev", b.dependency("libxev", .{ + .target = target, + .optimize = optimize, + }).module("xev")); step.root_module.addImport("z2d", b.addModule("z2d", .{ - .root_source_file = z2d_dep.path("src/z2d.zig"), + .root_source_file = b.dependency("z2d", .{}).path("src/z2d.zig"), .target = target, .optimize = optimize, })); + step.root_module.addImport("ziglyph", b.dependency("ziglyph", .{ + .target = target, + .optimize = optimize, + }).module("ziglyph")); + step.root_module.addImport("zf", b.dependency("zf", .{ + .target = target, + .optimize = optimize, + }).module("zf")); // Mac Stuff if (step.rootModuleTarget().isDarwin()) { + const objc_dep = b.dependency("zig_objc", .{ + .target = target, + .optimize = optimize, + }); + const macos_dep = b.dependency("macos", .{ + .target = target, + .optimize = optimize, + }); + // This is a bit of a hack that should probably be fixed upstream // in zig-objc, but we need to add the apple SDK paths to the // zig-objc module so that it can find the objc runtime headers. @@ -1158,89 +1241,32 @@ fn addDeps( } // cimgui + const cimgui_dep = b.dependency("cimgui", .{ + .target = target, + .optimize = optimize, + }); step.root_module.addImport("cimgui", cimgui_dep.module("cimgui")); step.linkLibrary(cimgui_dep.artifact("cimgui")); try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin()); - // Glslang - step.linkLibrary(glslang_dep.artifact("glslang")); - try static_libs.append(glslang_dep.artifact("glslang").getEmittedBin()); - // Highway + const highway_dep = b.dependency("highway", .{ + .target = target, + .optimize = optimize, + }); step.linkLibrary(highway_dep.artifact("highway")); try static_libs.append(highway_dep.artifact("highway").getEmittedBin()); - // simdutf - step.linkLibrary(simdutf_dep.artifact("simdutf")); - try static_libs.append(simdutf_dep.artifact("simdutf").getEmittedBin()); - - // utfcpp + // utfcpp - This is used as a dependency on our hand-written C++ code + const utfcpp_dep = b.dependency("utfcpp", .{ + .target = target, + .optimize = optimize, + }); step.linkLibrary(utfcpp_dep.artifact("utfcpp")); try static_libs.append(utfcpp_dep.artifact("utfcpp").getEmittedBin()); - // Spirv-Cross - step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); - try static_libs.append(spirv_cross_dep.artifact("spirv_cross").getEmittedBin()); - - if (target.result.os.tag != .windows) { - // Sentry - step.linkLibrary(sentry_dep.artifact("sentry")); - try static_libs.append(sentry_dep.artifact("sentry").getEmittedBin()); - - // We also need to include breakpad in the static libs. - const breakpad_dep = sentry_dep.builder.dependency("breakpad", .{ - .target = target, - .optimize = optimize, - }); - try static_libs.append(breakpad_dep.artifact("breakpad").getEmittedBin()); - } - - // Dynamic link - if (!config.static) { - step.addIncludePath(freetype_dep.path("")); - step.linkSystemLibrary2("bzip2", dynamic_link_opts); - step.linkSystemLibrary2("freetype2", dynamic_link_opts); - step.linkSystemLibrary2("libpng", dynamic_link_opts); - step.linkSystemLibrary2("oniguruma", dynamic_link_opts); - step.linkSystemLibrary2("zlib", dynamic_link_opts); - - if (config.font_backend.hasFontconfig()) { - step.linkSystemLibrary2("fontconfig", dynamic_link_opts); - } - if (config.font_backend.hasHarfbuzz()) { - step.linkSystemLibrary2("harfbuzz", dynamic_link_opts); - } - } - - // Other dependencies, we may dynamically link - if (config.static) { - step.linkLibrary(oniguruma_dep.artifact("oniguruma")); - try static_libs.append(oniguruma_dep.artifact("oniguruma").getEmittedBin()); - - step.linkLibrary(zlib_dep.artifact("z")); - try static_libs.append(zlib_dep.artifact("z").getEmittedBin()); - - step.linkLibrary(libpng_dep.artifact("png")); - try static_libs.append(libpng_dep.artifact("png").getEmittedBin()); - - // Freetype - step.linkLibrary(freetype_dep.artifact("freetype")); - try static_libs.append(freetype_dep.artifact("freetype").getEmittedBin()); - - // Harfbuzz - if (config.font_backend.hasHarfbuzz()) { - step.linkLibrary(harfbuzz_dep.artifact("harfbuzz")); - try static_libs.append(harfbuzz_dep.artifact("harfbuzz").getEmittedBin()); - } - - // Only Linux gets fontconfig - if (config.font_backend.hasFontconfig()) { - // Fontconfig - step.linkLibrary(fontconfig_dep.artifact("fontconfig")); - } - } - - if (!lib) { + // If we're building an exe then we have additional dependencies. + if (step.kind != .lib) { // We always statically compile glad step.addIncludePath(b.path("vendor/glad/include/")); step.addCSourceFile(.{ @@ -1515,8 +1541,6 @@ fn benchSteps( if (install) b.installArtifact(c_exe); _ = try addDeps(b, c_exe, config: { var copy = config; - copy.static = true; - var enum_name: [64]u8 = undefined; @memcpy(enum_name[0..name.len], name); std.mem.replaceScalar(u8, enum_name[0..name.len], '-', '_'); diff --git a/nix/build-support/fetch-zig-cache.sh b/nix/build-support/fetch-zig-cache.sh old mode 100644 new mode 100755 diff --git a/nix/devShell.nix b/nix/devShell.nix index a924d853e..7f0e206b7 100644 --- a/nix/devShell.nix +++ b/nix/devShell.nix @@ -28,6 +28,7 @@ fontconfig, freetype, glib, + glslang, gtk4, libadwaita, gnome, @@ -41,8 +42,12 @@ libXi, libXinerama, libXrandr, + libxml2, + spirv-cross, + simdutf, zlib, alejandra, + minisign, pandoc, hyperfine, typos, @@ -59,9 +64,14 @@ freetype harfbuzz libpng + libxml2 oniguruma + simdutf zlib + glslang + spirv-cross + libX11 libXcursor libXi @@ -79,6 +89,7 @@ in [ # For builds llvmPackages_latest.llvm + minisign ncurses pandoc pkg-config @@ -123,9 +134,14 @@ in freetype harfbuzz libpng + libxml2 oniguruma + simdutf zlib + glslang + spirv-cross + libX11 libXcursor libXext diff --git a/nix/package.nix b/nix/package.nix index 668663dc7..889eb978f 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -7,6 +7,7 @@ freetype, harfbuzz, libpng, + oniguruma, zlib, libGL, libX11, @@ -132,6 +133,7 @@ in freetype harfbuzz libpng + oniguruma zlib libX11 diff --git a/pkg/cimgui/build.zig b/pkg/cimgui/build.zig index 94163ed98..09d340adb 100644 --- a/pkg/cimgui/build.zig +++ b/pkg/cimgui/build.zig @@ -12,11 +12,6 @@ pub fn build(b: *std.Build) !void { }); const imgui = b.dependency("imgui", .{}); - const freetype = b.dependency("freetype", .{ - .target = target, - .optimize = optimize, - .@"enable-libpng" = true, - }); const lib = b.addStaticLibrary(.{ .name = "cimgui", .target = target, @@ -24,11 +19,30 @@ pub fn build(b: *std.Build) !void { }); lib.linkLibC(); lib.linkLibCpp(); - lib.linkLibrary(freetype.artifact("freetype")); if (target.result.os.tag == .windows) { lib.linkSystemLibrary("imm32"); } + // For dynamic linking, we prefer dynamic linking and to search by + // mode first. Mode first will search all paths for a dynamic library + // before falling back to static. + const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, + }; + + if (b.systemIntegrationOption("freetype", .{})) { + lib.linkSystemLibrary2("freetype", dynamic_link_opts); + } else { + const freetype = b.dependency("freetype", .{ + .target = target, + .optimize = optimize, + .@"enable-libpng" = true, + }); + lib.linkLibrary(freetype.artifact("freetype")); + module.addIncludePath(freetype.builder.dependency("freetype", .{}).path("include")); + } + lib.addIncludePath(imgui.path("")); module.addIncludePath(b.path("vendor")); diff --git a/pkg/fontconfig/build.zig b/pkg/fontconfig/build.zig index ed66d61a5..5a4ce8eeb 100644 --- a/pkg/fontconfig/build.zig +++ b/pkg/fontconfig/build.zig @@ -25,18 +25,6 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag != .windows) { lib.linkSystemLibrary("pthread"); } - if (freetype_enabled) { - const freetype_dep = b.dependency("freetype", .{ .target = target, .optimize = optimize }); - lib.linkLibrary(freetype_dep.artifact("freetype")); - } - if (libxml2_enabled) { - const libxml2_dep = b.dependency("libxml2", .{ - .target = target, - .optimize = optimize, - .iconv = libxml2_iconv_enabled, - }); - lib.linkLibrary(libxml2_dep.artifact("xml2")); - } lib.addIncludePath(upstream.path("")); lib.addIncludePath(b.path("override/include")); @@ -142,6 +130,31 @@ pub fn build(b: *std.Build) !void { }); } } + + // For dynamic linking, we prefer dynamic linking and to search by + // mode first. Mode first will search all paths for a dynamic library + // before falling back to static. + const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, + }; + + // Freetype2 + _ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help + if (freetype_enabled) { + if (b.systemIntegrationOption("freetype", .{})) { + lib.linkSystemLibrary2("freetype", dynamic_link_opts); + } else { + const freetype_dep = b.dependency( + "freetype", + .{ .target = target, .optimize = optimize }, + ); + lib.linkLibrary(freetype_dep.artifact("freetype")); + } + } + + // Libxml2 + _ = b.systemIntegrationOption("libxml2", .{}); // So it shows up in help if (libxml2_enabled) { try flags.appendSlice(&.{ "-DENABLE_LIBXML2", @@ -154,6 +167,17 @@ pub fn build(b: *std.Build) !void { "-Werror=implicit-function-declaration", }); } + + if (b.systemIntegrationOption("libxml2", .{})) { + lib.linkSystemLibrary2("libxml-2.0", dynamic_link_opts); + } else { + const libxml2_dep = b.dependency("libxml2", .{ + .target = target, + .optimize = optimize, + .iconv = libxml2_iconv_enabled, + }); + lib.linkLibrary(libxml2_dep.artifact("xml2")); + } } lib.addCSourceFiles(.{ diff --git a/pkg/freetype/build.zig b/pkg/freetype/build.zig index f62c6e487..46458c15c 100644 --- a/pkg/freetype/build.zig +++ b/pkg/freetype/build.zig @@ -23,13 +23,13 @@ pub fn build(b: *std.Build) !void { module.addIncludePath(upstream.path("include")); module.addIncludePath(b.path("")); - // Dependencies - const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize }); - lib.linkLibrary(zlib_dep.artifact("z")); - if (libpng_enabled) { - const libpng_dep = b.dependency("libpng", .{ .target = target, .optimize = optimize }); - lib.linkLibrary(libpng_dep.artifact("png")); - } + // For dynamic linking, we prefer dynamic linking and to search by + // mode first. Mode first will search all paths for a dynamic library + // before falling back to static. + const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, + }; var flags = std.ArrayList([]const u8).init(b.allocator); defer flags.deinit(); @@ -43,7 +43,30 @@ pub fn build(b: *std.Build) !void { "-fno-sanitize=undefined", }); - if (libpng_enabled) try flags.append("-DFT_CONFIG_OPTION_USE_PNG=1"); + + // Zlib + if (b.systemIntegrationOption("zlib", .{})) { + lib.linkSystemLibrary2("zlib", dynamic_link_opts); + } else { + const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize }); + lib.linkLibrary(zlib_dep.artifact("z")); + } + + // Libpng + _ = b.systemIntegrationOption("libpng", .{}); // So it shows up in help + if (libpng_enabled) { + try flags.append("-DFT_CONFIG_OPTION_USE_PNG=1"); + + if (b.systemIntegrationOption("libpng", .{})) { + lib.linkSystemLibrary2("libpng", dynamic_link_opts); + } else { + const libpng_dep = b.dependency( + "libpng", + .{ .target = target, .optimize = optimize }, + ); + lib.linkLibrary(libpng_dep.artifact("png")); + } + } lib.addCSourceFiles(.{ .root = upstream.path(""), diff --git a/pkg/harfbuzz/build.zig b/pkg/harfbuzz/build.zig index bc44be1a0..bded32172 100644 --- a/pkg/harfbuzz/build.zig +++ b/pkg/harfbuzz/build.zig @@ -41,13 +41,13 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, module); } - const freetype_dep = b.dependency("freetype", .{ - .target = target, - .optimize = optimize, - .@"enable-libpng" = true, - }); - lib.linkLibrary(freetype_dep.artifact("freetype")); - module.addIncludePath(freetype_dep.builder.dependency("freetype", .{}).path("include")); + // For dynamic linking, we prefer dynamic linking and to search by + // mode first. Mode first will search all paths for a dynamic library + // before falling back to static. + const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, + }; var flags = std.ArrayList([]const u8).init(b.allocator); defer flags.deinit(); @@ -61,15 +61,28 @@ pub fn build(b: *std.Build) !void { "-DHAVE_PTHREAD=1", }); } - if (freetype_enabled) try flags.appendSlice(&.{ - "-DHAVE_FREETYPE=1", - // Let's just assume a new freetype - "-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1", - "-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1", - "-DHAVE_FT_DONE_MM_VAR=1", - "-DHAVE_FT_GET_TRANSFORM=1", - }); + // Freetype + _ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help + if (freetype_enabled) { + try flags.appendSlice(&.{ + "-DHAVE_FREETYPE=1", + + // Let's just assume a new freetype + "-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1", + "-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1", + "-DHAVE_FT_DONE_MM_VAR=1", + "-DHAVE_FT_GET_TRANSFORM=1", + }); + + if (b.systemIntegrationOption("freetype", .{})) { + lib.linkSystemLibrary2("freetype", dynamic_link_opts); + } else { + lib.linkLibrary(freetype.artifact("freetype")); + module.addIncludePath(freetype.builder.dependency("freetype", .{}).path("include")); + } + } + if (coretext_enabled) { try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"}); lib.linkFramework("CoreText"); @@ -99,7 +112,7 @@ pub fn build(b: *std.Build) !void { var it = module.import_table.iterator(); while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*); - test_exe.linkLibrary(freetype_dep.artifact("freetype")); + test_exe.linkLibrary(freetype.artifact("freetype")); const tests_run = b.addRunArtifact(test_exe); const test_step = b.step("test", "Run tests"); test_step.dependOn(&tests_run.step); diff --git a/pkg/libpng/build.zig b/pkg/libpng/build.zig index 6d3487071..753084f62 100644 --- a/pkg/libpng/build.zig +++ b/pkg/libpng/build.zig @@ -20,10 +20,22 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, &lib.root_module); } - const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize }); - lib.linkLibrary(zlib_dep.artifact("z")); - lib.addIncludePath(upstream.path("")); - lib.addIncludePath(b.path("")); + // For dynamic linking, we prefer dynamic linking and to search by + // mode first. Mode first will search all paths for a dynamic library + // before falling back to static. + const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, + }; + + if (b.systemIntegrationOption("zlib", .{})) { + lib.linkSystemLibrary2("zlib", dynamic_link_opts); + } else { + const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize }); + lib.linkLibrary(zlib_dep.artifact("z")); + lib.addIncludePath(upstream.path("")); + lib.addIncludePath(b.path("")); + } var flags = std.ArrayList([]const u8).init(b.allocator); defer flags.deinit(); diff --git a/src/build_config.zig b/src/build_config.zig index 57df93a83..48391ef4f 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -20,7 +20,6 @@ const WasmTarget = @import("os/wasm/target.zig").Target; /// build types, etc. pub const BuildConfig = struct { version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }, - static: bool = false, flatpak: bool = false, libadwaita: bool = false, app_runtime: apprt.Runtime = .none,