Skip to content

Commit 7137666

Browse files
committed
Abstract apple framework creation behind a tool, which will lipo the input binaries together and create an appropriate Info.plist.
1 parent 47f11bc commit 7137666

File tree

6 files changed

+225
-83
lines changed

6 files changed

+225
-83
lines changed

test/SConstruct

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,30 @@ if env["target"] in ["editor", "template_debug"]:
1818
doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
1919
sources.append(doc_data)
2020

21-
if env["platform"] == "macos":
22-
library = env.SharedLibrary(
23-
"project/bin/libgdexample.{}.{}.framework/libgdexample.{}.{}".format(
24-
env["platform"], env["target"], env["platform"], env["target"]
25-
),
26-
source=sources,
27-
)
28-
elif env["platform"] == "ios":
29-
if env["ios_simulator"]:
30-
library = env.StaticLibrary(
31-
"project/bin/libgdexample.{}.{}.simulator.a".format(env["platform"], env["target"]),
32-
source=sources,
33-
)
34-
else:
35-
library = env.StaticLibrary(
36-
"project/bin/libgdexample.{}.{}.a".format(env["platform"], env["target"]),
37-
source=sources,
21+
library_targets = env.SharedLibrary(
22+
"project/bin/libgdexample{}{}".format(env["suffix"], env["SHLIBSUFFIX"]),
23+
source=sources,
24+
)
25+
26+
if env["platform"] == "macos" or env["platform"] == "ios":
27+
# The app store requires signed .framework bundles for dependencies.
28+
# We do not sign the test framework bundles anyway, but for consistency
29+
# (and testing) we will generate the .framework anyway.
30+
framework_tool = Tool("apple_framework", toolpath=["../tools"])
31+
32+
framework_name = f"gdexample.{env['platform']}.{env['target']}"
33+
34+
library_targets = framework_tool.generate(
35+
f"project/bin/{framework_name}.framework",
36+
env=env,
37+
source=library_targets,
38+
plist_keys=dict(
39+
CFBundleIdentifier=f"org.godotengine.{framework_name}"
3840
)
39-
else:
40-
library = env.SharedLibrary(
41-
"project/bin/libgdexample{}{}".format(env["suffix"], env["SHLIBSUFFIX"]),
42-
source=sources,
4341
)
4442

45-
env.NoCache(library)
46-
Default(library)
43+
# Keep the final build intact for as long as possible.
44+
env.Precious(library_targets)
45+
46+
env.NoCache(library_targets)
47+
Default(library_targets)

test/project/bin/libgdexample.macos.template_debug.framework/Resources/Info.plist

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

test/project/bin/libgdexample.macos.template_release.framework/Resources/Info.plist

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

test/project/example.gdextension

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ compatibility_minimum = "4.1"
55

66
[libraries]
77

8-
macos.debug = "res://bin/libgdexample.macos.template_debug.framework"
9-
macos.release = "res://bin/libgdexample.macos.template_release.framework"
8+
macos.debug = "res://bin/gdexample.macos.template_debug.framework"
9+
macos.release = "res://bin/gdexample.macos.template_release.framework"
1010
windows.debug.x86_32 = "res://bin/libgdexample.windows.template_debug.x86_32.dll"
1111
windows.release.x86_32 = "res://bin/libgdexample.windows.template_release.x86_32.dll"
1212
windows.debug.x86_64 = "res://bin/libgdexample.windows.template_debug.x86_64.dll"
@@ -27,17 +27,17 @@ android.debug.x86_64 = "res://bin/libgdexample.android.template_debug.x86_64.so"
2727
android.release.x86_64 = "res://bin/libgdexample.android.template_release.x86_64.so"
2828
android.debug.arm64 = "res://bin/libgdexample.android.template_debug.arm64.so"
2929
android.release.arm64 = "res://bin/libgdexample.android.template_release.arm64.so"
30-
ios.debug = "res://bin/libgdexample.ios.template_debug.xcframework"
31-
ios.release = "res://bin/libgdexample.ios.template_release.xcframework"
32-
web.debug.threads.wasm32 = "res://bin/libgdexample.web.template_debug.wasm32.wasm"
33-
web.release.threads.wasm32 = "res://bin/libgdexample.web.template_release.wasm32.wasm"
30+
ios.debug = "res://bin/libgdexample.ios.template_debug.framework"
31+
ios.release = "res://bin/libgdexample.ios.template_release.framework"
32+
web.debug.threads.wasm32 = "res://bin/gdexample.web.template_debug.wasm32.wasm"
33+
web.release.threads.wasm32 = "res://bin/gdexample.web.template_release.wasm32.wasm"
3434
web.debug.wasm32 = "res://bin/libgdexample.web.template_debug.wasm32.nothreads.wasm"
3535
web.release.wasm32 = "res://bin/libgdexample.web.template_release.wasm32.nothreads.wasm"
3636

3737
[dependencies]
3838
ios.debug = {
39-
"res://bin/libgodot-cpp.ios.template_debug.xcframework": ""
39+
"res://bin/libgodot-cpp.ios.template_debug.framework": ""
4040
}
4141
ios.release = {
42-
"res://bin/libgodot-cpp.ios.template_release.xcframework": ""
42+
"res://bin/libgodot-cpp.ios.template_release.framework": ""
4343
}

tools/apple_framework.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import os
2+
import pathlib
3+
4+
5+
def exists(env):
6+
return True
7+
8+
9+
def options(opts):
10+
pass
11+
12+
13+
def generate(
14+
target,
15+
*,
16+
env,
17+
source,
18+
min_macos_version="10.12",
19+
min_ios_version="12.0",
20+
plist_keys=None,
21+
):
22+
"""
23+
Generates an Apple .framework folder, containing the binary.
24+
:param target: Folder name of the framework, usually ending in `.framework`.
25+
:param env: The environment.
26+
:param source: A list of binary sources to generate.
27+
:param min_macos_version: The minimum macOS version supported by the framework, if the platform is macos.
28+
:param min_ios_version: The minimum iOS version supported by the framework, if the platform is iOS.
29+
:param plist_keys: Additional keys to send to the plist generator.
30+
:return: A list of files to be created, the first of which is the binary path.
31+
"""
32+
if env["platform"] == "macos":
33+
dt_platform_name = "macosx"
34+
min_os_part = f"LSMinimumSystemVersion={min_macos_version}"
35+
elif env["platform"] == "ios":
36+
dt_platform_name = "iphoneos"
37+
min_os_part = f"MinimumOSVersion={min_ios_version}"
38+
else:
39+
raise ValueError("Unsupported platform.")
40+
41+
framework_path = pathlib.Path(target)
42+
assert framework_path.suffix == ".framework"
43+
framework_name = framework_path.name.removesuffix(".framework")
44+
45+
parent_path = pathlib.Path(__file__).parent
46+
plist_creation_script_path = (parent_path / "create_apple_framework_plist.sh").relative_to(os.getcwd())
47+
plist_command = f"{plist_creation_script_path} $TARGET --set CFBundleExecutable={framework_name} --set DTPlatformName={dt_platform_name} --set {min_os_part}"
48+
if plist_keys:
49+
for key, value in plist_keys.items():
50+
plist_command += f' --set "{key}={value}"'
51+
52+
return [
53+
# Create the binary itself.
54+
env.Command(
55+
str(framework_path / framework_name),
56+
source,
57+
action="lipo -create $SOURCE -output $TARGET",
58+
),
59+
# Create the Info.plist
60+
env.Command(
61+
str(framework_path / "Resources" / "Info.plist"),
62+
[str(plist_creation_script_path)],
63+
action=plist_command,
64+
),
65+
]

tools/create_apple_framework_plist.sh

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/bin/bash
2+
3+
USAGE_STRING="Usage: $0 plist_path --set CFBundleExecutable=executable [--set key=value]..."
4+
5+
PLIST_PATH=""
6+
EXTRA_KEYS=()
7+
8+
# Parse the command line arguments.
9+
while [[ $# -gt 0 ]]; do
10+
case $1 in
11+
--set)
12+
IFS='=' read -r key value <<< "$2"
13+
# Replace if key exists, otherwise add new key-value.
14+
found=false
15+
for ((i=0; i<${#EXTRA_KEYS[@]}; i++)); do
16+
if [[ "${EXTRA_KEYS[i]}" =~ ^$key= ]]; then
17+
EXTRA_KEYS[i]="$key=$value"
18+
found=true
19+
break
20+
fi
21+
done
22+
if [ "$found" = false ]; then
23+
EXTRA_KEYS+=("$key=$value")
24+
fi
25+
shift 2
26+
;;
27+
*)
28+
# Assume positional argument is the plist path.
29+
if [ -n "$PLIST_PATH" ]; then
30+
# Cannot generate more than one plist; this was likely an error.
31+
echo "$USAGE_STRING"
32+
exit 1
33+
fi
34+
PLIST_PATH="$1"
35+
shift
36+
;;
37+
esac
38+
done
39+
40+
# Extract known keys from EXTRA_KEYS, for defaults and mandatory arguments.
41+
for ((i=0; i<${#EXTRA_KEYS[@]}; i++)); do
42+
IFS='=' read -r key value <<< "${EXTRA_KEYS[$i]}"
43+
case $key in
44+
CFBundleInfoDictionaryVersion)
45+
CFBundleInfoDictionaryVersion="$value"
46+
;;
47+
CFBundlePackageType)
48+
CFBundlePackageType="$value"
49+
;;
50+
CFBundleName)
51+
CFBundleName="$value"
52+
;;
53+
CFBundleExecutable)
54+
CFBundleExecutable="$value"
55+
;;
56+
CFBundleIdentifier)
57+
CFBundleIdentifier="$value"
58+
;;
59+
CFBundleVersion)
60+
CFBundleVersion="$value"
61+
;;
62+
CFBundleShortVersionString)
63+
CFBundleShortVersionString="$value"
64+
;;
65+
esac
66+
done
67+
68+
# Check for mandatory arguments.
69+
if [ -z "$PLIST_PATH" ] || [ -z "$CFBundleExecutable" ]; then
70+
echo "$USAGE_STRING"
71+
exit 1
72+
fi
73+
74+
# Add defaults for missing arguments.
75+
if [ -z "$CFBundleInfoDictionaryVersion" ]; then
76+
CFBundleInfoDictionaryVersion="6.0"
77+
EXTRA_KEYS+=("CFBundleInfoDictionaryVersion=$CFBundleInfoDictionaryVersion")
78+
fi
79+
if [ -z "$CFBundlePackageType" ]; then
80+
CFBundlePackageType="FMWK"
81+
EXTRA_KEYS+=("CFBundlePackageType=$CFBundlePackageType")
82+
fi
83+
if [ -z "$CFBundleName" ]; then
84+
CFBundleName="$CFBundleExecutable"
85+
EXTRA_KEYS+=("CFBundleName=$CFBundleName")
86+
fi
87+
if [ -z "$CFBundleIdentifier" ]; then
88+
CFBundleIdentifier="com.example.$CFBundleName"
89+
EXTRA_KEYS+=("CFBundleIdentifier=$CFBundleIdentifier")
90+
fi
91+
if [ -z "$CFBundleVersion" ]; then
92+
CFBundleVersion="1.0.0"
93+
EXTRA_KEYS+=("CFBundleVersion=$CFBundleVersion")
94+
fi
95+
if [ -z "$CFBundleShortVersionString" ]; then
96+
CFBundleShortVersionString="$CFBundleVersion"
97+
EXTRA_KEYS+=("CFBundleShortVersionString=$CFBundleShortVersionString")
98+
fi
99+
100+
# Ensure the directory exists.
101+
mkdir -p "$(dirname "$PLIST_PATH")"
102+
103+
# Create the Info.plist file.
104+
{
105+
echo '<?xml version="1.0" encoding="UTF-8"?>'
106+
echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
107+
echo '<plist version="1.0">'
108+
echo '<dict>'
109+
110+
for ((i=0; i<${#EXTRA_KEYS[@]}; i++)); do
111+
IFS='=' read -r key value <<< "${EXTRA_KEYS[$i]}"
112+
if [[ -n "$value" ]]; then
113+
echo " <key>$key</key>"
114+
echo " <string>$value</string>"
115+
fi
116+
done
117+
118+
echo '</dict>'
119+
echo '</plist>'
120+
} > "$PLIST_PATH"
121+
122+
# Confirm Info.plist was created.
123+
if [ -s "$PLIST_PATH" ]; then
124+
echo "$PLIST_PATH"
125+
else
126+
echo "Failed to create $PLIST_PATH."
127+
exit 1
128+
fi

0 commit comments

Comments
 (0)