mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-01-09 23:03:26 +00:00
Zig 0.15 removed the ability to compress from the stdlib, which makes porting our framegen tool to Zig 0.15+ more work than it's worth. We already depend on and have the ability to build zlib, and Zig is a full blown C compiler, so let's just use C. The framegen C program doesn't free any memory, because it is meant to exit quickly. It otherwise behaves pretty much the same as the old Zig codebase. The build scripts were modified to build the C program and run it, but also to include the framedata in the generated source tarball so that downstream packagers don't have to do this (although they'll have all the deps anyways).
191 lines
7.0 KiB
Zig
191 lines
7.0 KiB
Zig
const GhosttyDist = @This();
|
|
|
|
const std = @import("std");
|
|
const Config = @import("Config.zig");
|
|
const SharedDeps = @import("SharedDeps.zig");
|
|
const GhosttyFrameData = @import("GhosttyFrameData.zig");
|
|
|
|
/// The final source tarball.
|
|
archive: std.Build.LazyPath,
|
|
|
|
/// The step to install the tarball.
|
|
install_step: *std.Build.Step,
|
|
|
|
/// The step to depend on
|
|
archive_step: *std.Build.Step,
|
|
|
|
/// The step to depend on for checking the dist
|
|
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.gtkNgDistResources(b);
|
|
try resources.append(alloc, gtk.resources_c);
|
|
try resources.append(alloc, gtk.resources_h);
|
|
}
|
|
{
|
|
const framedata = GhosttyFrameData.distResources(b);
|
|
try resources.append(alloc, framedata.framedata);
|
|
}
|
|
|
|
// 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
|
|
// dist-only files (the "export-ignore" git attribute).
|
|
const git_archive = b.addSystemCommand(&.{
|
|
"git",
|
|
"archive",
|
|
"--format=tgz",
|
|
});
|
|
|
|
// embed the Ghostty version in the tarball
|
|
{
|
|
const version = b.addWriteFiles().add("VERSION", b.fmt("{}", .{cfg.version}));
|
|
// --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-{}/", .{
|
|
cfg.version,
|
|
}));
|
|
git_archive.addPrefixedFileArg("--add-file=", version);
|
|
}
|
|
|
|
// 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(
|
|
"ghostty-{}.tar.gz",
|
|
.{cfg.version},
|
|
));
|
|
git_archive.addArg("HEAD");
|
|
|
|
// The install step to put the dist into the build directory.
|
|
const install = b.addInstallFile(
|
|
output,
|
|
b.fmt("dist/ghostty-{}.tar.gz", .{cfg.version}),
|
|
);
|
|
|
|
// The check step to ensure the archive works.
|
|
const check = b.addSystemCommand(&.{ "tar", "xvzf" });
|
|
check.addFileArg(output);
|
|
check.addArg("-C");
|
|
|
|
// This is the root Ghostty source dir of the extracted source tarball.
|
|
// i.e. this is way `build.zig` is.
|
|
const extract_dir = check
|
|
.addOutputDirectoryArg("ghostty")
|
|
.path(b, b.fmt("ghostty-{}", .{cfg.version}));
|
|
|
|
// Check that tests pass within the extracted directory. This isn't
|
|
// a fully hermetic test because we're sharing the Zig cache. In
|
|
// the future we could add an option to use a totally new cache but
|
|
// in the interest of speed we don't do that for now and hope other
|
|
// CI catches any issues.
|
|
const check_test = step: {
|
|
const step = b.addSystemCommand(&.{ "zig", "build", "test" });
|
|
step.setCwd(extract_dir);
|
|
|
|
// Must be set so that Zig knows that this command doesn't
|
|
// have side effects and is being run for its exit code check.
|
|
// Zig will cache depending on its extract dir.
|
|
step.expectExitCode(0);
|
|
|
|
// Capture stderr so it doesn't spew into the parent build.
|
|
// On the flip side, if the test fails we won't know why so
|
|
// that sucks but we should have already ran tests at this point.
|
|
// NOTE(mitchellh): temporarily disabled to diagnose heisenbug
|
|
//_ = step.captureStdErr();
|
|
|
|
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,
|
|
.archive_step = &git_archive.step,
|
|
.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;
|
|
}
|
|
}
|
|
};
|