Skip to content

Commit b32ec36

Browse files
authored
MacOS improvement (#35)
* macos stuff :3 * macos stuff :3 * macos stuff :3 * macos stuff :3
1 parent 2ff0476 commit b32ec36

9 files changed

Lines changed: 169 additions & 15 deletions

File tree

.github/workflows/build.yml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: Build and Release TLEscope
22

33
on:
4+
workflow_dispatch:
45
push:
56
branches: [ "main", "master" ]
67
tags:
@@ -73,6 +74,31 @@ jobs:
7374
path: dist/TLEscope-Linux-arm64-Portable/
7475
retention-days: 14
7576

77+
build-macos:
78+
name: Build macOS
79+
runs-on: macos-latest
80+
steps:
81+
- name: Checkout Repository
82+
uses: actions/checkout@v4
83+
84+
- name: Install macOS Build Dependencies
85+
run: |
86+
brew install raylib pkg-config
87+
88+
- name: Compile and Bundle .app
89+
# bundler compiles the icon + ad-hoc signs natively on the runner
90+
run: make app
91+
92+
- name: Zip .app (preserves bundle structure and exec bits)
93+
run: ditto -c -k --sequesterRsrc --keepParent dist/TLEscope.app TLEscope-macOS.zip
94+
95+
- name: Upload macOS Artifact
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: TLEscope-macOS
99+
path: TLEscope-macOS.zip
100+
retention-days: 14
101+
76102
build-windows:
77103
name: Build Windows
78104
runs-on: windows-latest
@@ -162,7 +188,7 @@ jobs:
162188

163189
release:
164190
name: Publish Release
165-
needs: [build-linux, build-linux-arm64, build-windows, build-windows-arm64]
191+
needs: [build-linux, build-linux-arm64, build-macos, build-windows, build-windows-arm64]
166192
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v'))
167193
runs-on: ubuntu-latest
168194
permissions:
@@ -191,6 +217,8 @@ jobs:
191217
192218
# Move installer out of its artifact subdirectory for flat publishing
193219
mv TLEscope-Installer/TLEscope-Installer.exe ../
220+
# macOS artifact is already a ditto-zipped .app, just move it up
221+
mv TLEscope-macOS/TLEscope-macOS.zip ../
194222
cd ..
195223
196224
- name: Clean Existing Release Assets
@@ -223,6 +251,7 @@ jobs:
223251
files: |
224252
TLEscope-Linux-Portable.zip
225253
TLEscope-Linux-arm64-Portable.zip
254+
TLEscope-macOS.zip
226255
TLEscope-Win-Portable.zip
227256
TLEscope-Win-arm64-Portable.zip
228257
TLEscope-Installer.exe
@@ -235,6 +264,7 @@ jobs:
235264
files: |
236265
TLEscope-Linux-Portable.zip
237266
TLEscope-Linux-arm64-Portable.zip
267+
TLEscope-macOS.zip
238268
TLEscope-Win-Portable.zip
239269
TLEscope-Win-arm64-Portable.zip
240270
TLEscope-Installer.exe

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@ dist/
1313
# Compoile Commands CLANGD
1414
/compile_commands.json
1515

16+
# mac finder junk
17+
.DS_Store
18+
1619
# csv exports
1720
*.csv

Makefile

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,16 @@ APP_DIR ?= /usr/share/applications
4242

4343
# macOS (Apple Silicon / Intel)
4444
CC_MACOS = clang
45-
RAYLIB_CFLAGS = $(shell pkg-config --cflags raylib 2>/dev/null)
46-
RAYLIB_LIBS = $(shell pkg-config --libs raylib 2>/dev/null)
45+
empty :=
46+
space := $(empty) $(empty)
47+
MACOS_PC_PATH := $(subst $(space),:,$(strip $(wildcard /opt/homebrew/lib/pkgconfig /usr/local/lib/pkgconfig)))
48+
MACOS_PKG_CONFIG_ENV = PKG_CONFIG_PATH="$(PKG_CONFIG_PATH):$(MACOS_PC_PATH)"
49+
RAYLIB_CFLAGS = $(shell $(MACOS_PKG_CONFIG_ENV) pkg-config --cflags raylib 2>/dev/null)
50+
RAYLIB_LIBS = $(shell $(MACOS_PKG_CONFIG_ENV) pkg-config --libs raylib 2>/dev/null)
4751
LDFLAGS_MACOS = $(RAYLIB_LIBS) -lcurl -framework IOKit -framework Cocoa -framework OpenGL
4852
DIST_MACOS = dist/TLEscope-macOS-Portable
4953

50-
.PHONY: all linux macos windows windows-arm64 win-installer clean build bin install uninstall raylib raylib-crossbuild
54+
.PHONY: all linux macos app windows windows-arm64 win-installer clean build bin install uninstall raylib raylib-crossbuild
5155

5256
all: linux
5357

@@ -68,7 +72,11 @@ macos: bin/TLEscope-macos
6872
cp logo*.png $(DIST_MACOS)/ 2>/dev/null || true
6973
@echo "macOS build bundled in $(DIST_MACOS)/"
7074
@echo "Run it with: cd $(DIST_MACOS)/ && ./TLEscope"
71-
@echo "To make a MacOS bundle: ./macos_bundle.sh"
75+
@echo "To make a proper .app bundle: make app"
76+
77+
# proper .app bundle: compiles the icon, generates Info.plist, codesigns
78+
app: bin/TLEscope-macos
79+
./scripts/macos-bundle.sh
7280

7381
windows: bin/TLEscope.exe
7482
@mkdir -p $(DIST_WIN)
@@ -103,7 +111,7 @@ bin/TLEscope: $(OBJ) | bin
103111
$(CC_LINUX) $(CFLAGS) -o $@ $^ $(LDFLAGS_LIN)
104112

105113
bin/TLEscope-macos: $(SRC) | bin
106-
@if ! pkg-config --exists raylib 2>/dev/null; then echo "Error: raylib not found. Install with: brew install raylib"; exit 1; fi
114+
@if ! $(MACOS_PKG_CONFIG_ENV) pkg-config --exists raylib 2>/dev/null; then echo "Error: raylib not found. Install with: brew install raylib"; exit 1; fi
107115
$(CC_MACOS) $(CFLAGS) $(RAYLIB_CFLAGS) -o $@ $^ $(LDFLAGS_MACOS)
108116

109117
bin/TLEscope.exe: $(SRC) | bin

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,11 @@ brew install raylib pkg-config
9494
git clone https://github.com/aweeri/TLEscope
9595
cd TLEscope
9696
make macos # Results in dist/TLEscope-macOS-Portable/
97+
make app # Results in dist/TLEscope.app
9798
```
9899

100+
`make app` additionally bundles everything into a proper `TLEscope.app`: it compiles `AppIcon.icns` from `logo.png`, generates an `Info.plist` with the git version baked in, and ad-hoc codesigns the bundle (required for Apple Silicon to run at all). To sign with a real identity instead, set `CODE_SIGN_IDENTITY="Developer ID Application: ..."` before bundling. The app must be built on macOS itself - the icon and signing steps use native tooling.
101+
99102
**Windows systems**
100103
Install [MSYS2](https://www.msys2.org/), then run the following in a UCRT64 or MINGW64 terminal:
101104
```

bundle/Contents/MacOS/TLEscope

Lines changed: 0 additions & 4 deletions
This file was deleted.
-355 KB
Binary file not shown.

macos_bundle.sh

Lines changed: 0 additions & 5 deletions
This file was deleted.

scripts/macos-bundle.sh

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env bash
2+
# bundles a freshly built macOS binary into a proper TLEscope.app
3+
#
4+
# does everything natively on the build machine:
5+
# - compiles AppIcon.icns from logo.png (sips + iconutil, no precompiled icon)
6+
# - generates Info.plist with the real git version baked in
7+
# - ad-hoc codesigns by default (arm64 binaries refuse to run unsigned)
8+
#
9+
# usage: scripts/macos-bundle.sh [binary] default binary: bin/TLEscope-macos
10+
#
11+
# env vars:
12+
# CODE_SIGN_IDENTITY set to e.g. "Developer ID Application: Your Name (TEAMID)"
13+
# for real signing. default is "-" (ad-hoc).
14+
set -euo pipefail
15+
16+
APP_NAME="TLEscope"
17+
BUNDLE_ID="com.aweeri.tlescope"
18+
BIN="${1:-bin/${APP_NAME}-macos}"
19+
APP="dist/${APP_NAME}.app"
20+
21+
GIT_VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "vUnknown")
22+
23+
for tool in sips iconutil codesign; do
24+
command -v "$tool" >/dev/null || { echo "error: '$tool' not found - this script must run on macOS"; exit 1; }
25+
done
26+
[ -x "$BIN" ] || { echo "error: $BIN not found - run 'make macos' first"; exit 1; }
27+
28+
rm -rf "$APP"
29+
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
30+
31+
# portable resource layout lives inside Resources (the binary chdirs there)
32+
cp -r themes "$APP/Contents/Resources/themes"
33+
cp logo.png logo_l.png "$APP/Contents/Resources/"
34+
cp settings.json "$APP/Contents/Resources/" 2>/dev/null || true
35+
cp data.tle "$APP/Contents/Resources/" 2>/dev/null || true
36+
cp "$BIN" "$APP/Contents/MacOS/$APP_NAME"
37+
38+
# compile a multi-resolution .icns from logo.png
39+
ICONSET_DIR="$(mktemp -d)"
40+
ICONSET="$ICONSET_DIR/${APP_NAME}.iconset"
41+
mkdir -p "$ICONSET"
42+
for size in 16 32 128 256 512; do
43+
double=$((size * 2))
44+
sips -z "$size" "$size" logo.png --out "$ICONSET/icon_${size}x${size}.png" >/dev/null
45+
sips -z "$double" "$double" logo.png --out "$ICONSET/icon_${size}x${size}@2x.png" >/dev/null
46+
done
47+
iconutil -c icns "$ICONSET" -o "$APP/Contents/Resources/AppIcon.icns"
48+
rm -rf "$ICONSET_DIR"
49+
50+
cat > "$APP/Contents/Info.plist" <<EOF
51+
<?xml version="1.0" encoding="UTF-8"?>
52+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
53+
<plist version="1.0">
54+
<dict>
55+
<key>CFBundleExecutable</key>
56+
<string>${APP_NAME}</string>
57+
<key>CFBundleIconFile</key>
58+
<string>AppIcon</string>
59+
<key>CFBundleIdentifier</key>
60+
<string>${BUNDLE_ID}</string>
61+
<key>CFBundleName</key>
62+
<string>${APP_NAME}</string>
63+
<key>CFBundleDisplayName</key>
64+
<string>${APP_NAME}</string>
65+
<key>CFBundlePackageType</key>
66+
<string>APPL</string>
67+
<key>CFBundleShortVersionString</key>
68+
<string>${GIT_VERSION}</string>
69+
<key>CFBundleVersion</key>
70+
<string>${GIT_VERSION}</string>
71+
<key>NSHighResolutionCapable</key>
72+
<true/>
73+
<key>LSMinimumSystemVersion</key>
74+
<string>11.0</string>
75+
<key>NSHumanReadableCopyright</key>
76+
<string>See LICENSE</string>
77+
</dict>
78+
</plist>
79+
EOF
80+
81+
# signing: ad-hoc by default so the app actually launches on Apple Silicon.
82+
# with CODE_SIGN_IDENTITY set we also opt into hardened runtime (needed for notarization).
83+
# note: settings.json lives inside Resources, so editing it in-place after
84+
# signing invalidates the signature - Gatekeeper only cares on first launch tho
85+
if [ "${CODE_SIGN_IDENTITY:--}" = "-" ]; then
86+
echo "ad-hoc signing $APP (set CODE_SIGN_IDENTITY for real signing)"
87+
codesign --force --deep --sign - "$APP"
88+
else
89+
codesign --force --deep --sign "$CODE_SIGN_IDENTITY" --options runtime "$APP"
90+
fi
91+
codesign --verify --strict "$APP"
92+
93+
echo "built $APP (${GIT_VERSION})"

