Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to a dynamic framework. #33

Merged
merged 9 commits into from
Aug 11, 2024
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,14 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13-dev" ]
framework: [ "toga", "pyside6", "pygame", "console" ]

exclude:
# PySide6 hasn't published 3.13 wheels.
- python-version: "3.13-dev"
framework: "pyside6"

# Pygame hasn't published 3.13 wheels.
- python-version: "3.13-dev"
framework: "pygame"
5 changes: 4 additions & 1 deletion .github/workflows/update-binary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ jobs:
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
python -m pip install git+https://github.com/beeware/briefcase.git
# TODO - Revert to the development version of Briefcase
# Use the development version of Briefcase
# python -m pip install git+https://github.com/beeware/briefcase.git
python -m pip install git+https://github.com/freakboy3742/briefcase.git@version-bumps

- name: Generate Xcode App Template
run: |
Expand Down
17 changes: 11 additions & 6 deletions {{ cookiecutter.format }}/briefcase.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Generated using Python {{ cookiecutter.python_version }}
[briefcase]
# This is the start of the framework-based support package era.
target_version = "0.3.20"

[paths]
app_path = "{{ cookiecutter.class_name }}/app"
app_packages_path = "{{ cookiecutter.class_name }}/app_packages"
Expand All @@ -7,13 +11,14 @@ entitlements_path = "{{ cookiecutter.class_name }}/{{ cookiecutter.app_name }}.e

support_path = "Support"
{{ {
"3.8": "support_revision = 14",
"3.9": "support_revision = 12",
"3.10": "support_revision = 8",
"3.11": "support_revision = 3",
"3.12": "support_revision = 2",
"3.9": "support_revision = 13",
"3.10": "support_revision = 9",
"3.11": "support_revision = 4",
"3.12": "support_revision = 3",
"3.13": "support_revision = 0",
}.get(cookiecutter.python_version|py_tag, "") }}

cleanup_paths = [
]
icon.16 = "{{ cookiecutter.class_name }}/Assets.xcassets/{{ cookiecutter.formal_name }}.appiconset/icon-16.png"
icon.32 = "{{ cookiecutter.class_name }}/Assets.xcassets/{{ cookiecutter.formal_name }}.appiconset/icon-32.png"
icon.64 = "{{ cookiecutter.class_name }}/Assets.xcassets/{{ cookiecutter.formal_name }}.appiconset/icon-64.png"
Expand Down
33 changes: 17 additions & 16 deletions {{ cookiecutter.format }}/{{ cookiecutter.class_name }}/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
#include <Python.h>
#import <Python/Python.h>
#include <dlfcn.h>
#include <libgen.h>
#include <mach-o/dyld.h>
Expand All @@ -32,6 +32,8 @@ int main(int argc, char *argv[]) {
PyConfig config;
NSBundle *mainBundle;
NSString *resourcePath;
NSString *frameworksPath;
NSString *python_tag;
NSString *python_home;
NSString *app_module_name;
NSString *path;
Expand All @@ -55,6 +57,7 @@ int main(int argc, char *argv[]) {
// Set the resource path for the app
mainBundle = get_main_bundle();
resourcePath = [mainBundle resourcePath];
frameworksPath = [mainBundle privateFrameworksPath];

// Generate an isolated Python configuration.
debug_log(@"Configuring isolated Python...");
Expand All @@ -72,6 +75,8 @@ int main(int argc, char *argv[]) {
config.write_bytecode = 0;
// Isolated apps need to set the full PYTHONPATH manually.
config.module_search_paths_set = 1;
// Enable verbose logging for debug purposes
// config.verbose = 1;

debug_log(@"Pre-initializing Python runtime...");
status = Py_PreInitialize(&preconfig);
Expand All @@ -82,7 +87,8 @@ int main(int argc, char *argv[]) {
}

// Set the home for the Python interpreter
python_home = [NSString stringWithFormat:@"%@/support/python-stdlib", resourcePath, nil];
python_tag = @"{{ cookiecutter.python_version|py_tag }}";
python_home = [NSString stringWithFormat:@"%@/Python.framework/Versions/%@", frameworksPath, python_tag, nil];
debug_log(@"PythonHome: %@", python_home);
wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL);
status = PyConfig_SetString(&config, &config.home, wtmp_str);
Expand Down Expand Up @@ -125,20 +131,8 @@ int main(int argc, char *argv[]) {
// Set the full module path. This includes the stdlib, site-packages, and app code.
debug_log(@"PYTHONPATH:");

// The .zip form of the stdlib
path = [NSString stringWithFormat:@"%@/support/python{{ cookiecutter.python_version|py_libtag }}.zip", resourcePath, nil];
debug_log(@"- %@", path);
wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
if (PyStatus_Exception(status)) {
crash_dialog([NSString stringWithFormat:@"Unable to set .zip form of stdlib path: %s", status.err_msg, nil]);
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
PyMem_RawFree(wtmp_str);

// The unpacked form of the stdlib
path = [NSString stringWithFormat:@"%@/support/python-stdlib", resourcePath, nil];
path = [NSString stringWithFormat:@"%@/lib/python%@", python_home, python_tag, nil];
debug_log(@"- %@", path);
wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
Expand All @@ -150,7 +144,7 @@ int main(int argc, char *argv[]) {
PyMem_RawFree(wtmp_str);

// Add the stdlib binary modules path
path = [NSString stringWithFormat:@"%@/support/python-stdlib/lib-dynload", resourcePath, nil];
path = [NSString stringWithFormat:@"%@/lib/python%@/lib-dynload", python_home, python_tag, nil];
debug_log(@"- %@", path);
wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
Expand Down Expand Up @@ -401,6 +395,13 @@ void setup_stdout(NSBundle *mainBundle) {
int ret = 0;
const char *nslog_script;

// If the app is running under Xcode 15 or later, we don't need to do anything,
// as stdout and stderr are automatically captured by the in-IDE console.
// See https://developer.apple.com/forums/thread/705868 for details.
if (getenv("IDE_DISABLED_OS_ACTIVITY_DT_MODE")) {
return;
}

// Install the nslog script to redirect stdout/stderr if available.
// Set the name of the python NSLog bootstrap script
nslog_script = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,30 @@
0D354FEF2551C249009178D1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D354FEE2551C249009178D1 /* Cocoa.framework */; };
0D7B44A82555E01500CBC44B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D7B44A72555E01500CBC44B /* Foundation.framework */; };
0D7B44DA2556C84100CBC44B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D354FD72551BFBD009178D1 /* main.m */; };
60A04BBF28AF5E7400DAA9E5 /* python-stdlib in Copy Python standard library */ = {isa = PBXBuildFile; fileRef = 60A04BBE28AF5E7400DAA9E5 /* python-stdlib */; };
60A04BC028AF5EC000DAA9E5 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60A04BBC28AF5E6900DAA9E5 /* Python.xcframework */; };
6060E7722AF0B40500C04AE0 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6060E76F2AF0B14D00C04AE0 /* Python.xcframework */; };
6060E7732AF0B40500C04AE0 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6060E76F2AF0B14D00C04AE0 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
0D7B44EB2556C8B800CBC44B /* Embed App Extensions */ = {
0D7B44EB2556C8B800CBC44B /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
);
name = "Embed App Extensions";
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
609384A728C873E2005B2A5D /* Copy Python standard library */ = {
6060E7742AF0B40500C04AE0 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = support;
dstSubfolderSpec = 7;
dstPath = "";
dstSubfolderSpec = 10;
files = (
60A04BBF28AF5E7400DAA9E5 /* python-stdlib in Copy Python standard library */,
6060E7732AF0B40500C04AE0 /* Python.xcframework in Embed Frameworks */,
);
name = "Copy Python standard library";
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
Expand All @@ -53,8 +53,7 @@
0D354FE52551C1E1009178D1 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
0D354FEE2551C249009178D1 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
0D7B44A72555E01500CBC44B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
60A04BBC28AF5E6900DAA9E5 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = "<group>"; };
60A04BBE28AF5E7400DAA9E5 /* python-stdlib */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "python-stdlib"; sourceTree = "<group>"; };
6060E76F2AF0B14D00C04AE0 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -64,8 +63,8 @@
files = (
0D354FE62551C1E1009178D1 /* AppKit.framework in Frameworks */,
0D354FEF2551C249009178D1 /* Cocoa.framework in Frameworks */,
60A04BC028AF5EC000DAA9E5 /* Python.xcframework in Frameworks */,
0D7B44A82555E01500CBC44B /* Foundation.framework in Frameworks */,
6060E7722AF0B40500C04AE0 /* Python.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -116,8 +115,7 @@
60A04BBB28AF5E1000DAA9E5 /* Support */ = {
isa = PBXGroup;
children = (
60A04BBE28AF5E7400DAA9E5 /* python-stdlib */,
60A04BBC28AF5E6900DAA9E5 /* Python.xcframework */,
6060E76F2AF0B14D00C04AE0 /* Python.xcframework */,
);
path = Support;
sourceTree = "<group>";
Expand All @@ -132,8 +130,8 @@
0D354FC42551BFBA009178D1 /* Sources */,
0D354FC52551BFBA009178D1 /* Frameworks */,
0D354FC62551BFBA009178D1 /* Resources */,
609384A728C873E2005B2A5D /* Copy Python standard library */,
0D7B44EB2556C8B800CBC44B /* Embed App Extensions */,
0D7B44EB2556C8B800CBC44B /* Embed Foundation Extensions */,
6060E7742AF0B40500C04AE0 /* Embed Frameworks */,
60A04BC128AF640400DAA9E5 /* Sign Python Binary Modules */,
);
buildRules = (
Expand All @@ -152,7 +150,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1330;
LastUpgradeCheck = 1540;
ORGANIZATIONNAME = "{{ cookiecutter.author }}";
TargetAttributes = {
0D354FC72551BFBA009178D1 = {
Expand Down Expand Up @@ -209,7 +207,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\necho \"Signed as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)\"\nfind \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/support/python-stdlib/lib-dynload\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\; \nfind \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/app_packages\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\; \nfind \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/app\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\; \n";
shellScript = "set -e\necho \"Signed as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)\"\nfind \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/app_packages\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\; \nfind \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/app\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\; \n";
};
/* End PBXShellScriptBuildPhase section */

Expand Down Expand Up @@ -259,9 +257,11 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)\"",
Expand Down Expand Up @@ -323,9 +323,11 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)\"",
Expand Down Expand Up @@ -354,12 +356,19 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CODE_SIGN_ENTITLEMENTS = "{{ cookiecutter.class_name }}/{{ cookiecutter.app_name }}.entitlements";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/Support\"",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
INFOPLIST_FILE = "{{ cookiecutter.class_name }}/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -368,7 +377,6 @@
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = "{{ cookiecutter.bundle }}.{{ cookiecutter.app_name }}";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO;
};
name = Debug;
};
Expand All @@ -380,21 +388,27 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CODE_SIGN_ENTITLEMENTS = "{{ cookiecutter.class_name }}/{{ cookiecutter.app_name }}.entitlements";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/Support\"",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
INFOPLIST_FILE = "{{ cookiecutter.class_name }}/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = "{{ cookiecutter.bundle }}.{{ cookiecutter.app_name }}";
STRIP_INSTALLED_PRODUCT = NO;
};
name = Release;
};
Expand Down