mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-12 10:48:39 +03:00
build: distribute gresource c/h with source tarball
This introduces the concept of a "dist resource" (specifically a `GhosttyDist.Resource` type). This is a resource that may be present in dist tarballs but not in the source tree. If the resource is present and we're not in a Git checkout, then we use it directly instead of generating it. This is used for the first time in this commit for the gresource c/h files, which depend on a variety of external tools (blueprint-compiler, glib-compile-resources, etc.) that we do not want to require downstream users/packagers to have and we also do not want to worry about them having the right versions. This also adds a check for `distcheck` to ensure our distribution contains all the expected files.
This commit is contained in:
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -239,7 +239,17 @@ jobs:
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Build and Check Source Tarball
|
||||
run: nix develop -c zig build distcheck
|
||||
run: |
|
||||
rm -rf zig-out/dist
|
||||
nix develop -c zig build distcheck
|
||||
cp zig-out/dist/*.tar.gz ghostty-source.tar.gz
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
path: |-
|
||||
ghostty-source.tar.gz
|
||||
|
||||
build-macos:
|
||||
runs-on: namespace-profile-ghostty-macos
|
||||
@ -828,7 +838,7 @@ jobs:
|
||||
test-debian-12:
|
||||
name: Test build on Debian 12
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: test
|
||||
needs: [test, build-dist]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@ -839,6 +849,11 @@ jobs:
|
||||
- name: Configure Namespace powered Buildx
|
||||
uses: namespacelabs/nscloud-setup-buildx-action@v0
|
||||
|
||||
- name: Download Source Tarball Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
|
22
README.md
22
README.md
@ -188,8 +188,11 @@ SENTRY_DSN=https://e914ee84fd895c4fe324afa3e53dac76@o4507352570920960.ingest.us.
|
||||
## Developing Ghostty
|
||||
|
||||
See the documentation on the Ghostty website for
|
||||
[building Ghostty from source](http://ghostty.org/docs/install/build).
|
||||
For development, omit the `-Doptimize` flag to build a debug build.
|
||||
[building Ghostty from a source tarball](http://ghostty.org/docs/install/build).
|
||||
Building Ghostty from a Git checkout is very similar, except you want to
|
||||
omit the `-Doptimize` flag to build a debug build, and you may require
|
||||
additional dependencies since the source tarball includes some processed
|
||||
files that are not in the Git repository.
|
||||
|
||||
On Linux or macOS, you can use `zig build -Dapp-runtime=glfw run` for a quick
|
||||
GLFW-based app for a faster development cycle while developing core
|
||||
@ -206,6 +209,21 @@ Other useful commands:
|
||||
in the current running terminal emulator so if you want to check the
|
||||
behavior of this project, you must run this command in Ghostty.
|
||||
|
||||
### Extra Dependencies
|
||||
|
||||
Building Ghostty from a Git checkout on Linux requires some additional
|
||||
dependencies:
|
||||
|
||||
- `blueprint-compiler`
|
||||
|
||||
macOS users don't require any additional dependencies.
|
||||
|
||||
> [!NOTE]
|
||||
> This only applies to building from a _Git checkout_. This section does
|
||||
> not apply if you're building from a released _source tarball_. For
|
||||
> source tarballs, see the
|
||||
> [website](http://ghostty.org/docs/install/build).
|
||||
|
||||
### Linting
|
||||
|
||||
#### Prettier
|
||||
|
@ -108,6 +108,12 @@ in
|
||||
|
||||
# Localization
|
||||
gettext
|
||||
|
||||
# We need these GTK-related deps on all platform so we can build
|
||||
# dist tarballs.
|
||||
blueprint-compiler
|
||||
libadwaita
|
||||
gtk4
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
# My nix shell environment installs the non-interactive version
|
||||
@ -146,9 +152,6 @@ in
|
||||
libXrandr
|
||||
|
||||
# Only needed for GTK builds
|
||||
blueprint-compiler
|
||||
libadwaita
|
||||
gtk4
|
||||
gtk4-layer-shell
|
||||
glib
|
||||
gobject-introspection
|
||||
|
@ -17,6 +17,15 @@ archive_step: *std.Build.Step,
|
||||
check_step: *std.Build.Step,
|
||||
|
||||
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
|
||||
// Get the resources we're going to inject into the source tarball.
|
||||
const alloc = b.allocator;
|
||||
var resources: std.ArrayListUnmanaged(Resource) = .empty;
|
||||
{
|
||||
const gtk = SharedDeps.gtkDistResources(b);
|
||||
try resources.append(alloc, gtk.resources_c);
|
||||
try resources.append(alloc, gtk.resources_h);
|
||||
}
|
||||
|
||||
// git archive to create the final tarball. "git archive" is the
|
||||
// easiest way I can find to create a tarball that ignores stuff
|
||||
// from gitignore and also supports adding files as well as removing
|
||||
@ -25,12 +34,34 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
|
||||
"git",
|
||||
"archive",
|
||||
"--format=tgz",
|
||||
});
|
||||
|
||||
// Add all of our resources into the tarball.
|
||||
for (resources.items) |resource| {
|
||||
// Our dist path basename may not match our generated file basename,
|
||||
// and git archive requires this. To be safe, we copy the file once
|
||||
// to ensure the basename matches and then use that as the final
|
||||
// generated file.
|
||||
const copied = b.addWriteFiles().addCopyFile(
|
||||
resource.generated,
|
||||
std.fs.path.basename(resource.dist),
|
||||
);
|
||||
|
||||
// --add-file uses the most recent --prefix to determine the path
|
||||
// in the archive to copy the file (the directory only).
|
||||
git_archive.addArg(b.fmt("--prefix=ghostty-{}/{s}/", .{
|
||||
cfg.version,
|
||||
std.fs.path.dirname(resource.dist).?,
|
||||
}));
|
||||
git_archive.addPrefixedFileArg("--add-file=", copied);
|
||||
}
|
||||
|
||||
// Add our output
|
||||
git_archive.addArgs(&.{
|
||||
// This is important. Standard source tarballs extract into
|
||||
// a directory named `project-version`. This is expected by
|
||||
// standard tooling such as debhelper and rpmbuild.
|
||||
b.fmt("--prefix=ghostty-{}/", .{cfg.version}),
|
||||
|
||||
"-o",
|
||||
});
|
||||
const output = git_archive.addOutputFileArg(b.fmt(
|
||||
@ -78,6 +109,13 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
|
||||
break :step step;
|
||||
};
|
||||
|
||||
// Check that all our dist resources are at the proper path.
|
||||
for (resources.items) |resource| {
|
||||
const path = extract_dir.path(b, resource.dist);
|
||||
const check_path = b.addCheckFile(path, .{});
|
||||
check_test.step.dependOn(&check_path.step);
|
||||
}
|
||||
|
||||
return .{
|
||||
.archive = output,
|
||||
.install_step = &install.step,
|
||||
@ -85,3 +123,51 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
|
||||
.check_step = &check_test.step,
|
||||
};
|
||||
}
|
||||
|
||||
/// A dist resource is a resource that is built and distributed as part
|
||||
/// of the source tarball with Ghostty. These aren't committed to the Git
|
||||
/// repository but are built as part of the `zig build dist` command.
|
||||
/// The purpose is to limit the number of build-time dependencies required
|
||||
/// for downstream users and packagers.
|
||||
pub const Resource = struct {
|
||||
/// The relative path in the source tree where the resource will be
|
||||
/// if it was pre-built. These are not checksummed or anything because the
|
||||
/// assumption is that the source tarball itself is checksummed and signed.
|
||||
dist: []const u8,
|
||||
|
||||
/// The path to the generated resource in the build system. By depending
|
||||
/// on this you'll force it to regenerate. This does NOT point to the
|
||||
/// "path" above.
|
||||
generated: std.Build.LazyPath,
|
||||
|
||||
/// Returns the path to use for this resource.
|
||||
pub fn path(self: *const Resource, b: *std.Build) std.Build.LazyPath {
|
||||
// If the dist path exists at build compile time then we use it.
|
||||
if (self.exists(b)) {
|
||||
return b.path(self.dist);
|
||||
}
|
||||
|
||||
// Otherwise we use the generated path.
|
||||
return self.generated;
|
||||
}
|
||||
|
||||
/// Returns true if the dist path exists at build time.
|
||||
pub fn exists(self: *const Resource, b: *std.Build) bool {
|
||||
if (std.fs.accessAbsolute(b.pathFromRoot(self.dist), .{})) {
|
||||
// If we have a ".git" directory then we're a git checkout
|
||||
// and we never want to use the dist path. This shouldn't happen
|
||||
// so show a warning to the user.
|
||||
if (std.fs.accessAbsolute(b.pathFromRoot(".git"), .{})) {
|
||||
std.log.warn(
|
||||
"dist resource '{s}' should not be in a git checkout",
|
||||
.{self.dist},
|
||||
);
|
||||
return false;
|
||||
} else |_| {}
|
||||
|
||||
return true;
|
||||
} else |_| {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,9 @@ const HelpStrings = @import("HelpStrings.zig");
|
||||
const MetallibStep = @import("MetallibStep.zig");
|
||||
const UnicodeTables = @import("UnicodeTables.zig");
|
||||
const GhosttyFrameData = @import("GhosttyFrameData.zig");
|
||||
const DistResource = @import("GhosttyDist.zig").Resource;
|
||||
|
||||
const gresource = @import("../apprt/gtk/gresource.zig");
|
||||
|
||||
config: *const Config,
|
||||
|
||||
@ -659,54 +662,7 @@ fn addGTK(
|
||||
}
|
||||
|
||||
{
|
||||
const gresource = @import("../apprt/gtk/gresource.zig");
|
||||
|
||||
const gresource_xml = gresource_xml: {
|
||||
const generate_gresource_xml = b.addExecutable(.{
|
||||
.name = "generate_gresource_xml",
|
||||
.root_source_file = b.path("src/apprt/gtk/gresource.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
|
||||
const generate = b.addRunArtifact(generate_gresource_xml);
|
||||
|
||||
const gtk_blueprint_compiler = b.addExecutable(.{
|
||||
.name = "gtk_blueprint_compiler",
|
||||
.root_source_file = b.path("src/apprt/gtk/blueprint_compiler.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
gtk_blueprint_compiler.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
||||
gtk_blueprint_compiler.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
|
||||
gtk_blueprint_compiler.linkLibC();
|
||||
|
||||
for (gresource.blueprint_files) |blueprint_file| {
|
||||
const blueprint_compiler = b.addRunArtifact(gtk_blueprint_compiler);
|
||||
blueprint_compiler.addArgs(&.{
|
||||
b.fmt("{d}", .{blueprint_file.major}),
|
||||
b.fmt("{d}", .{blueprint_file.minor}),
|
||||
});
|
||||
const ui_file = blueprint_compiler.addOutputFileArg(b.fmt(
|
||||
"{d}.{d}/{s}.ui",
|
||||
.{
|
||||
blueprint_file.major,
|
||||
blueprint_file.minor,
|
||||
blueprint_file.name,
|
||||
},
|
||||
));
|
||||
blueprint_compiler.addFileArg(b.path(b.fmt(
|
||||
"src/apprt/gtk/ui/{d}.{d}/{s}.blp",
|
||||
.{
|
||||
blueprint_file.major,
|
||||
blueprint_file.minor,
|
||||
blueprint_file.name,
|
||||
},
|
||||
)));
|
||||
generate.addFileArg(ui_file);
|
||||
}
|
||||
|
||||
break :gresource_xml generate.captureStdOut();
|
||||
};
|
||||
|
||||
// For our actual build, we validate our GTK builder files if we can.
|
||||
{
|
||||
const gtk_builder_check = b.addExecutable(.{
|
||||
.name = "gtk_builder_check",
|
||||
@ -734,30 +690,98 @@ fn addGTK(
|
||||
}
|
||||
}
|
||||
|
||||
const generate_resources_c = b.addSystemCommand(&.{
|
||||
"glib-compile-resources",
|
||||
"--c-name",
|
||||
"ghostty",
|
||||
"--generate-source",
|
||||
"--target",
|
||||
});
|
||||
const ghostty_resources_c = generate_resources_c.addOutputFileArg("ghostty_resources.c");
|
||||
generate_resources_c.addFileArg(gresource_xml);
|
||||
step.addCSourceFile(.{ .file = ghostty_resources_c, .flags = &.{} });
|
||||
|
||||
const generate_resources_h = b.addSystemCommand(&.{
|
||||
"glib-compile-resources",
|
||||
"--c-name",
|
||||
"ghostty",
|
||||
"--generate-header",
|
||||
"--target",
|
||||
});
|
||||
const ghostty_resources_h = generate_resources_h.addOutputFileArg("ghostty_resources.h");
|
||||
generate_resources_h.addFileArg(gresource_xml);
|
||||
step.addIncludePath(ghostty_resources_h.dirname());
|
||||
// Get our gresource c/h files and add them to our build.
|
||||
const dist = gtkDistResources(b);
|
||||
step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} });
|
||||
step.addIncludePath(dist.resources_h.path(b).dirname());
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the resources that can be prebuilt for our dist build.
|
||||
pub fn gtkDistResources(
|
||||
b: *std.Build,
|
||||
) struct {
|
||||
resources_c: DistResource,
|
||||
resources_h: DistResource,
|
||||
} {
|
||||
const gresource_xml = gresource_xml: {
|
||||
const xml_exe = b.addExecutable(.{
|
||||
.name = "generate_gresource_xml",
|
||||
.root_source_file = b.path("src/apprt/gtk/gresource.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const xml_run = b.addRunArtifact(xml_exe);
|
||||
|
||||
const blueprint_exe = b.addExecutable(.{
|
||||
.name = "gtk_blueprint_compiler",
|
||||
.root_source_file = b.path("src/apprt/gtk/blueprint_compiler.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
blueprint_exe.linkLibC();
|
||||
blueprint_exe.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
||||
blueprint_exe.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
|
||||
|
||||
for (gresource.blueprint_files) |blueprint_file| {
|
||||
const blueprint_run = b.addRunArtifact(blueprint_exe);
|
||||
blueprint_run.addArgs(&.{
|
||||
b.fmt("{d}", .{blueprint_file.major}),
|
||||
b.fmt("{d}", .{blueprint_file.minor}),
|
||||
});
|
||||
const ui_file = blueprint_run.addOutputFileArg(b.fmt(
|
||||
"{d}.{d}/{s}.ui",
|
||||
.{
|
||||
blueprint_file.major,
|
||||
blueprint_file.minor,
|
||||
blueprint_file.name,
|
||||
},
|
||||
));
|
||||
blueprint_run.addFileArg(b.path(b.fmt(
|
||||
"src/apprt/gtk/ui/{d}.{d}/{s}.blp",
|
||||
.{
|
||||
blueprint_file.major,
|
||||
blueprint_file.minor,
|
||||
blueprint_file.name,
|
||||
},
|
||||
)));
|
||||
|
||||
xml_run.addFileArg(ui_file);
|
||||
}
|
||||
|
||||
break :gresource_xml xml_run.captureStdOut();
|
||||
};
|
||||
|
||||
const generate_c = b.addSystemCommand(&.{
|
||||
"glib-compile-resources",
|
||||
"--c-name",
|
||||
"ghostty",
|
||||
"--generate-source",
|
||||
"--target",
|
||||
});
|
||||
const resources_c = generate_c.addOutputFileArg("ghostty_resources.c");
|
||||
generate_c.addFileArg(gresource_xml);
|
||||
|
||||
const generate_h = b.addSystemCommand(&.{
|
||||
"glib-compile-resources",
|
||||
"--c-name",
|
||||
"ghostty",
|
||||
"--generate-header",
|
||||
"--target",
|
||||
});
|
||||
const resources_h = generate_h.addOutputFileArg("ghostty_resources.h");
|
||||
generate_h.addFileArg(gresource_xml);
|
||||
|
||||
return .{
|
||||
.resources_c = .{
|
||||
.dist = "src/apprt/gtk/ghostty_resources.c",
|
||||
.generated = resources_c,
|
||||
},
|
||||
.resources_h = .{
|
||||
.dist = "src/apprt/gtk/ghostty_resources.h",
|
||||
.generated = resources_h,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -5,7 +5,6 @@ FROM docker.io/library/debian:${DISTRO_VERSION}
|
||||
RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \
|
||||
apt-get -qq -y --no-install-recommends install \
|
||||
# Build Tools
|
||||
blueprint-compiler \
|
||||
build-essential \
|
||||
curl \
|
||||
libbz2-dev \
|
||||
@ -37,25 +36,24 @@ RUN export ZIG_VERSION=$(sed -n -e 's/^.*requireZig("\(.*\)").*$/\1/p' /src/buil
|
||||
rm /tmp/zig.tar.xz && \
|
||||
ln -s "/opt/zig-linux-$(uname -m)-$ZIG_VERSION/zig" /usr/local/bin/zig
|
||||
|
||||
# Extract our source tarball
|
||||
COPY ./ghostty-source.tar.gz /src
|
||||
WORKDIR /src
|
||||
|
||||
COPY ./dist/linux /src/dist/linux
|
||||
COPY ./images /src/images
|
||||
COPY ./include /src/include
|
||||
COPY ./pkg /src/pkg
|
||||
COPY ./po /src/po
|
||||
COPY ./nix /src/nix
|
||||
COPY ./vendor /src/vendor
|
||||
COPY ./build.zig /src/build.zig
|
||||
COPY ./build.zig.zon /src/build.zig.zon
|
||||
COPY ./build.zig.zon.txt /src/build.zig.zon.txt
|
||||
RUN tar xvzf ghostty-source.tar.gz && \
|
||||
rm ghostty-source.tar.gz && \
|
||||
mv ghostty-* ghostty-source && \
|
||||
mv ghostty-source/* . && \
|
||||
rm -rf ghostty-source
|
||||
|
||||
RUN ZIG_GLOBAL_CACHE_DIR=/zig/global-cache ./nix/build-support/fetch-zig-cache.sh
|
||||
|
||||
COPY ./src /src/src
|
||||
|
||||
# Debian 12 doesn't have gtk4-layer-shell, so we have to manually compile it ourselves
|
||||
RUN zig build -Doptimize=Debug -Dcpu=baseline -Dapp-runtime=gtk -fno-sys=gtk4-layer-shell --system /zig/global-cache/p
|
||||
RUN zig build \
|
||||
-Doptimize=Debug \
|
||||
-Dcpu=baseline \
|
||||
-Dapp-runtime=gtk \
|
||||
-fno-sys=gtk4-layer-shell \
|
||||
--system /zig/global-cache/p
|
||||
|
||||
RUN ./zig-out/bin/ghostty +version
|
||||
|
||||
|
Reference in New Issue
Block a user