Skip to content

Build for macOS#426

Draft
dudeofawesome wants to merge 6 commits intoKesomannen:masterfrom
dudeofawesome:feat/darwin-build
Draft

Build for macOS#426
dudeofawesome wants to merge 6 commits intoKesomannen:masterfrom
dudeofawesome:feat/darwin-build

Conversation

@dudeofawesome
Copy link

@dudeofawesome dudeofawesome commented Aug 30, 2025

This build is partially functional, but still needs work.

Things missing / broken in my testing so far:

  • No "Quit Gale" option in the menu.
    image

  • Mods don't load with game launch.
    You can get mods to load by running a command like this from the terminal:

    arch -x86_64 ~/Library/Application\ Support/com.kesomannen.gale/valheim/profiles/Default/start_game_bepinex.sh \
      ~/Library/Application\ Support/Steam/steamapps/common/Valheim/Valheim.app \
      --doorstop-enabled true \
      --doorstop-target-assembly "/Users/dudeofawesome/Library/Application Support/com.kesomannen.gale/valheim/profiles/Default/BepInEx/core/BepInEx.Preloader.dll"

    Update: I found that setting the game's launch arguments in Steam works to get the game to launch with mods from both Gale & Steam directly:

    /usr/bin/arch -x86_64 '/Users/dudeofawesome/Library/Application Support/com.kesomannen.gale/valheim/profiles/Default/start_game_bepinex.sh' %command%

    This still requires modifying the game's settings in Steam though, so this still isn't an actual production-ready solution.

    Update 2: It looks like BepinEx requires the above launch arguments work-around to function with native games on Linux / macOS. I'm not sure how Gale manages to launch modded native games at all on Linux for example? Is this work-around required there as well, and just not documented?

  • Deep links do not work
    This means that Profile sync's Sign in with Discord doesn't work.
    The macOS build seems to not be receiving the correct CLI args upon clicking a link.
    image

    [bug] Register deep-link with single instance not working. tauri-apps/tauri#12726

    Update: It seems like there are maybe 2 problems here.

    1. Deep linking opens a new instance, which means that the single instance plugin comes into play here.

    2. Deep links on macOS function more like deep links on mobile, rather than the other desktop platforms.

      This means that:

      1. Deep links must be registered in config, not at run time.
      2. I believe Deep links are not passed as CLI args to the bin, unlike Windows & Linux.

I've also added a Nix flake to simplify spinning up a development environment. It can be used by running nix develop.

config::commands::open_config_file,
config::commands::delete_config_file,
])
.plugin(tauri_plugin_single_instance::init(handle_single_instance))
Copy link
Author

Choose a reason for hiding this comment

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

@TianMengLucky
Copy link
Contributor

So where is this PR stuck? By the way, Tauri only supports deep link runtime configuration for Windows and Linux. Other platforms need to be configured using configuration files.

@LewsTherinTelescope
Copy link

Unfortunately I'm running quite low on disk space and can't build the app, but something like this should work for desktop shortcut generation:

Code
         #[cfg(target_os = "macos")]
        {
            let script_name = "Gale";
            
            let info_path = &shortcut_path.join("Contents/Info.plist");
            let info_content = format!(
                "<?xml version='1.0' encoding='UTF-8'?>\n\
                <!DOCTYPE plist PUBLIC '-//Apple Computer//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>\n\
                <plist version='1.0'>\n\
                <dict>\n\
                <key>CFBundleExecutable</key>\n\
                <string>{}</string>\n\
                </dict>\n\
                </plist>",
                script_name
            );

            let script_path = &shortcut_path.join("Contents/MacOS").join(script_name);
            let script_content = format!(
                "#!/bin/sh\n\
                 '{}' --game {} --profile '{}' --launch --no-gui",
                exe_path.to_string_lossy(),
                self.game.slug,
                profile.name
            );

            std::fs::create_dir_all(&shortcut_path.join("Contents/MacOS"))
                .context("Failed to create shortcut folder")?;
            std::fs::write(&info_path, info_content)
                .context("Failed to write shortcut info file")?;
            std::fs::write(&script_path, script_content)
                .context("Failed to write shortcut script file")?;

            std::fs::set_permissions(
                &script_path,
                std::os::unix::fs::PermissionsExt::from_mode(0o755),
            )
            .context("Failed to set permissions on shortcut script file")?;
        }

Update 2: It looks like BepinEx requires the above launch arguments work-around to function with native games on Linux / macOS. I'm not sure how Gale manages to launch modded native games at all on Linux for example? Is this work-around required there as well, and just not documented?

Based on #381, I think it's required there too and just not documented yet. The options discussed in #381 (comment) are relevant to the Mac implementation as well—currently the direct mode works like the Linux one and copies all the BepInEx files next to the app and runs the first script it finds, but if that changes there then it should probably change here too?

@LewsTherinTelescope
Copy link

Oh, implementation detail that might not be obvious: while games using run_bepinex.sh should launch fine on Mac, games using start_game_bepinex.sh won't inject Doorstop because the script was only designed with Linux in mind. You'll need a wrapper along these lines (this example takes the BepInEx directory as the first argument and passes the rest to the script):

Code
#!/bin/sh
dir=$1; shift
export DYLD_LIBRARY_PATH="${dir}/doorstop_libs:${DYLD_LIBRARY_PATH}"
if [ -z "${DYLD_INSERT_LIBRARIES}" ]; then
	export DYLD_INSERT_LIBRARIES="libdoorstop_x64.dylib"
else
	export DYLD_INSERT_LIBRARIES="libdoorstop_x64.dylib:${DYLD_INSERT_LIBRARIES}"
fi
source "${dir}/start_game_bepinex.sh" "$@"

It's possible that doing this by setting command.env in the Rust code could work instead, but at least when I was testing it in Bash it needed to be done inside a script that sources start_game_bepinex.sh in-place rather than setting before making the call. Probably worth trying it, at least.

I can't be absolutely certain that every app uses the names doorstop_libs or libdoorstop_x64.dylib, if needed maybe it could recursively search the folder for anything with libdoorstop in the name + the extension dylib and then add that file to DYLD_INSERT_LIBRARIES + its folder to DYLD_LIBRARY_PATH, but at least from a quick search that seems to be assumed in every start_game_bepinex.sh pack I'm seeing. (run_bepinex.sh packs often have libdoorstop.dylib in the root, but those already work fine so there's no need to do anything special for them.)

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.

3 participants