feat(macos,ios): entitlements + permission strings + bundle-glue script
macOS:
* Runner/DebugProfile.entitlements + Release.entitlements: add
com.apple.security.network.client (outbound TS3 server connect)
and com.apple.security.device.audio-input (microphone capture).
Debug keeps com.apple.security.network.server + cs.allow-jit
(Flutter hot-reload needs both); Release drops them.
* Runner/Info.plist: add NSMicrophoneUsageDescription and
NSInputMonitoringUsageDescription so the macOS system prompts
show a sensible explanation when Chanora first needs mic or
Input Monitoring access. Input Monitoring is required by
CGEventTapCreate (SDD-085).
* Runner.xcodeproj/project.pbxproj: switch Debug/Release/Profile
code-signing from Automatic + Apple Development to Manual +
"Sign to Run Locally" (CODE_SIGN_IDENTITY = -). This lets
`flutter build macos --release` work over SSH where the login
keychain is locked. The owner re-enables the personal team
locally in Xcode for physical-device iOS testing later.
iOS:
* Runner/Info.plist: add NSMicrophoneUsageDescription and the
UIBackgroundModes = ['audio'] entry so voice traffic continues
when the app is backgrounded (TS3 servers drop clients on idle
audio streams).
tools/macos-postbuild.sh: new script. flutter build macos --release
emits build/macos/Build/Products/Release/chanora_flutter.app but
does NOT bundle libchanora_bridge.dylib. FRB on macOS dlopen()s the
bridge as chanora_bridge.framework/chanora_bridge, not a plain
dylib. This script:
1. Wraps target/release/libchanora_bridge.dylib in a proper
chanora_bridge.framework (Versions/A layout, Info.plist,
Resources, symlinks).
2. Rewrites LC_ID_DYLIB to
@rpath/chanora_bridge.framework/chanora_bridge.
3. Ad-hoc codesigns the framework and the .app bundle.
4. Verifies with codesign --verify --deep --strict.
macOS analogue of buildit.cmd on Windows. Auto-integration into
Xcode build phases via cargokit / corrosion is a P1 carryover.
Verified end-to-end on the M1 Mac:
cargo build --release -p chanora_bridge 11.76 s
flutter build macos --release ok (59.2 MB)
tools/macos-postbuild.sh Release ok
chanora_flutter.app launch via SSH bridge initialised,
identity + bookmark
store initialised
(~5 s smoke).
~/Library/Logs/app.chanora.chanora_flutter/chanora.log captures
the boot sequence cleanly.
DEC-025 reference: macOS desktop is officially in scope.
This commit is contained in:
@@ -26,6 +26,8 @@
|
|||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>Chanora needs microphone access so you can talk on your TeamSpeak-compatible voice server.</string>
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
@@ -49,6 +51,10 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
</array>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
|
|||||||
@@ -259,7 +259,7 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 0920;
|
||||||
LastUpgradeCheck = 2650;
|
LastUpgradeCheck = 1510;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
331C80D4294CF70F00263BE5 = {
|
331C80D4294CF70F00263BE5 = {
|
||||||
@@ -269,7 +269,6 @@
|
|||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
CreatedOnToolsVersion = 9.2;
|
CreatedOnToolsVersion = 9.2;
|
||||||
LastSwiftMigration = 1100;
|
LastSwiftMigration = 1100;
|
||||||
ProvisioningStyle = Automatic;
|
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.Sandbox = {
|
com.apple.Sandbox = {
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
@@ -390,14 +389,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
@@ -579,10 +574,11 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = 349G7M4TQQ;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||||
@@ -728,10 +724,11 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = 349G7M4TQQ;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||||
@@ -762,10 +759,11 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = 349G7M4TQQ;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "2650"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -2,11 +2,18 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<!-- Flutter default: app-sandbox + JIT for the Dart VM + server
|
||||||
|
listener for `flutter run` hot-reload. -->
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<!-- Chanora additions: outbound TS3 server connect + mic capture. -->
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -28,5 +28,13 @@
|
|||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
|
<!-- Permission-prompt strings.
|
||||||
|
macOS shows these verbatim in the system dialog when Chanora
|
||||||
|
first triggers the permission. Avoid jargon — these are
|
||||||
|
user-facing. -->
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>Chanora needs microphone access so you can talk on your TeamSpeak-compatible voice server.</string>
|
||||||
|
<key>NSInputMonitoringUsageDescription</key>
|
||||||
|
<string>Chanora uses Input Monitoring so push-to-talk keys work even when other apps are focused. Chanora never records what you type — only the key you bound for talking.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<!-- Release builds omit the cs.allow-jit + network.server entitlements
|
||||||
|
used only by Flutter's debug hot-reload. -->
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Executable
+89
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Post-process the Flutter macOS Release bundle so it actually launches:
|
||||||
|
#
|
||||||
|
# 1. Wrap target/release/libchanora_bridge.dylib into a proper
|
||||||
|
# chanora_bridge.framework bundle (Versions/A layout, Info.plist,
|
||||||
|
# symlinks). FRB on macOS dlopen()s chanora_bridge.framework/
|
||||||
|
# chanora_bridge — not the plain dylib.
|
||||||
|
# 2. Rewrite the dylib's LC_ID_DYLIB so dyld resolves it as
|
||||||
|
# @rpath/chanora_bridge.framework/chanora_bridge.
|
||||||
|
# 3. Ad-hoc codesign the framework + the .app bundle.
|
||||||
|
#
|
||||||
|
# This is the macOS analogue of buildit.cmd on Windows. cocogkit /
|
||||||
|
# corrosion-style auto-integration into the Xcode project is a P1
|
||||||
|
# carryover — for now we run this script after `flutter build macos
|
||||||
|
# --release`.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# tools/macos-postbuild.sh [Release|Debug] (defaults to Release)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONFIG="${1:-Release}"
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
APP="$REPO_ROOT/apps/chanora_flutter/build/macos/Build/Products/$CONFIG/chanora_flutter.app"
|
||||||
|
DYLIB="$REPO_ROOT/target/release/libchanora_bridge.dylib"
|
||||||
|
FW="$APP/Contents/Frameworks/chanora_bridge.framework"
|
||||||
|
|
||||||
|
if [[ ! -d "$APP" ]]; then
|
||||||
|
echo "ERROR: app bundle not found at $APP" >&2
|
||||||
|
echo " run \`flutter build macos --$(echo "$CONFIG" | tr '[:upper:]' '[:lower:]')\` first" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$DYLIB" ]]; then
|
||||||
|
echo "ERROR: bridge dylib not found at $DYLIB" >&2
|
||||||
|
echo " run \`cargo build --release -p chanora_bridge\` first" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 1: build the chanora_bridge.framework layout.
|
||||||
|
echo "[postbuild] wrapping bridge into chanora_bridge.framework"
|
||||||
|
rm -rf "$FW"
|
||||||
|
mkdir -p "$FW/Versions/A/Resources"
|
||||||
|
cp "$DYLIB" "$FW/Versions/A/chanora_bridge"
|
||||||
|
|
||||||
|
cat > "$FW/Versions/A/Resources/Info.plist" << 'PLIST'
|
||||||
|
<?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>CFBundleExecutable</key>
|
||||||
|
<string>chanora_bridge</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>app.chanora.bridge</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>chanora_bridge</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
PLIST
|
||||||
|
|
||||||
|
ln -sf A "$FW/Versions/Current"
|
||||||
|
ln -sf Versions/Current/chanora_bridge "$FW/chanora_bridge"
|
||||||
|
ln -sf Versions/Current/Resources "$FW/Resources"
|
||||||
|
|
||||||
|
# Step 2: rewrite install_name so dyld finds the bundled copy.
|
||||||
|
echo "[postbuild] install_name_tool -id @rpath/chanora_bridge.framework/chanora_bridge"
|
||||||
|
install_name_tool \
|
||||||
|
-id "@rpath/chanora_bridge.framework/chanora_bridge" \
|
||||||
|
"$FW/Versions/A/chanora_bridge"
|
||||||
|
|
||||||
|
# Drop a stale plain dylib if a previous build left one alongside the
|
||||||
|
# framework.
|
||||||
|
rm -f "$APP/Contents/Frameworks/libchanora_bridge.dylib"
|
||||||
|
|
||||||
|
# Step 3: codesign. Ad-hoc identity ('-') is sufficient for local
|
||||||
|
# launches; replace with a real Developer ID when distributing.
|
||||||
|
echo "[postbuild] codesigning framework + app (ad-hoc)"
|
||||||
|
codesign --force -s - --timestamp=none "$FW/Versions/A/chanora_bridge"
|
||||||
|
codesign --force -s - --timestamp=none "$FW"
|
||||||
|
codesign --force --deep -s - --timestamp=none "$APP"
|
||||||
|
|
||||||
|
codesign --verify --deep --strict "$APP"
|
||||||
|
|
||||||
|
echo "[postbuild] OK: $APP is launchable"
|
||||||
Reference in New Issue
Block a user