diff --git a/src/build.zig b/src/build.zig index 3ca1e4c6a..5db1a818b 100644 --- a/src/build.zig +++ b/src/build.zig @@ -86,12 +86,34 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674 }); if (options.config.len > 0) { - const file = b.pathJoin(&.{ srcDir(b), "config.h" }); - const content = try std.fs.cwd().readFileAlloc(b.allocator, file, std.math.maxInt(usize)); - defer b.allocator.free(content); + // Note: Both this and the commented out code immediately below should behave the same. + // `@embedFile` probably captures the intent better, but has the unfortunate consequence of + // bloating up the size of a binary. The performance of a build system probably isn't vital. + // It should also be possible to get the list of configs needed from `src/config.h` at + // comptime such that this cost can be ignored AND simplify the logic. + const content = @embedFile("config.h"); + // Note: Usage of `std.build.pathFromRoot` is disadvised according to its documentation + //const file = b.pathFromRoot(b.pathJoin(&.{ srcDir(b), "config.h" })); + //const content = try std.fs.cwd().readFileAlloc(b.allocator, file, std.math.maxInt(usize)); + + // Sets a flag indiciating the use of a custom `config.h` + try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS"); + + // Splits a space-separated list of config flags into multiple flags + // + // Note: This means certain flags like `-x c++` won't be processed properly. + // `-xc++` or similar should be used when possible + var config_iter = std.mem.tokenizeScalar(u8, options.config, ' '); + + // Append config flags supplied by user to compile flags + while (config_iter.next()) |config_flag| + try raylib_flags_arr.append(b.allocator, config_flag); + + // Apply all relevant configs from `src/config.h` *except* the user-specified ones + var lines = std.mem.tokenizeScalar(u8, content, '\n'); + outer: while (lines.next()) |line| { + config_iter.reset(); - var lines = std.mem.splitScalar(u8, content, '\n'); - while (lines.next()) |line| { if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue; if (std.mem.startsWith(u8, line, "//")) continue; if (std.mem.startsWith(u8, line, "#if")) continue; @@ -100,19 +122,23 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. flag = flag["#define ".len - 1 ..]; // Remove #define flag = std.mem.trimLeft(u8, flag, " \t"); // Trim whitespace flag = flag[0 .. std.mem.indexOf(u8, flag, " ") orelse continue]; // Flag is only one word, so capture till space - flag = try std.fmt.allocPrint(b.allocator, "-D{s}", .{flag}); // Prepend with -D + flag = b.fmt("-D{s}", .{flag}); // Prepend with -D - // If user specifies the flag skip it - if (std.mem.containsAtLeast(u8, options.config, 1, flag)) continue; + // If a user already specified the flag, skip it + // + // Note: Currently using a suboptimal `O(m*n)` time algorithm where: + // `m` corresponds to the number of lines in `src/config.h` + // `n` corresponds to the number of user-specified flags + while (config_iter.next()) |config_flag| { + // For a user-specified flag to match, it must share the same prefix and have the + // same length or be followed by an equals sign + if (!std.mem.startsWith(u8, config_flag, flag)) continue; + if (config_flag.len == flag.len or config_flag[flag.len] == '=') continue :outer; + } // Append default value from config.h to compile flags try raylib_flags_arr.append(b.allocator, flag); } - - // Append config flags supplied by user to compile flags - try raylib_flags_arr.append(b.allocator, options.config); - - try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS"); } if (options.shared) { @@ -319,10 +345,28 @@ pub const Options = struct { shared: bool = false, linux_display_backend: LinuxDisplayBackend = .Both, opengl_version: OpenglVersion = .auto, - /// config should be a list of cflags, eg, "-DSUPPORT_CUSTOM_FRAME_CONTROL" + /// config should be a list of space-separated cflags, eg, "-DSUPPORT_CUSTOM_FRAME_CONTROL" config: []const u8 = &.{}, raygui_dependency_name: []const u8 = "raygui", + + const defaults = Options{}; + + fn getOptions(b: *std.Build) Options { + return .{ + .platform = b.option(PlatformBackend, "platform", "Choose the platform backedn for desktop target") orelse defaults.platform, + .raudio = b.option(bool, "raudio", "Compile with audio support") orelse defaults.raudio, + .raygui = b.option(bool, "raygui", "Compile with raygui support") orelse defaults.raygui, + .rmodels = b.option(bool, "rmodels", "Compile with models support") orelse defaults.rmodels, + .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext, + .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures, + .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes, + .shared = b.option(bool, "shared", "Compile as shared library") orelse defaults.shared, + .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, + .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, + .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse &.{}, + }; + } }; pub const OpenglVersion = enum { @@ -371,22 +415,7 @@ pub fn build(b: *std.Build) !void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const defaults = Options{}; - const options = Options{ - .platform = b.option(PlatformBackend, "platform", "Choose the platform backedn for desktop target") orelse defaults.platform, - .raudio = b.option(bool, "raudio", "Compile with audio support") orelse defaults.raudio, - .raygui = b.option(bool, "raygui", "Compile with raygui support") orelse defaults.raygui, - .rmodels = b.option(bool, "rmodels", "Compile with models support") orelse defaults.rmodels, - .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext, - .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures, - .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes, - .shared = b.option(bool, "shared", "Compile as shared library") orelse defaults.shared, - .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, - .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, - .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse &.{}, - }; - - const lib = try compileRaylib(b, target, optimize, options); + const lib = try compileRaylib(b, target, optimize, Options.getOptions(b)); lib.installHeader(b.path(b.pathJoin(&.{ srcDir(b), "raylib.h" })), "raylib.h"); lib.installHeader(b.path(b.pathJoin(&.{ srcDir(b), "raymath.h" })), "raymath.h");