Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,11 @@ pub fn build(b: *std.Build) !void {
if (!config.target.result.os.tag.isDarwin()) {
lib_shared.installHeader(); // Only need one header
if (config.target.result.os.tag == .windows) {
lib_shared.install("ghostty.dll");
lib_static.install("ghostty-static.lib");
lib_shared.install("ghostty-internal.dll");
lib_static.install("ghostty-internal-static.lib");
} else {
lib_shared.install("libghostty.so");
lib_static.install("libghostty.a");
lib_shared.install("ghostty-internal.so");
lib_static.install("ghostty-internal.a");
}
}
}
Expand Down
96 changes: 94 additions & 2 deletions src/build/GhosttyLib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ step: *std.Build.Step,
/// The final static library file
output: std.Build.LazyPath,
dsym: ?std.Build.LazyPath,
pkg_config: ?std.Build.LazyPath,
pkg_config_static: ?std.Build.LazyPath,

pub fn initStatic(
b: *std.Build,
Expand Down Expand Up @@ -54,6 +56,8 @@ pub fn initStatic(
.step = &lib.step,
.output = lib.getEmittedBin(),
.dsym = null,
.pkg_config = null,
.pkg_config_static = null,
};

// Create a static lib that contains all our dependencies.
Expand All @@ -70,6 +74,8 @@ pub fn initStatic(

// Static libraries cannot have dSYMs because they aren't linked.
.dsym = null,
.pkg_config = null,
.pkg_config_static = null,
};
}

Expand Down Expand Up @@ -146,10 +152,20 @@ pub fn initShared(
break :dsymutil output;
};

// pkg-config
//
// pkg-config's --static only expands Libs.private / Requires.private;
// it doesn't rewrite Libs: into an archive-only reference when both
// shared and static libraries are installed. Install a dedicated
// static module so consumers can request the archive explicitly.
const pcs = pkgConfigFiles(b, deps);

return .{
.step = &lib.step,
.output = lib.getEmittedBin(),
.dsym = dsymutil,
.pkg_config = pcs.shared,
.pkg_config_static = pcs.static,
};
}

Expand All @@ -168,7 +184,7 @@ pub fn initMacOSUniversal(

const universal = LipoStep.create(b, .{
.name = "ghostty",
.out_name = "libghostty.a",
.out_name = "ghostty-internal.a",
.input_a = aarch64.output,
.input_b = x86_64.output,
});
Expand All @@ -180,13 +196,31 @@ pub fn initMacOSUniversal(
// You can't run dsymutil on a universal binary, you have to
// do it on the individual binaries.
.dsym = null,
.pkg_config = null,
.pkg_config_static = null,
};
}

pub fn install(self: *const GhosttyLib, name: []const u8) void {
const b = self.step.owner;
const step = b.getInstallStep();
const lib_install = b.addInstallLibFile(self.output, name);
b.getInstallStep().dependOn(&lib_install.step);
step.dependOn(&lib_install.step);

if (self.pkg_config) |pc| {
step.dependOn(&b.addInstallFileWithDir(
pc,
.prefix,
"share/pkgconfig/ghostty-internal.pc",
).step);
}
if (self.pkg_config_static) |pc| {
step.dependOn(&b.addInstallFileWithDir(
pc,
.prefix,
"share/pkgconfig/ghostty-internal-static.pc",
).step);
}
}

pub fn installHeader(self: *const GhosttyLib) void {
Expand All @@ -197,3 +231,61 @@ pub fn installHeader(self: *const GhosttyLib) void {
);
b.getInstallStep().dependOn(&header_install.step);
}

const PkgConfigFiles = struct {
shared: std.Build.LazyPath,
static: std.Build.LazyPath,
};

fn pkgConfigFiles(
b: *std.Build,
deps: *const SharedDeps,
) PkgConfigFiles {
const os_tag = deps.config.target.result.os.tag;
const wf = b.addWriteFiles();

return .{
.shared = wf.add("ghostty-internal.pc", b.fmt(
\\prefix={s}
\\includedir=${{prefix}}/include
\\libdir=${{prefix}}/lib
\\
\\Name: ghostty-internal
\\URL: https://github.com/ghostty-org/ghostty
\\Description: Ghostty internal library (not for external use)
\\Version: {f}
\\Cflags: -I${{includedir}}
\\Libs: ${{libdir}}/{s}
\\Libs.private:
\\Requires.private:
, .{ b.install_prefix, deps.config.version, sharedLibraryName(os_tag) })),
.static = wf.add("ghostty-internal-static.pc", b.fmt(
\\prefix={s}
\\includedir=${{prefix}}/include
\\libdir=${{prefix}}/lib
\\
\\Name: ghostty-internal-static
\\URL: https://github.com/ghostty-org/ghostty
\\Description: Ghostty internal library, static (not for external use)
\\Version: {f}
\\Cflags: -I${{includedir}}
\\Libs: ${{libdir}}/{s}
\\Libs.private:
\\Requires.private:
, .{ b.install_prefix, deps.config.version, staticLibraryName(os_tag) })),
};
}

fn sharedLibraryName(os_tag: std.Target.Os.Tag) []const u8 {
return if (os_tag == .windows)
"ghostty-internal.dll"
else
"ghostty-internal.so";
}

fn staticLibraryName(os_tag: std.Target.Os.Tag) []const u8 {
return if (os_tag == .windows)
"ghostty-internal-static.lib"
else
"ghostty-internal.a";
}
10 changes: 5 additions & 5 deletions test/windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ Manual test programs for Windows-specific functionality.

## test_dll_init.c

Regression test for the DLL CRT initialization fix. Loads ghostty.dll
at runtime and calls ghostty_info + ghostty_init to verify the MSVC C
runtime is properly initialized.
Regression test for the DLL CRT initialization fix. Loads
ghostty-internal.dll at runtime and calls ghostty_info + ghostty_init to
verify the MSVC C runtime is properly initialized.

### Build

First build ghostty.dll, then compile the test:
First build ghostty-internal.dll, then compile the test:

```
zig build -Dapp-runtime=none -Demit-exe=false
Expand All @@ -22,7 +22,7 @@ zig cc test_dll_init.c -o test_dll_init.exe -target native-native-msvc
From this directory:

```
copy ..\..\zig-out\lib\ghostty.dll . && test_dll_init.exe
copy ..\..\zig-out\lib\ghostty-internal.dll . && test_dll_init.exe
```

Expected output (after the CRT fix):
Expand Down
13 changes: 7 additions & 6 deletions test/windows/test_dll_init.c
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
/*
* Minimal reproducer for the libghostty DLL CRT initialization issue.
* Minimal reproducer for the ghostty-internal DLL CRT initialization issue.
*
* Before the fix (DllMain calling __vcrt_initialize / __acrt_initialize),
* loading ghostty.dll and calling any function that touches the C runtime
* crashed with "access violation writing 0x0000000000000024" because Zig's
* _DllMainCRTStartup does not initialize the MSVC C runtime for DLL targets.
* loading ghostty-internal.dll and calling any function that touches the C
* runtime crashed with "access violation writing 0x0000000000000024" because
* Zig's _DllMainCRTStartup does not initialize the MSVC C runtime for DLL
* targets.
*
* This test loads the DLL and calls ghostty_info, which exercises the CRT
* (string handling, memory). If it returns a version string without
* crashing, the CRT is properly initialized.
*
* Build: zig cc test_dll_init.c -o test_dll_init.exe -target native-native-msvc
* Run: copy ..\..\zig-out\lib\ghostty.dll . && test_dll_init.exe
* Run: copy ..\..\zig-out\lib\ghostty-internal.dll . && test_dll_init.exe
*
* Expected output (after fix):
* ghostty_info: <version string>
Expand All @@ -29,7 +30,7 @@ typedef struct {
typedef ghostty_info_s (*ghostty_info_fn)(void);

int main(void) {
HMODULE dll = LoadLibraryA("ghostty.dll");
HMODULE dll = LoadLibraryA("ghostty-internal.dll");
if (!dll) {
fprintf(stderr, "LoadLibrary failed: %lu\n", GetLastError());
return 1;
Expand Down
Loading