src/main.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,28 @@
66
#include <stdlib.h>
77
#include <string.h>
88

9+
#if defined(__APPLE__)
10+
#include <unistd.h>
11+
#include <libgen.h>
12+
#include <mach-o/dyld.h>
13+
/* launchd starts .app bundles with cwd=/ but all our assets (themes,
14+
* settings.json, logo.png, ...) load relative to cwd, so when the binary
15+
* lives inside a bundle hop over to Contents/Resources. no wrapper needed */
16+
static void BundleChdir(void)
17+
{
18+
char exe[4096];
19+
uint32_t size = sizeof(exe);
20+
if (_NSGetExecutablePath(exe, &size) != 0)
21+
return;
22+
char *contents = strstr(exe, "/Contents/MacOS/");
23+
if (!contents)
24+
return;
25+
strcpy(contents + strlen("/Contents"), "/Resources");
26+
if (chdir(exe) != 0)
27+
fprintf(stderr, "warning: could not chdir into %s\n", exe);
28+
}
29+
#endif
30+
931
#include "astro.h"
1032
#include "config.h"
1133

@@ -530,6 +552,10 @@ static bool GetMouseEarthIntersection(Vector2 mouse, bool is_2d, Camera2D cam2d,
530552

531553
int main(void)
532554
{
555+
#if defined(__APPLE__)
556+
BundleChdir();
557+
#endif
558+
533559
LoadAppConfig("settings.json", &cfg);
534560

535561
/* window setup and msaa */

0 commit comments

Comments
 (0)