Skip to content

Conversation

HaxSam
Copy link
Contributor

@HaxSam HaxSam commented Jul 6, 2025

I pulled out the structs from the function arguments because that makes it easier to include zemscripten in other libraries.
Specially because zig removed Anonymous Struct.

@HaxSam
Copy link
Contributor Author

HaxSam commented Aug 15, 2025

Could I get an update on the PR?
I need the change so I can include zemscripten into the build system for raylib.

@hazeycode hazeycode requested a review from Copilot August 15, 2025 13:49
Copilot

This comment was marked as outdated.

Copy link
Member

@hazeycode hazeycode left a comment

Choose a reason for hiding this comment

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

Sorry I neglected this. Just a couple or minor naming things other than the issue flagged by the bot. Note that the intended interface for flags and settings are hash maps... meaning these are just convenience fns that override some reasonable defaults since we don't intend to define all emcc flags and options.

Btw these are not actually anonymous, they are named by the compiler. I don't understand why the current interface is not sufficient sorry. Could you elaborate on the problem you are trying to solve?

@HaxSam
Copy link
Contributor Author

HaxSam commented Aug 16, 2025

I wanted to wrap zemscripten in the raylib build system with the needed default flags for raylib while the usage still feel like zemscripten.
I didn't wanted to copy paste structs from the lib so it stays more in sync with zemscripten.
Here is an example how I wanna implement it in raylib.

pub const emsdk = struct {
    const zemscripten = @import("zemscripten");

    pub fn shell(b: *std.Build, raylib_dep: *std.Build.Dependency) []const u8 {
        return raylib_dep.path("src/shell.html").getPath(b);
    }

    pub const FlagsOptions = struct {
        optimize: std.builtin.OptimizeMode,
        asyncify: bool = true,
    };

    pub fn emccDefaultFlags(allocator: std.mem.Allocator, options: FlagsOptions) zemscripten.EmccFlags {
        var emcc_flags = zemscripten.emccDefaultFlags(allocator, .{
            .optimize = options.optimize,
            .fsanitize = true,
        });

        if (options.asyncify)
            emcc_flags.put("-sASYNCIFY", {}) catch unreachable;

        return emcc_flags;
    }

    pub const SettingsOptions = struct {
        optimize: std.builtin.OptimizeMode,
        es3: bool = true,
        emsdk_allocator: zemscripten.EmsdkAllocator = .emmalloc,
    };

    pub fn emccDefaultSettings(allocator: std.mem.Allocator, options: SettingsOptions) zemscripten.EmccSettings {
        var emcc_settings = zemscripten.emccDefaultSettings(allocator, .{
            .optimize = options.optimize,
            .emsdk_allocator = options.emsdk_allocator,
        });

        if (options.es3)
            emcc_settings.put("FULL_ES3", "1") catch unreachable;
        emcc_settings.put("USE_GLFW", "3") catch unreachable;
        emcc_settings.put("EXPORTED_RUNTIME_METHODS", "['requestFullscreen']") catch unreachable;

        return emcc_settings;
    }

    pub fn emccStep(b: *std.Build, raylib: *std.Build.Step.Compile, wasm: *std.Build.Step.Compile, options: zemscripten.StepOptions) *std.Build.Step {
        const activate_emsdk_step = zemscripten.activateEmsdkStep(b);

        const emsdk_dep = b.dependency("emsdk", .{});
        raylib.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include"));
        wasm.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include"));

        const emcc_step = zemscripten.emccStep(b, wasm, options);
        emcc_step.dependOn(activate_emsdk_step);

        return emcc_step;
    }

    pub fn emrunStep(
        b: *std.Build,
        html_path: []const u8,
        extra_args: []const []const u8,
    ) *std.Build.Step {
        return zemscripten.emrunStep(b, html_path, extra_args);
    }
};

Also the problem I had at the start why I also mentioned anonymous structs was when I copy pasted the options into a struct for example

pub const FlagsOptions = struct {
    optimize: std.builtin.OptimizeMode,
    fsanitize: bool,
};

and wanted to use it and just give it into the method

const options: FlagsOptions = .{.optimize = optimize, .fsanitize = true};
const emcc_flags = emccDefaultFlags(b.allocator, options);

it wouldn't work


I would start fixing the comments if u feel like that your questions are answer why I want that change.

@HaxSam HaxSam force-pushed the pullout_options branch 5 times, most recently from fe79954 to 2fe7535 Compare August 22, 2025 16:06
@HaxSam HaxSam requested a review from hazeycode August 22, 2025 16:08
@HaxSam
Copy link
Contributor Author

HaxSam commented Aug 22, 2025

Had to fight a bit with git.
Hope you can understand why I want this change.

@hazeycode
Copy link
Member

hazeycode commented Aug 28, 2025

I am reluctant to accept the rationale here. You are providing a different API and should be explicit about this instead of trying to make it look like it's the same thing (see zig zen).

We can make the case to put more flags and settings into the upstream defaults and doing so would make some of the incentive for this change disappear. But in general, I would recommend something like the following instead:

    pub fn makeEmccSettings(allocator: std.mem.Allocator, args: struct {
        optimize: std.builtin.OptimizeMode,
        gl_es_version: enum {es2, es3} = .es2,
        // i.e. any project specific options that you want to expose to your users
    }) !zemscripten.EmccSettings {
        var settings = zemscripten.emccDefaultSettings(allocator, .{
            .optimize = args.optimize,
        });
        
        try settings.put("USE_GLFW", "3");
        
        switch (args.gl_es_version) {
           .es2 => ...
        // etc. etc.
        
        return settings;
    }

To reiterate, emccDefaultFlags and emccDefaultSettings are merely convenience functions and the core API here is simply hash maps that you should construct yourself if the convenience functions do not meet your needs.

@HaxSam
Copy link
Contributor Author

HaxSam commented Aug 29, 2025

I can't figure out what you mean by zig zen. I can't find a case where my code contradicts the guidelines you mentioned.
I agree it would be cool to get more flag options upstream, to the point where you can define all flags with a struct and good defaults.
I still think it would be better to put the function options into their own structs; that way, it would be more readable. See the Zig codebase, for example: Step.Options, Step.MakeOptions, Progress.Options. Zig usually does not use this style of coding.

@hazeycode hazeycode merged commit 3fa4b77 into zig-gamedev:main Aug 29, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants