Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/2443.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
iOS and macOS apps now read the minimum supported OS version from the Python framework's metadata, rather than requiring a ``VERSIONS`` file as part of the support package.
1 change: 1 addition & 0 deletions changes/2443.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
If an iOS or macOS app doesn't specify a minimum OS version, the minimum OS version is now derived from the support package, rather than being hard coded.
48 changes: 34 additions & 14 deletions src/briefcase/platforms/iOS/xcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,27 +322,47 @@ def _install_app_requirements(
app_packages_path: Path,
**kwargs,
):
# Determine the min iOS version from the VERSIONS file in the support package.
versions = dict(
[part.strip() for part in line.split(": ", 1)]
for line in (
(self.support_path(app) / "VERSIONS")
.read_text(encoding="UTF-8")
.split("\n")
try:
# Determine the min iOS version from the framework metadata
# of the ios-arm64 slice of the XCframework
plist_file = (
self.support_path(app)
/ "Python.xcframework/ios-arm64/Python.framework/Info.plist"
)
if ": " in line
)
support_min_version = Version(versions.get("Min iOS version", "13.0"))
with plist_file.open("rb") as f:
info_plist = plistlib.load(f)

support_min_version = info_plist["MinimumOSVersion"]
except KeyError:
raise BriefcaseCommandError(
"Your iOS XCframework doesn't specify a minimum iOS version."
)
except FileNotFoundError:
# If a plist file couldn't be found, it's an old-style support package;
# Determine the min iOS version from the VERSIONS file in the support package.
versions = dict(
[part.strip() for part in line.split(": ", 1)]
for line in (
(self.support_path(app) / "VERSIONS")
.read_text(encoding="UTF-8")
.split("\n")
)
if ": " in line
)
support_min_version = versions.get("Min iOS version", "13.0")

# Check that the app's definition is compatible with the support package.
# If the app doesn't specify a minimum version, use the support package
# minimum version as a default.
ios_min_version = getattr(app, "min_os_version", support_min_version)

# Check that the app's definition is compatible with the support package
ios_min_version = Version(getattr(app, "min_os_version", "13.0"))
if ios_min_version < support_min_version:
if Version(ios_min_version) < Version(support_min_version):
raise BriefcaseCommandError(
f"Your iOS app specifies a minimum iOS version of {ios_min_version}, "
f"but the support package only supports {support_min_version}"
)

ios_min_tag = str(ios_min_version).replace(".", "_")
ios_min_tag = ios_min_version.replace(".", "_")

# Feb 2025: The platform-site was moved into the xcframework as
# `platform-config`. Look for the new location; fall back to the old location.
Expand Down
43 changes: 30 additions & 13 deletions src/briefcase/platforms/macOS/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,27 +184,44 @@ def _install_app_requirements(
app_packages_path: Path,
**kwargs,
):
# Determine the min macOS version from the VERSIONS file in the support package.
versions = dict(
[part.strip() for part in line.split(": ", 1)]
for line in (
(self.support_path(app) / "VERSIONS")
.read_text(encoding="UTF-8")
.split("\n")
try:
# Determine the min macOS version from the framework metadata
# of the macos-arm64_x86_64 slice of the XCframework
plist_file = (
self.support_path(app)
/ "Python.xcframework/macos-arm64_x86_64"
/ "Python.framework/Resources/Info.plist"
)
if ": " in line
)
support_min_version = Version(versions.get("Min macOS version", "11.0"))
with plist_file.open("rb") as f:
info_plist = plistlib.load(f)

support_min_version = info_plist.get("MinimumOSVersion", "11.0")
except FileNotFoundError:
# If a plist file couldn't be found, it's an old-style support package;
# Determine the min macOS version from the VERSIONS file in the support package.
versions = dict(
[part.strip() for part in line.split(": ", 1)]
for line in (
(self.support_path(app) / "VERSIONS")
.read_text(encoding="UTF-8")
.split("\n")
)
if ": " in line
)
support_min_version = versions.get("Min macOS version", "11.0")

# Check that the app's definition is compatible with the support package
macOS_min_version = Version(getattr(app, "min_os_version", "11.0"))
if macOS_min_version < support_min_version:
# If the app doesn't specify a minimum version, use the support package
# minimum version as a default.
macOS_min_version = getattr(app, "min_os_version", support_min_version)

if Version(macOS_min_version) < Version(support_min_version):
raise BriefcaseCommandError(
f"Your macOS app specifies a minimum macOS version of {macOS_min_version}, "
f"but the support package only supports {support_min_version}"
)

macOS_min_tag = str(macOS_min_version).replace(".", "_")
macOS_min_tag = macOS_min_version.replace(".", "_")

if getattr(app, "universal_build", True):
# Perform the initial install targeting the current platform
Expand Down
30 changes: 12 additions & 18 deletions tests/platforms/iOS/xcode/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,24 @@ def first_app_generated(first_app_config, tmp_path):
},
)

# Create the support package VERSIONS file
# with a deliberately weird min iOS version
create_file(
tmp_path / "base_path/build/first-app/ios/xcode/Support/VERSIONS",
"\n".join(
[
"Python version: 3.10.15",
"Build: b11",
"Min iOS version: 12.0",
"---------------------",
"BZip2: 1.0.8-1",
"libFFI: 3.4.6-1",
"OpenSSL: 3.0.15-1",
"XZ: 5.6.2-1",
"",
]
),
)
# Create the package-config folders for each platform.
# We don't need anything in them; they just need to exist.
xcframework_path = (
tmp_path / "base_path/build/first-app/ios/xcode/Support/Python.xcframework"
)

# Create the XCframeworks's ios-arm64 Info.plist file
# with a deliberately weird min iOS version
create_plist_file(
xcframework_path / "ios-arm64/Python.framework/Info.plist",
{
"CFBundleSupportedPlatforms": "iPhoneOS",
"CFBundleVersion": "3.10.15",
"MinimumOSVersion": "12.0",
},
)
(xcframework_path / "ios-arm64/platform-config/arm64-iphoneos").mkdir(parents=True)

(
xcframework_path
/ "ios-arm64_x86_64-simulator/platform-config/arm64-iphonesimulator"
Expand Down
Loading
Loading