mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
build: fix windows build to properly run tests and build of libghostty-vt (#11781)
Our Windows build has been broken for a _long_ time. It hasn't actually worked and our CI was falsely passing when it was actually failing to build/test. This PR fixes that and fixes the issues it found so `libghostty-vt` can build and pass tests. **This is only for libghostty!** I'd still like to expand our _test_ coverage to all of Ghostty for Windows but libghostty is more important for that platform in the short term and it's an incremental piece of work. A couple windows compatibility issues fixed: - `terminal.Page` uses `VirtualAlloc` on Windows (thanks @deblasis) - Our rgb.txt loading was not resilient to CRLF endings
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -12,3 +12,4 @@ src/font/nerd_font_attributes.zig linguist-generated=true
|
||||
src/font/nerd_font_codepoint_tables.py linguist-generated=true
|
||||
src/font/res/** linguist-vendored
|
||||
src/terminal/res/** linguist-vendored
|
||||
src/terminal/res/rgb.txt -text
|
||||
|
||||
94
.github/workflows/test.yml
vendored
94
.github/workflows/test.yml
vendored
@@ -92,13 +92,13 @@ jobs:
|
||||
- build-libghostty-vt
|
||||
- build-libghostty-vt-android
|
||||
- build-libghostty-vt-macos
|
||||
- build-libghostty-vt-windows
|
||||
- build-linux
|
||||
- build-linux-libghostty
|
||||
- build-nix
|
||||
- build-macos
|
||||
- build-macos-freetype
|
||||
- build-snap
|
||||
- build-windows
|
||||
- test
|
||||
- test-simd
|
||||
- test-gtk
|
||||
@@ -500,6 +500,25 @@ jobs:
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
|
||||
build-libghostty-vt-windows:
|
||||
runs-on: windows-2025
|
||||
continue-on-error: true
|
||||
timeout-minutes: 45
|
||||
needs: test
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Zig
|
||||
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
|
||||
|
||||
# TODO: Work towards passing the full test suite on Windows.
|
||||
- name: Test libghostty-vt
|
||||
run: zig build test-lib-vt
|
||||
|
||||
- name: Build libghostty-vt
|
||||
run: zig build -Demit-lib-vt
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -795,79 +814,6 @@ jobs:
|
||||
run: |
|
||||
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Demit-macos-app=false -Drenderer=metal -Dfont-backend=coretext_freetype
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
# this will not stop other jobs from running
|
||||
continue-on-error: true
|
||||
timeout-minutes: 45
|
||||
needs: test
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# This could be from a script if we wanted to but inlining here for now
|
||||
# in one place.
|
||||
# Using powershell so that we do not need to install WSL components. Also,
|
||||
# WSLv1 is only installed on Github runners.
|
||||
- name: Install zig
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Get the zig version from build.zig.zon so that it only needs to be updated
|
||||
$fileContent = Get-Content -Path "build.zig.zon" -Raw
|
||||
$pattern = 'minimum_zig_version\s*=\s*"([^"]+)"'
|
||||
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
|
||||
$version = "zig-x86_64-windows-$zigVersion"
|
||||
Write-Output $version
|
||||
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
|
||||
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
|
||||
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
|
||||
Remove-Item -Path ".\zig-windows.zip"
|
||||
Rename-Item -Path ".\$version" -NewName ".\zig"
|
||||
Write-Host "Zig installed."
|
||||
.\zig\zig.exe version
|
||||
|
||||
- name: Compile build
|
||||
shell: pwsh
|
||||
run: .\zig\zig.exe build --help
|
||||
|
||||
- name: Generate build testing script
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Generate a script so that we can swallow the errors
|
||||
$scriptContent = @"
|
||||
.\zig\zig.exe build test 2>&1 | Out-File -FilePath "build.log" -Append
|
||||
exit 0
|
||||
"@
|
||||
$scriptPath = "zigbuild.ps1"
|
||||
# Write the script content to a file
|
||||
$scriptContent | Set-Content -Path $scriptPath
|
||||
Write-Host "Script generated at: $scriptPath"
|
||||
|
||||
- name: Test Windows
|
||||
shell: pwsh
|
||||
run: .\zigbuild.ps1 -ErrorAction SilentlyContinue
|
||||
|
||||
- name: Generate build script
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Generate a script so that we can swallow the errors
|
||||
$scriptContent = @"
|
||||
.\zig\zig.exe build 2>&1 | Out-File -FilePath "build.log" -Append
|
||||
exit 0
|
||||
"@
|
||||
$scriptPath = "zigbuild.ps1"
|
||||
# Write the script content to a file
|
||||
$scriptContent | Set-Content -Path $scriptPath
|
||||
Write-Host "Script generated at: $scriptPath"
|
||||
|
||||
- name: Build Windows
|
||||
shell: pwsh
|
||||
run: .\zigbuild.ps1 -ErrorAction SilentlyContinue
|
||||
|
||||
- name: Dump logs
|
||||
shell: pwsh
|
||||
run: Get-Content -Path ".\build.log"
|
||||
|
||||
test:
|
||||
if: github.repository == 'ghostty-org/ghostty' && needs.skip.outputs.skip != 'true'
|
||||
needs: skip
|
||||
|
||||
@@ -6,6 +6,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const assert = @import("../quirks.zig").inlineAssert;
|
||||
const testing = std.testing;
|
||||
const posix = std.posix;
|
||||
const windows = std.os.windows;
|
||||
const fastmem = @import("../fastmem.zig");
|
||||
const color = @import("color.zig");
|
||||
const hyperlink = @import("hyperlink.zig");
|
||||
@@ -26,6 +27,60 @@ const alignBackward = std.mem.alignBackward;
|
||||
|
||||
const log = std.log.scoped(.page);
|
||||
|
||||
/// Page-aligned allocator used for terminal page backing memory. Pages
|
||||
/// require page-aligned, zeroed memory obtained directly from the OS
|
||||
/// (not the Zig allocator) because the allocation fast-path is
|
||||
/// performance-critical and the OS guarantees zeroed pages.
|
||||
const PageAlloc = switch (builtin.os.tag) {
|
||||
.windows => AllocWindows,
|
||||
else => AllocPosix,
|
||||
};
|
||||
|
||||
/// Allocate page-aligned, zeroed backing memory using mmap with
|
||||
/// MAP_PRIVATE | MAP_ANONYMOUS which guarantees zeroed pages.
|
||||
const AllocPosix = struct {
|
||||
pub fn alloc(n: usize) ![]align(std.heap.page_size_min) u8 {
|
||||
return try posix.mmap(
|
||||
null,
|
||||
n,
|
||||
posix.PROT.READ | posix.PROT.WRITE,
|
||||
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
|
||||
-1,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn free(mem: []align(std.heap.page_size_min) u8) void {
|
||||
posix.munmap(mem);
|
||||
}
|
||||
};
|
||||
|
||||
/// Allocate page-aligned, zeroed backing memory using VirtualAlloc with
|
||||
/// MEM_COMMIT | MEM_RESERVE which guarantees zeroed pages.
|
||||
const AllocWindows = struct {
|
||||
pub fn alloc(n: usize) error{OutOfMemory}![]align(std.heap.page_size_min) u8 {
|
||||
const addr = windows.VirtualAlloc(
|
||||
null,
|
||||
n,
|
||||
windows.MEM_COMMIT | windows.MEM_RESERVE,
|
||||
windows.PAGE_READWRITE,
|
||||
) catch return error.OutOfMemory;
|
||||
|
||||
return @as(
|
||||
[*]align(std.heap.page_size_min) u8,
|
||||
@ptrCast(@alignCast(addr)),
|
||||
)[0..n];
|
||||
}
|
||||
|
||||
pub fn free(mem: []align(std.heap.page_size_min) u8) void {
|
||||
windows.VirtualFree(
|
||||
@ptrCast(@alignCast(mem.ptr)),
|
||||
0,
|
||||
windows.MEM_RELEASE,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/// The allocator to use for multi-codepoint grapheme data. We use
|
||||
/// a chunk size of 4 codepoints. It'd be best to set this empirically
|
||||
/// but it is currently set based on vibes. My thinking around 4 codepoints
|
||||
@@ -167,20 +222,13 @@ pub const Page = struct {
|
||||
pub inline fn init(cap: Capacity) !Page {
|
||||
const l = layout(cap);
|
||||
|
||||
// We use mmap directly to avoid Zig allocator overhead
|
||||
// (small but meaningful for this path) and because a private
|
||||
// anonymous mmap is guaranteed on Linux and macOS to be zeroed,
|
||||
// We allocate page-aligned zeroed memory directly to avoid Zig
|
||||
// allocator overhead (small but meaningful for this path). Both
|
||||
// mmap (POSIX) and VirtualAlloc (Windows) guarantee zeroed pages,
|
||||
// which is a critical property for us.
|
||||
assert(l.total_size % std.heap.page_size_min == 0);
|
||||
const backing = try posix.mmap(
|
||||
null,
|
||||
l.total_size,
|
||||
posix.PROT.READ | posix.PROT.WRITE,
|
||||
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
|
||||
-1,
|
||||
0,
|
||||
);
|
||||
errdefer posix.munmap(backing);
|
||||
const backing = try PageAlloc.alloc(l.total_size);
|
||||
errdefer PageAlloc.free(backing);
|
||||
|
||||
const buf = OffsetBuf.init(backing);
|
||||
return initBuf(buf, l);
|
||||
@@ -245,7 +293,7 @@ pub const Page = struct {
|
||||
/// this if you allocated the backing memory yourself (i.e. you used
|
||||
/// initBuf).
|
||||
pub inline fn deinit(self: *Page) void {
|
||||
posix.munmap(self.memory);
|
||||
PageAlloc.free(self.memory);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -578,15 +626,8 @@ pub const Page = struct {
|
||||
/// using the page allocator. If you want to manage memory manually,
|
||||
/// use cloneBuf.
|
||||
pub inline fn clone(self: *const Page) !Page {
|
||||
const backing = try posix.mmap(
|
||||
null,
|
||||
self.memory.len,
|
||||
posix.PROT.READ | posix.PROT.WRITE,
|
||||
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
|
||||
-1,
|
||||
0,
|
||||
);
|
||||
errdefer posix.munmap(backing);
|
||||
const backing = try PageAlloc.alloc(self.memory.len);
|
||||
errdefer PageAlloc.free(backing);
|
||||
return self.cloneBuf(backing);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,18 @@ fn colorMap() ColorMap {
|
||||
// of our unit tests will catch it.
|
||||
var iter = std.mem.splitScalar(u8, data, '\n');
|
||||
var i: usize = 0;
|
||||
while (iter.next()) |line| {
|
||||
while (iter.next()) |raw_line| {
|
||||
// Trim \r so this works with both LF and CRLF line endings,
|
||||
// since git may convert rgb.txt to CRLF on Windows checkouts.
|
||||
const line = if (raw_line.len > 0 and raw_line[raw_line.len - 1] == '\r')
|
||||
raw_line[0 .. raw_line.len - 1]
|
||||
else
|
||||
raw_line;
|
||||
if (line.len == 0) continue;
|
||||
const r = try std.fmt.parseInt(u8, std.mem.trim(u8, line[0..3], " "), 10);
|
||||
const g = try std.fmt.parseInt(u8, std.mem.trim(u8, line[4..7], " "), 10);
|
||||
const b = try std.fmt.parseInt(u8, std.mem.trim(u8, line[8..11], " "), 10);
|
||||
const name = std.mem.trim(u8, line[12..], " \t\n");
|
||||
const name = std.mem.trim(u8, line[12..], " \t");
|
||||
kvs[i] = .{ name, .{ .r = r, .g = g, .b = b } };
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user