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
44 changes: 20 additions & 24 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -174,39 +174,35 @@ pub fn build(b: *std.Build) !void {
run_step.dependOn(&run_example.step);
}

/// Prefetches a list of dependencies, where each dependency has a name field and an args field. If
/// any dependencies are not yet fetched, returns `error.LazyDependencyNeeded`. Saves us from
/// repeatedly earlying out every time a new lazy dependency is needed.
pub fn prefetch(b: *std.Build, comptime deps: anytype) error{LazyDependencyNeeded}!void {
var lazy_deps_needed = false;
inline for (deps) |dep| {
if (!b.systemIntegrationOption(dep.name, .{})) {
if (b.lazyDependency(dep.name, dep.args) == null) {
lazy_deps_needed = true;
}
}
}
if (lazy_deps_needed) {
return error.LazyDependencyNeeded;
/// Fetches a lazy dependency, unless provided via `-fsys`.
///
/// A more typical setup would still need to link to the library in the event that it's provided by
/// `-fsys`, but we can just treat this as a noop as SDL doesn't link to its dependencies, it
/// `dlopen`s them when available. We only pull them in for the headers.
pub fn lazyDependency(
step: *std.Build.Step,
name: []const u8,
args: anytype,
) ?*std.Build.Dependency {
// If we're using a system integration, return null as no work needs to be done. SDL will
// `dlopen` the library, if available, at runtime so we don't need to link it here.
if (step.owner.systemIntegrationOption(name, .{})) return null;
if (@hasDecl(std.Build, "dependencyStep")) {
return step.owner.dependencyStep(step, name, args);
} else {
// https://github.com/allyourcodebase/SDL/issues/44
return step.owner.lazyDependency(name, .{});
}
}

/// Similar to `std.Build.lazyDependency`, but returns `null` if provided by the system with
/// `-fsys`, and otherwise assserts that the dependency was already prefetched (e.g. with
/// `prefetch`.)
pub fn lazyDependency(b: *std.Build, name: []const u8) ?*std.Build.Dependency {
if (b.systemIntegrationOption(name, .{})) return null;
return b.lazyDependency(name, .{}) orelse std.debug.panic("{s} not prefetched", .{name});
}

/// Adds an include path via `lazyDependency`, unless provided via `-fsys`.
pub fn addDepIncludePath(
b: *std.Build,
lib: *std.Build.Step.Compile,
name: []const u8,
sub_path: []const u8,
args: anytype,
) void {
if (lazyDependency(b, name)) |dep| {
if (lazyDependency(&lib.step, name, args)) |dep| {
lib.root_module.addIncludePath(dep.path(sub_path));
}
}
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.{
.name = .sdl,
.fingerprint = 0xec638ccbd103848b,
.version = "1.0.7+3.4.18",
.version = "1.0.8+3.4.18",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.0.7 has no functional changes yet, so you could use it for this stuff.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the build system ignores these version numbers, but in the future we'd like to use them to make decisions in situations where multiple versions of a package are present simultaneously. However, this doesn't work if multiple PRs are merged into master without bumping the version number, since this means two users who fetch latest master could have different commits with the same version number, and the build system has no way of knowing which is newer.

My plan is to follow this pattern until we have these features nailed down (likely in the 0.18 release cycle) at which point I can reevaluate whether this pattern makes sense given how the features end up being implemented.

To save some busy work though, I don't think we need to make a new tag for each patch release. Tags are a useful way to find old releases quickly, but I don't think there are many use cases for intentionally fetching an old patch release. Most users will just want whatever is latest on master, the users looking for an old release are probably looking for a particular Zig or SDL version.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been tagging when I want to pull a new version into SDL_image or SDL_ttf. (I know I could just use commit too, but it's nice when there's a tag attached.)

I do fear the upcoming package manager changes are going to create a lot of problems or busy work if every commit effectively has to have a unique version. It'd be nice if the package manager could support version templating or something when used in conjunction with version control tags. .version = "1.0.8-dev.{n}+upstream.3.4.18.git.{id}". Obviously that wouldn't work with archives, but most distributions will only be producing archives for tagged, concrete versions anyway.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been tagging when I want to pull a new version into SDL_image or SDL_ttf. (I know I could just use commit too, but it's nice when there's a tag attached.)

Gotcha, that makes sense.

I do fear the upcoming package manager changes are going to create a lot of problems or busy work if every commit effectively has to have a unique version. It'd be nice if the package manager could support version templating or something when used in conjunction with version control tags. .version = "1.0.8-dev.{n}+upstream.3.4.18.git.{id}". Obviously that wouldn't work with archives, but most distributions will only be producing archives for tagged, concrete versions anyway.

I think there's still room to explore how exactly this works in the build system (just saw your comment on the respective issue.)

FWIW I don't think every commit needs a unique version--just anything that main is going to point at for a substantial amount of time. For example, there's no reason to bump the version multiple times within the same PR since on merge main will jump to the last commit. Similarly, if merging two PRs in a row, it's probably fine to do the version bump after the sequence of merges.

An alternative strategy would be to not treat main as stable, and instead tag any release that's intended to be depended on. There are definitely advantages to this, but I'm experimenting with the former approach first since it requires less clicks.

.minimum_zig_version = "0.16.0",
.dependencies = .{
.sdl = .{
Expand Down
71 changes: 22 additions & 49 deletions src/linux.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,6 @@ pub fn build(
else => "pulseaudio",
};

root.prefetch(b, .{
.{ .name = "dbus", .args = .{} },
.{ .name = "x11", .args = .{} },
.{ .name = "xcursor", .args = .{} },
.{ .name = "pipewire", .args = .{} },
.{ .name = pulseaudio_name, .args = .{} },
.{ .name = "wayland", .args = .{} },
.{ .name = "alsa", .args = .{} },
.{ .name = "fribidi", .args = .{} },
.{ .name = "ibus", .args = .{} },
.{ .name = "xkbcommon", .args = .{} },
.{ .name = "xorgproto", .args = .{} },
.{ .name = "xext", .args = .{} },
.{ .name = "usb", .args = .{} },
.{ .name = "xi", .args = .{} },
.{ .name = "xfixes", .args = .{} },
.{ .name = "xrandr", .args = .{} },
.{ .name = "xrender", .args = .{} },
.{ .name = "xscrnsaver", .args = .{} },
.{ .name = "jack", .args = .{} },
.{ .name = "sndio", .args = .{} },
.{ .name = "decor", .args = .{} },
.{ .name = "thai", .args = .{} },
}) catch |err| switch (err) {
error.LazyDependencyNeeded => return,
};

const upstream = b.dependency("sdl", .{});

// Provide the platform specific dependency include paths
Expand All @@ -53,7 +26,7 @@ pub fn build(
lib.root_module.addIncludePath(generated.getDirectory());

// Provide the D-Bus headers
if (root.lazyDependency(b, "dbus")) |dbus| {
if (root.lazyDependency(&lib.step, "dbus", .{})) |dbus| {
lib.root_module.addIncludePath(dbus.path("."));

const version_string = build_zon.dependencies.dbus.version;
Expand Down Expand Up @@ -138,7 +111,7 @@ pub fn build(
// Provide the IBus headers
{
// The headers are here
root.addDepIncludePath(b, lib, "ibus", "src");
root.addDepIncludePath(lib, "ibus", "src", .{});

// They depend on the GLib headers, which require some configuration
lib.root_module.addIncludePath(b.path("deps/glib/upstream/include"));
Expand Down Expand Up @@ -321,7 +294,7 @@ pub fn build(

// Provide the X11 headers
{
if (root.lazyDependency(b, "x11")) |x11| {
if (root.lazyDependency(&lib.step, "x11", .{})) |x11| {
lib.root_module.addIncludePath(x11.path("include"));

const config = b.addConfigHeader(.{
Expand All @@ -335,7 +308,7 @@ pub fn build(
}

// Provide the Xcursor headers
if (root.lazyDependency(b, "xcursor")) |xcursor| {
if (root.lazyDependency(&lib.step, "xcursor", .{})) |xcursor| {
const version_string = build_zon.dependencies.xcursor.version;
const version = comptime std.SemanticVersion.parse(version_string) catch unreachable;
const config = b.addConfigHeader(.{
Expand Down Expand Up @@ -381,7 +354,7 @@ pub fn build(
}

// Provide the pipewire headers
if (root.lazyDependency(b, "pipewire")) |pipewire| {
if (root.lazyDependency(&lib.step, "pipewire", .{})) |pipewire| {
lib.root_module.addIncludePath(pipewire.path("spa/include"));
lib.root_module.addIncludePath(pipewire.path("src"));
const version_string = build_zon.dependencies.pipewire.version;
Expand All @@ -402,7 +375,7 @@ pub fn build(
// Provide the pulseaudio headers
{
@setEvalBranchQuota(2000);
if (root.lazyDependency(b, pulseaudio_name)) |pulseaudio| {
if (root.lazyDependency(&lib.step, pulseaudio_name, .{})) |pulseaudio| {
lib.root_module.addIncludePath(pulseaudio.path("src"));
const version_string = build_zon.dependencies.pulseaudio.version;
const version = comptime std.SemanticVersion.parse(version_string) catch unreachable;
Expand All @@ -424,7 +397,7 @@ pub fn build(
}

// Provide the Wayland headers
if (root.lazyDependency(b, "wayland")) |wayland| {
if (root.lazyDependency(&lib.step, "wayland", .{})) |wayland| {
lib.root_module.addIncludePath(wayland.path("src"));
lib.root_module.addIncludePath(wayland.path("cursor"));
lib.root_module.addIncludePath(wayland.path("egl"));
Expand Down Expand Up @@ -452,7 +425,7 @@ pub fn build(
}

// Provide the Alsa headers
if (root.lazyDependency(b, "alsa")) |alsa| {
if (root.lazyDependency(&lib.step, "alsa", .{})) |alsa| {
_ = generated.addCopyDirectory(alsa.path("include"), "alsa", .{
.include_extensions = &.{".h"},
});
Expand All @@ -461,7 +434,7 @@ pub fn build(
}

// Provide the Fribidi headers
if (root.lazyDependency(b, "fribidi")) |fribidi| {
if (root.lazyDependency(&lib.step, "fribidi", .{})) |fribidi| {
const interface_version = build_zon.dependencies.fribidi.interface_version;
const version_string = build_zon.dependencies.fribidi.version;
const version = comptime std.SemanticVersion.parse(version_string) catch unreachable;
Expand Down Expand Up @@ -507,21 +480,21 @@ pub fn build(
}

// Provide upstream headers that don't require any special handling
root.addDepIncludePath(b, lib, "xkbcommon", "include");
root.addDepIncludePath(b, lib, "xorgproto", "include");
root.addDepIncludePath(b, lib, "xext", "include");
root.addDepIncludePath(b, lib, "usb", "libusb");
root.addDepIncludePath(b, lib, "xi", "include");
root.addDepIncludePath(b, lib, "xfixes", "include");
root.addDepIncludePath(b, lib, "xrandr", "include");
root.addDepIncludePath(b, lib, "xrender", "include");
root.addDepIncludePath(b, lib, "xscrnsaver", "include");
root.addDepIncludePath(b, lib, "jack", "common");
root.addDepIncludePath(b, lib, "sndio", "libsndio");
root.addDepIncludePath(lib, "xkbcommon", "include", .{});
root.addDepIncludePath(lib, "xorgproto", "include", .{});
root.addDepIncludePath(lib, "xext", "include", .{});
root.addDepIncludePath(lib, "usb", "libusb", .{});
root.addDepIncludePath(lib, "xi", "include", .{});
root.addDepIncludePath(lib, "xfixes", "include", .{});
root.addDepIncludePath(lib, "xrandr", "include", .{});
root.addDepIncludePath(lib, "xrender", "include", .{});
root.addDepIncludePath(lib, "xscrnsaver", "include", .{});
root.addDepIncludePath(lib, "jack", "common", .{});
root.addDepIncludePath(lib, "sndio", "libsndio", .{});
lib.root_module.addIncludePath(b.path("deps/wayland/protocols"));
root.addDepIncludePath(b, lib, "decor", "src");
root.addDepIncludePath(lib, "decor", "src", .{});
lib.root_module.addIncludePath(b.path("deps/mesa/include"));
root.addDepIncludePath(b, lib, "thai", "include");
root.addDepIncludePath(lib, "thai", "include", .{});

// Provide vendored headers that don't require any special handling
lib.root_module.addIncludePath(b.path("deps/xcb/include"));
Expand Down
Loading