`mainProgram` for Mac OS Apps

How do we specify the meta.mainProgram for a Mac OS App?

Should a shell script $out/bin/my-app that looks like

#!/nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-bash-0.0.p00
open /nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-my-app/Applications/MyApp.app

or should we do

ln -s "$out/Applications/MyApp.app/Contents/MacOS/MyApp" "$out/bin/my-app"

to make

meta.mainProgram = my-app;

?

I’m not a Mac OS user, and I’m not sure how the open command in Mac OS works. Is it an executable that we can called from the shells, or just a built-in in the Mac-specific Terminal Application? Can the binary located in Application/MyApp.app/Contents/MacOS be executed directly?

1 Like

here is an explanation of starting a mac app from terminal:

https://macissues.com/2015/02/06/how-to-launch-os-x-apps-via-the-terminal/

1 Like

The binary in MyApp.app/Contents/MacOS can in fact be executed directly. The downside is it will now be connected to the terminal and therefore your terminal will be blocked on it just like on any CLI program until such time as you quit the app. Also, executing it directly like this will actually produce multiple instances of the GUI app running simultaneously.

So I guess the question here is “what behavior should happen if I nix run nixpkgs#myApp?”. Should it spawn an instance of the app and block until the app is quit, or should it just launch the app?

My vote here is “just launch the app”, which does mean using /usr/bin/open to launch it. Unfortunately mianProgram is just a name that is run from $out/bin/<name>, which does mean that you need to actually have something in $out/bin to execute, and this means that whatever you put there will end up in the user’s PATH. Having a script in my PATH that just launches a GUI app is pretty weird; usually GUI apps with CLI tools will actually do smart behavior with those tools instead of just launching the app (and these tools do tend to block, but they’ll often block until the spawned app’s window is closed rather than the whole app).

I’m actually curious what would happen if you said something like mainProgram = "../Applications/MyApp.app/Contents/MacOS/MyApp, would that actually work? If so, you could stick whatever script you want inside the app bundle and execute that, and the script can then run the app with /usr/bin/open (note: please do pass arguments along, /usr/bin/open -a /path/to/MyApp.app arg1 arg2 will open the app and pass the args to it as files to open, as though you right-clicked the files and used Open With to open them with the app).

Edit: Glancing at the source, it literally says auto program = outPath + "/bin/" + mainProgram, so unless there’s explicit logic elsewhere that checks for .. in the path (very doubtful) then using a ../foo path should work.

1 Like

So it looks like an issue of design choices.

As a Linux user, I’m pretty used to the “block the terminal” way, as it’s the behavior of most of the programs, and it’s also a convenient way to get the real-time diagnostic information through the terminal output. Should I don’t need it, I’ll launch it from the “Run Command” equivalence provided by the desktop environment. Following the line, it’s quite natural for a program to have something executable in my $PATH.

Edit: Glancing at the source, it literally says auto program = outPath + "/bin/" + mainProgram , so unless there’s explicit logic elsewhere that checks for .. in the path (very doubtful) then using a ../foo path should work.

Should the launch-and-detach way be desired and the launcher kept away from the $PATH, a workaround would be to write a launcher script $opt/MyApp/my-app-launcher which contains something like

#!/nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-bash-0.0.p00
open /nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-my-app/Applications/MyApp.app

and specify

{
  meta.mainProgram = "../opt/MyApp/my-app-launcher";
}