Open wojtekmach opened 2 years ago
I was able to make it work. It requires two things:
erts/etc/darwin/Info.plist
erlexec
and beam.smp
in the same directory as the app's executable. A symlink is enough.Here is roughly how the bundle looks like:
MyApp.app/
Contents/
Info.plist
MacOS/
MyApp # bash script
erlexec # symlink to ../Resources/otp/erts-VSN/erlexec
beam.smp # symlink to ../Resources/otp/erts-VSN/beam.smp
Resources/
otp/
I'd love to find a solution that doesn't require copying or symlinking files to the Contents/MacOS directory and instead set everything in the launcher executable. Any help with that would be very appreciated.
For anyone interested, here is a step-by-step guide how to reproduce my results in an OTP git checkout.
Step 1: add some debugging
diff --git a/lib/wx/c_src/wxe_ps_init.c b/lib/wx/c_src/wxe_ps_init.c
index 473354a671..56a0d13ec9 100644
--- a/lib/wx/c_src/wxe_ps_init.c
+++ b/lib/wx/c_src/wxe_ps_init.c
@@ -58,6 +58,8 @@ void * wxe_ps_init2() {
// Setup and enable gui
pool = [[NSAutoreleasePool alloc] init];
+ NSLog(@"bundleURL=%@", [[NSBundle mainBundle] bundleURL]);
+ NSLog(@"bundleIdentifier=%@", [[NSBundle mainBundle] bundleIdentifier]);
if( !is_packaged_app() ) {
// Undocumented function (but no documented way of doing this exists)
Step 2: Remove Info.plist
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 979f76c4c6..c73c3bc1c7 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -242,9 +242,6 @@ VOID_EMULATOR =
endif
OPSYS=@OPSYS@
-ifeq ($(OPSYS),darwin)
-LDFLAGS += -sectcreate __TEXT __info_plist "$(ERL_TOP)/erts/etc/darwin/Info.plist"
-endif
sol2CFLAGS=
linuxCFLAGS=
diff --git a/erts/etc/darwin/Info.plist b/erts/etc/darwin/Info.plist
deleted file mode 100644
index 3896524588..0000000000
--- a/erts/etc/darwin/Info.plist
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>NSAppTransportSecurity</key>
- <dict>
- <key>NSExceptionDomains</key>
- <dict>
- <key>localhost</key>
- <dict>
- <key>NSExceptionAllowsInsecureHTTPLoads</key>
- <true/>
- <key>NSIncludesSubdomains</key>
- <true/>
- </dict>
- </dict>
- </dict>
-</dict>
-</plist>
Step 3: Run this script to build the app
#!/bin/sh
set -e pipefail
export MAKEFLAGS=-j8
export ERL_TOP=$PWD
erts_version="13.0.2"
# ./otp_build setup -a
# ./otp_build boot -a
# ./otp_build release -a
rm -rf MyApp.app
mkdir -p MyApp.app/Contents/Resources
cp -R release/`$ERL_TOP/make/autoconf/config.guess` MyApp.app/Contents/Resources/otp
(cd MyApp.app/Contents/Resources/otp && ./Install -sasl $PWD)
cat << EOF > MyApp.app/Contents/Info.plist
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>MyApp</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundleIdentifier</key>
<string>myapp</string>
</dict>
</plist>
EOF
mkdir -p MyApp.app/Contents/MacOS
(cd MyApp.app/Contents/MacOS && ln -s ../Resources/otp/erts-${erts_version}/bin/{erlexec,beam.smp} .)
cat << EOF > MyApp.app/Contents/MacOS/MyApp
#!/bin/sh
set -e pipefail
cwd=\$(dirname \$0)
export EMU=beam
export BINDIR=\$cwd
export ROOTDIR=\$cwd/../Resources/otp
\$cwd/erlexec -noshell -eval 'wx:new(), timer:sleep(5000), halt().'
EOF
chmod +x MyApp.app/Contents/MacOS/MyApp
Step 4: Run the app
$ open --stdout $TTY --stderr $TTY MyApp.app
For me, it now prints:
2022-06-28 12:34:59.511 beam.smp[28676:1151422] bundleURL=file:///Users/wojtek/src/otp/MyApp.app/
2022-06-28 12:34:59.511 beam.smp[28676:1151422] bundleIdentifier=myapp
Even if we go with the hack from the previous comment, putting erlexec and beam.smp into particular locations in the bundle, we'd be running the VM in a separate OS process from the "main" executable (the one at MyApp.app/Contents/MacOS/MyApp
) and it seems that is the crux of the issue.
Another way to solve is to statically link Erlang into the main executable and start it from there. Below is a script that does just that:
Which would print:
bundleURL=file:///Users/wojtek/src/otp/MyApp.app/
infoDictionary={
CFBundleIdentifier = myapp;
CFBundleName = MyApp;
CFBundleNumericVersion = 16809984;
CFBundleVersion = "1.0.0";
}
which is exactly what I wanted.
Worth mentioning this is a very similar approach to deploying OTP on iOS where we need to statically link everything. On a Mac, though, it is totally fine to dynamically link things.
I believe this way of deploying OTP in macOS apps is pretty promising.
At the moment one obvious problem with this is it relies on internals, namely this function:
extern void erl_start(int argc, char **argv);
and various libX.a files.
If this approach is reasonable, it would be really convenient if otp_build
could output something like this liberl.a (and/or .so) and an associated header.
Looking forward to any feedback!
PS Big thanks to @dominicletz for working on deploying OTP to iOS and recently helping us using the statically linking approach on Mac.
Describe the bug When packaging up a Erlang in an .app bundle on macOS, the system runtime incorrectly reports the bundle location and cannot retrieve any of the bundle keys.
Here is at least one place in OTP that could benefit from improvements in this area: https://github.com/erlang/otp/blob/OTP-25.0.1/lib/wx/c_src/wxe_ps_init.c#L38:L41
It might be a problem in OTP but it is also very possible it's just a matter of proper packaging.
To Reproduce
For me, it prints:
Expected behavior
Report the proper bundle information:
Affected versions Most likely all
Additional context
I have determined that updating
erts/etc/darwin/Info.plist
, e.g. settingCFBundleIdentifier
will make it so that identifier will be reported by macOS runtime. That is not helping much as ideally we wouldn't need to touch that file. In fact in https://github.com/erlang/otp/pull/5393 we specifically removed some CFBundle* fields exactly so that people that do packaging could set everything themselves.