Merge pull request #2491 from ghostty-org/pkg

System packaging guide, build.zig updates, source tarball generation with signature
This commit is contained in:
Mitchell Hashimoto
2024-10-24 19:38:40 -07:00
committed by GitHub
14 changed files with 532 additions and 275 deletions

View File

@ -84,6 +84,42 @@ jobs:
run: | run: |
sentry-cli dif upload --project ghostty --wait dsym.zip 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: build-macos:
if: | if: |
${{ ${{

View File

@ -50,7 +50,7 @@ jobs:
# Cross-compile the binary. We always use static building for this # Cross-compile the binary. We always use static building for this
# because its the only way to access the headers. # because its the only way to access the headers.
- name: Test Build - 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: build-bench:
# We build benchmarks on large because it uses ReleaseFast # 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 # GhosttyKit is the framework that is built from Zig for our native
# Mac app to access. # Mac app to access.
- name: Build GhosttyKit - 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 # The native app is built with native XCode tooling. This also does
# codesigning. IMPORTANT: this must NOT run in a Nix environment. # codesigning. IMPORTANT: this must NOT run in a Nix environment.
@ -332,8 +332,9 @@ jobs:
- name: Test GLFW Build - name: Test GLFW Build
run: nix develop -c zig build -Dapp-runtime=glfw run: nix develop -c zig build -Dapp-runtime=glfw
- name: Test Dynamic Build # This relies on the cache being populated by the commands above.
run: nix develop -c zig build -Dstatic=false - name: Test System Build
run: nix develop -c zig build --system ${ZIG_GLOBAL_CACHE_DIR}/p
test-macos: test-macos:
runs-on: namespace-profile-ghostty-macos runs-on: namespace-profile-ghostty-macos

93
PACKAGING.md Normal file
View File

@ -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.

View File

@ -566,8 +566,8 @@ the configuration file.
### Linux Installation Tips ### Linux Installation Tips
On Linux, you'll need to install header packages for Ghostty's dependencies On Linux, you'll need to install header packages for Ghostty's dependencies
before building it. Typically, these are only gtk4 and libadwaita (unless before building it. Typically, these are only gtk4 and libadwaita, since
building with `-Dstatic=false`). On Ubuntu and Debian, use Ghostty will build everything else static by default. On Ubuntu and Debian, use
``` ```
sudo apt install libgtk-4-dev libadwaita-1-dev git sudo apt install libgtk-4-dev libadwaita-1-dev git

452
build.zig
View File

@ -94,12 +94,6 @@ pub fn build(b: *std.Build) !void {
"Enables the use of libadwaita when using the gtk rendering backend.", "Enables the use of libadwaita when using the gtk rendering backend.",
) orelse true; ) orelse true;
config.static = b.option(
bool,
"static",
"Statically build as much as possible for the exe",
) orelse true;
const conformance = b.option( const conformance = b.option(
[]const u8, []const u8,
"conformance", "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 // We can use wasmtime to test wasm
b.enable_wasmtime = true; b.enable_wasmtime = true;
@ -653,10 +685,6 @@ pub fn build(b: *std.Build) !void {
const wasm_config: BuildConfig = config: { const wasm_config: BuildConfig = config: {
var copy = 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 // Backends that are fixed for wasm
copy.font_backend = .web_canvas; copy.font_backend = .web_canvas;
@ -748,12 +776,7 @@ pub fn build(b: *std.Build) !void {
{ {
if (emit_test_exe) b.installArtifact(main_test); if (emit_test_exe) b.installArtifact(main_test);
_ = try addDeps(b, main_test, config: { _ = try addDeps(b, main_test, config);
var copy = config;
copy.static = true;
break :config copy;
});
const test_run = b.addRunArtifact(main_test); const test_run = b.addRunArtifact(main_test);
test_step.dependOn(&test_run.step); test_step.dependOn(&test_run.step);
} }
@ -807,17 +830,6 @@ fn createMacOSLib(
optimize: std.builtin.OptimizeMode, optimize: std.builtin.OptimizeMode,
config: BuildConfig, config: BuildConfig,
) !struct { *std.Build.Step, std.Build.LazyPath } { ) !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 static_lib_aarch64 = lib: {
const lib = b.addStaticLibrary(.{ const lib = b.addStaticLibrary(.{
.name = "ghostty", .name = "ghostty",
@ -829,7 +841,7 @@ fn createMacOSLib(
lib.linkLibC(); lib.linkLibC();
// Create a single static lib with all our dependencies merged // 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()); try lib_list.append(lib.getEmittedBin());
const libtool = LibtoolStep.create(b, .{ const libtool = LibtoolStep.create(b, .{
.name = "ghostty", .name = "ghostty",
@ -853,7 +865,7 @@ fn createMacOSLib(
lib.linkLibC(); lib.linkLibC();
// Create a single static lib with all our dependencies merged // 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()); try lib_list.append(lib.getEmittedBin());
const libtool = LibtoolStep.create(b, .{ const libtool = LibtoolStep.create(b, .{
.name = "ghostty", .name = "ghostty",
@ -888,12 +900,6 @@ fn createIOSLib(
optimize: std.builtin.OptimizeMode, optimize: std.builtin.OptimizeMode,
config: BuildConfig, config: BuildConfig,
) !struct { *std.Build.Step, std.Build.LazyPath } { ) !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(.{ const lib = b.addStaticLibrary(.{
.name = "ghostty", .name = "ghostty",
.root_source_file = b.path("src/main_c.zig"), .root_source_file = b.path("src/main_c.zig"),
@ -909,7 +915,7 @@ fn createIOSLib(
lib.linkLibC(); lib.linkLibC();
// Create a single static lib with all our dependencies merged // 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()); try lib_list.append(lib.getEmittedBin());
const libtool = LibtoolStep.create(b, .{ const libtool = LibtoolStep.create(b, .{
.name = "ghostty", .name = "ghostty",
@ -938,9 +944,14 @@ fn addDeps(
try config.addOptions(exe_options); try config.addOptions(exe_options);
step.root_module.addOptions("build_options", 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); var static_libs = LazyPathList.init(b.allocator);
errdefer static_libs.deinit(); 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 // For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library // mode first. Mode first will search all paths for a dynamic library
// before falling back to static. // before falling back to static.
@ -949,106 +960,167 @@ fn addDeps(
.search_strategy = .mode_first, .search_strategy = .mode_first,
}; };
const target = step.root_module.resolved_target.?; // Freetype
const optimize = step.root_module.optimize.?; _ = b.systemIntegrationOption("freetype", .{}); // Shows it in help
if (config.font_backend.hasFreetype()) {
// 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,
});
const fontconfig_dep = b.dependency("fontconfig", .{
.target = target,
.optimize = optimize,
});
const freetype_dep = b.dependency("freetype", .{ const freetype_dep = b.dependency("freetype", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.@"enable-libpng" = true, .@"enable-libpng" = true,
}); });
const glslang_dep = b.dependency("glslang", .{ step.root_module.addImport("freetype", freetype_dep.module("freetype"));
.target = target,
.optimize = optimize, if (b.systemIntegrationOption("freetype", .{})) {
}); step.linkSystemLibrary2("bzip2", dynamic_link_opts);
const spirv_cross_dep = b.dependency("spirv_cross", .{ step.linkSystemLibrary2("freetype", dynamic_link_opts);
.target = target, } else {
.optimize = optimize, step.linkLibrary(freetype_dep.artifact("freetype"));
}); try static_libs.append(freetype_dep.artifact("freetype").getEmittedBin());
const highway_dep = b.dependency("highway", .{ }
.target = target, }
.optimize = optimize,
}); // Harfbuzz
const simdutf_dep = b.dependency("simdutf", .{ _ = b.systemIntegrationOption("harfbuzz", .{}); // Shows it in help
.target = target, if (config.font_backend.hasHarfbuzz()) {
.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,
});
const oniguruma_dep = b.dependency("oniguruma", .{
.target = target,
.optimize = optimize,
});
const opengl_dep = b.dependency("opengl", .{});
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", .{ const harfbuzz_dep = b.dependency("harfbuzz", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.@"enable-freetype" = true, .@"enable-freetype" = true,
.@"enable-coretext" = config.font_backend.hasCoretext(), .@"enable-coretext" = config.font_backend.hasCoretext(),
}); });
const ziglyph_dep = b.dependency("ziglyph", .{
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, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const vaxis_dep = b.dependency("vaxis", .{ step.root_module.addImport(
.target = target, "fontconfig",
.optimize = optimize, fontconfig_dep.module("fontconfig"),
.libxev = false, );
.images = false,
}); if (b.systemIntegrationOption("fontconfig", .{})) {
const wuffs_dep = b.dependency("wuffs", .{ 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, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const zf_dep = b.dependency("zf", .{ 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, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const z2d_dep = b.dependency("z2d", .{}); 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,
});
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,
});
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. // Wasm we do manually since it is such a different build.
if (step.rootModuleTarget().cpu.arch == .wasm32) { 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")); step.root_module.addImport("zig-js", js_dep.module("zig-js"));
return static_libs; 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. // We always require the system SDK so that our system headers are available.
// This makes things like `os/log.h` available for cross-compiling. // This makes things like `os/log.h` available for cross-compiling.
if (step.rootModuleTarget().isDarwin()) { if (step.rootModuleTarget().isDarwin()) {
@ -1110,36 +1179,50 @@ fn addDeps(
try addMetallib(b, step); try addMetallib(b, step);
} }
// We always need the Zig packages // Other dependencies, mostly pure Zig
// TODO: This can't be the right way to use the new Zig modules system, step.root_module.addImport("opengl", b.dependency(
// so take a closer look at this again later. "opengl",
if (config.font_backend.hasFontconfig()) step.root_module.addImport( .{},
"fontconfig", ).module("opengl"));
fontconfig_dep.module("fontconfig"), step.root_module.addImport("vaxis", b.dependency("vaxis", .{
); .target = target,
if (config.font_backend.hasHarfbuzz()) step.root_module.addImport( .optimize = optimize,
"harfbuzz", .libxev = false,
harfbuzz_dep.module("harfbuzz"), .images = false,
); }).module("vaxis"));
step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma")); step.root_module.addImport("wuffs", b.dependency("wuffs", .{
step.root_module.addImport("freetype", freetype_dep.module("freetype")); .target = target,
step.root_module.addImport("glslang", glslang_dep.module("glslang")); .optimize = optimize,
step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross")); }).module("wuffs"));
step.root_module.addImport("xev", libxev_dep.module("xev")); step.root_module.addImport("xev", b.dependency("libxev", .{
step.root_module.addImport("opengl", opengl_dep.module("opengl")); .target = target,
step.root_module.addImport("sentry", sentry_dep.module("sentry")); .optimize = optimize,
step.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph")); }).module("xev"));
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"));
step.root_module.addImport("z2d", b.addModule("z2d", .{ 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, .target = target,
.optimize = optimize, .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 // Mac Stuff
if (step.rootModuleTarget().isDarwin()) { 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 // 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 // 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. // zig-objc module so that it can find the objc runtime headers.
@ -1158,89 +1241,32 @@ fn addDeps(
} }
// cimgui // cimgui
const cimgui_dep = b.dependency("cimgui", .{
.target = target,
.optimize = optimize,
});
step.root_module.addImport("cimgui", cimgui_dep.module("cimgui")); step.root_module.addImport("cimgui", cimgui_dep.module("cimgui"));
step.linkLibrary(cimgui_dep.artifact("cimgui")); step.linkLibrary(cimgui_dep.artifact("cimgui"));
try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin()); 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 // Highway
step.linkLibrary(highway_dep.artifact("highway")); const highway_dep = b.dependency("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
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, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
try static_libs.append(breakpad_dep.artifact("breakpad").getEmittedBin()); step.linkLibrary(highway_dep.artifact("highway"));
} try static_libs.append(highway_dep.artifact("highway").getEmittedBin());
// Dynamic link // utfcpp - This is used as a dependency on our hand-written C++ code
if (!config.static) { const utfcpp_dep = b.dependency("utfcpp", .{
step.addIncludePath(freetype_dep.path("")); .target = target,
step.linkSystemLibrary2("bzip2", dynamic_link_opts); .optimize = optimize,
step.linkSystemLibrary2("freetype2", dynamic_link_opts); });
step.linkSystemLibrary2("libpng", dynamic_link_opts); step.linkLibrary(utfcpp_dep.artifact("utfcpp"));
step.linkSystemLibrary2("oniguruma", dynamic_link_opts); try static_libs.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
step.linkSystemLibrary2("zlib", dynamic_link_opts);
if (config.font_backend.hasFontconfig()) { // If we're building an exe then we have additional dependencies.
step.linkSystemLibrary2("fontconfig", dynamic_link_opts); if (step.kind != .lib) {
}
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) {
// We always statically compile glad // We always statically compile glad
step.addIncludePath(b.path("vendor/glad/include/")); step.addIncludePath(b.path("vendor/glad/include/"));
step.addCSourceFile(.{ step.addCSourceFile(.{
@ -1515,8 +1541,6 @@ fn benchSteps(
if (install) b.installArtifact(c_exe); if (install) b.installArtifact(c_exe);
_ = try addDeps(b, c_exe, config: { _ = try addDeps(b, c_exe, config: {
var copy = config; var copy = config;
copy.static = true;
var enum_name: [64]u8 = undefined; var enum_name: [64]u8 = undefined;
@memcpy(enum_name[0..name.len], name); @memcpy(enum_name[0..name.len], name);
std.mem.replaceScalar(u8, enum_name[0..name.len], '-', '_'); std.mem.replaceScalar(u8, enum_name[0..name.len], '-', '_');

0
nix/build-support/fetch-zig-cache.sh Normal file → Executable file
View File

View File

@ -28,6 +28,7 @@
fontconfig, fontconfig,
freetype, freetype,
glib, glib,
glslang,
gtk4, gtk4,
libadwaita, libadwaita,
gnome, gnome,
@ -41,8 +42,12 @@
libXi, libXi,
libXinerama, libXinerama,
libXrandr, libXrandr,
libxml2,
spirv-cross,
simdutf,
zlib, zlib,
alejandra, alejandra,
minisign,
pandoc, pandoc,
hyperfine, hyperfine,
typos, typos,
@ -59,9 +64,14 @@
freetype freetype
harfbuzz harfbuzz
libpng libpng
libxml2
oniguruma oniguruma
simdutf
zlib zlib
glslang
spirv-cross
libX11 libX11
libXcursor libXcursor
libXi libXi
@ -79,6 +89,7 @@ in
[ [
# For builds # For builds
llvmPackages_latest.llvm llvmPackages_latest.llvm
minisign
ncurses ncurses
pandoc pandoc
pkg-config pkg-config
@ -123,9 +134,14 @@ in
freetype freetype
harfbuzz harfbuzz
libpng libpng
libxml2
oniguruma oniguruma
simdutf
zlib zlib
glslang
spirv-cross
libX11 libX11
libXcursor libXcursor
libXext libXext

View File

@ -7,6 +7,7 @@
freetype, freetype,
harfbuzz, harfbuzz,
libpng, libpng,
oniguruma,
zlib, zlib,
libGL, libGL,
libX11, libX11,
@ -132,6 +133,7 @@ in
freetype freetype
harfbuzz harfbuzz
libpng libpng
oniguruma
zlib zlib
libX11 libX11

View File

@ -12,11 +12,6 @@ pub fn build(b: *std.Build) !void {
}); });
const imgui = b.dependency("imgui", .{}); const imgui = b.dependency("imgui", .{});
const freetype = b.dependency("freetype", .{
.target = target,
.optimize = optimize,
.@"enable-libpng" = true,
});
const lib = b.addStaticLibrary(.{ const lib = b.addStaticLibrary(.{
.name = "cimgui", .name = "cimgui",
.target = target, .target = target,
@ -24,11 +19,30 @@ pub fn build(b: *std.Build) !void {
}); });
lib.linkLibC(); lib.linkLibC();
lib.linkLibCpp(); lib.linkLibCpp();
lib.linkLibrary(freetype.artifact("freetype"));
if (target.result.os.tag == .windows) { if (target.result.os.tag == .windows) {
lib.linkSystemLibrary("imm32"); 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("")); lib.addIncludePath(imgui.path(""));
module.addIncludePath(b.path("vendor")); module.addIncludePath(b.path("vendor"));

View File

@ -25,18 +25,6 @@ pub fn build(b: *std.Build) !void {
if (target.result.os.tag != .windows) { if (target.result.os.tag != .windows) {
lib.linkSystemLibrary("pthread"); 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(upstream.path(""));
lib.addIncludePath(b.path("override/include")); 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) { if (libxml2_enabled) {
try flags.appendSlice(&.{ try flags.appendSlice(&.{
"-DENABLE_LIBXML2", "-DENABLE_LIBXML2",
@ -154,6 +167,17 @@ pub fn build(b: *std.Build) !void {
"-Werror=implicit-function-declaration", "-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(.{ lib.addCSourceFiles(.{

View File

@ -23,13 +23,13 @@ pub fn build(b: *std.Build) !void {
module.addIncludePath(upstream.path("include")); module.addIncludePath(upstream.path("include"));
module.addIncludePath(b.path("")); module.addIncludePath(b.path(""));
// Dependencies // For dynamic linking, we prefer dynamic linking and to search by
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize }); // mode first. Mode first will search all paths for a dynamic library
lib.linkLibrary(zlib_dep.artifact("z")); // before falling back to static.
if (libpng_enabled) { const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
const libpng_dep = b.dependency("libpng", .{ .target = target, .optimize = optimize }); .preferred_link_mode = .dynamic,
lib.linkLibrary(libpng_dep.artifact("png")); .search_strategy = .mode_first,
} };
var flags = std.ArrayList([]const u8).init(b.allocator); var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit(); defer flags.deinit();
@ -43,7 +43,30 @@ pub fn build(b: *std.Build) !void {
"-fno-sanitize=undefined", "-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(.{ lib.addCSourceFiles(.{
.root = upstream.path(""), .root = upstream.path(""),

View File

@ -41,13 +41,13 @@ pub fn build(b: *std.Build) !void {
try apple_sdk.addPaths(b, module); try apple_sdk.addPaths(b, module);
} }
const freetype_dep = b.dependency("freetype", .{ // For dynamic linking, we prefer dynamic linking and to search by
.target = target, // mode first. Mode first will search all paths for a dynamic library
.optimize = optimize, // before falling back to static.
.@"enable-libpng" = true, const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
}); .preferred_link_mode = .dynamic,
lib.linkLibrary(freetype_dep.artifact("freetype")); .search_strategy = .mode_first,
module.addIncludePath(freetype_dep.builder.dependency("freetype", .{}).path("include")); };
var flags = std.ArrayList([]const u8).init(b.allocator); var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit(); defer flags.deinit();
@ -61,7 +61,11 @@ pub fn build(b: *std.Build) !void {
"-DHAVE_PTHREAD=1", "-DHAVE_PTHREAD=1",
}); });
} }
if (freetype_enabled) try flags.appendSlice(&.{
// Freetype
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
if (freetype_enabled) {
try flags.appendSlice(&.{
"-DHAVE_FREETYPE=1", "-DHAVE_FREETYPE=1",
// Let's just assume a new freetype // Let's just assume a new freetype
@ -70,6 +74,15 @@ pub fn build(b: *std.Build) !void {
"-DHAVE_FT_DONE_MM_VAR=1", "-DHAVE_FT_DONE_MM_VAR=1",
"-DHAVE_FT_GET_TRANSFORM=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) { if (coretext_enabled) {
try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"}); try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"});
lib.linkFramework("CoreText"); lib.linkFramework("CoreText");
@ -99,7 +112,7 @@ pub fn build(b: *std.Build) !void {
var it = module.import_table.iterator(); var it = module.import_table.iterator();
while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*); 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 tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests"); const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step); test_step.dependOn(&tests_run.step);

View File

@ -20,10 +20,22 @@ pub fn build(b: *std.Build) !void {
try apple_sdk.addPaths(b, &lib.root_module); try apple_sdk.addPaths(b, &lib.root_module);
} }
// 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 }); const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
lib.linkLibrary(zlib_dep.artifact("z")); lib.linkLibrary(zlib_dep.artifact("z"));
lib.addIncludePath(upstream.path("")); lib.addIncludePath(upstream.path(""));
lib.addIncludePath(b.path("")); lib.addIncludePath(b.path(""));
}
var flags = std.ArrayList([]const u8).init(b.allocator); var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit(); defer flags.deinit();

View File

@ -20,7 +20,6 @@ const WasmTarget = @import("os/wasm/target.zig").Target;
/// build types, etc. /// build types, etc.
pub const BuildConfig = struct { pub const BuildConfig = struct {
version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }, version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
static: bool = false,
flatpak: bool = false, flatpak: bool = false,
libadwaita: bool = false, libadwaita: bool = false,
app_runtime: apprt.Runtime = .none, app_runtime: apprt.Runtime = .none,