Skip to content
Open
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
41 changes: 33 additions & 8 deletions .github/workflows/update-binary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
fail-fast: false
matrix:
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
framework: [true, false]
outputs:
BUILD_NUMBER: ${{ steps.build-vars.outputs.BUILD_NUMBER }}

Expand All @@ -25,9 +26,11 @@ jobs:
run: |
export BUILD_NUMBER=$(basename $TAG_NAME)
export PYTHON_TAG=$(python -c "print('${{ matrix.python-version }}'.split('-')[0])")
export STUB_PREFIX=${{ !matrix.framework && 'L' || '' }}

echo "PYTHON_TAG=${PYTHON_TAG}" | tee -a $GITHUB_ENV
echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a $GITHUB_ENV
echo "STUB_PREFIX=${STUB_PREFIX}" | tee -a $GITHUB_ENV

# Expose BUILD_NUMBER as a step output so it can be consumed as a job output
echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a $GITHUB_OUTPUT
Expand All @@ -38,39 +41,61 @@ jobs:
persist-credentials: false

- name: Setup Python ${{ matrix.python-version }}
if: ${{ matrix.framework }}
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true

- name: Setup Miniconda
if: ${{ !matrix.framework }}
uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1
with:
python-version: ${{ matrix.python-version }}
auto-update-conda: true
activate-environment: briefcase-env

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
python -m pip install git+https://github.com/beeware/briefcase.git

- name: Generate Xcode App Template
- name: Create Framework stub app
if: ${{ matrix.framework }}
run: |
# Generate the stub app
cd stub
briefcase create macOS Xcode

- name: Create Non-framework stub app
if: ${{ !matrix.framework }}
run: |
# Generate the stub app
cd stub
briefcase create macOS Xcode -C env_manager=\"conda\" -C universal_build=false

- name: Build Stub App
run: |
cd stub
briefcase build macOS Xcode

echo "Build ${PYTHON_TAG}-${BUILD_NUMBER} console stub artefact"
echo "Build ${PYTHON_TAG}-${BUILD_NUMBER} ${{ !matrix.framework && 'non-framework' || 'framework' }} console stub artefact"
mv "./build/console-stub/macos/xcode/build/Release/Console Stub.app/Contents/MacOS/Console Stub" Stub
codesign --remove-signature Stub
zip Console-Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip Stub
zip Console-${STUB_PREFIX}Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip Stub

echo "Build ${PYTHON_TAG}-${BUILD_NUMBER} GUI stub artefact"
echo "Build ${PYTHON_TAG}-${BUILD_NUMBER} ${{ !matrix.framework && 'non-framework' || 'framework' }} GUI stub artefact"
mv "./build/gui-stub/macos/xcode/build/Release/GUI Stub.app/Contents/MacOS/GUI Stub" Stub
codesign --remove-signature Stub
zip GUI-Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip Stub
zip GUI-${STUB_PREFIX}Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip Stub

echo "Stub binaries:"
ls -1 *.zip

- name: Upload build artefacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ env.PYTHON_TAG }}-stubs
name: ${{ env.PYTHON_TAG }}-${{ env.STUB_PREFIX }}Stubs
path: stub/*.zip

- name: Upload Release Asset to S3
Expand All @@ -82,8 +107,8 @@ jobs:
python -m pip install -U setuptools
python -m pip install awscli

aws s3 cp stub/Console-Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip s3://briefcase-support/python/${PYTHON_TAG}/macOS/Console-Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip
aws s3 cp stub/GUI-Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip s3://briefcase-support/python/${PYTHON_TAG}/macOS/GUI-Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip
aws s3 cp stub/Console-${STUB_PREFIX}Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip s3://briefcase-support/python/${PYTHON_TAG}/macOS/Console-${STUB_PREFIX}Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip
aws s3 cp stub/GUI-${STUB_PREFIX}Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip s3://briefcase-support/python/${PYTHON_TAG}/macOS/GUI-${STUB_PREFIX}Stub-${PYTHON_TAG}-${BUILD_NUMBER}.zip

make-release:
name: Make Release
Expand Down
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ repos:
- id: check-case-conflict
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.25.2
hooks:
- id: zizmor
1 change: 1 addition & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"universal_build": true,
"host_arch": "arm64",
"min_os_version": "11.0",
"use_framework": true,
"_extensions": [
"briefcase.integrations.cookiecutter.PythonVersionExtension",
"briefcase.integrations.cookiecutter.PListExtension"
Expand Down
4 changes: 2 additions & 2 deletions stub/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ template = ".."

[tool.briefcase.app.gui-stub]
formal_name = "GUI Stub"
description = "A stub binary for GUI apps that can be integrated into the macOS app template"
description = "A stub binary for macOS GUI apps"
sources = ['src/gui_stub']

[tool.briefcase.app.console-stub]
formal_name = "Console Stub"
description = "A stub binary for console apps that can be integrated into the macOS app template"
description = "A stub binary for macOS console apps"
sources = ['src/console_stub']
console_app = true
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
{%- if cookiecutter.use_framework %}
#import <Python/Python.h>
{%- else %}
#import <Python.h>
{%- endif %}
#include <dlfcn.h>
#include <libgen.h>
#include <mach-o/dyld.h>
Expand Down Expand Up @@ -96,7 +100,11 @@ int main(int argc, char *argv[]) {

// Set the home for the Python interpreter
python_tag = @"{{ cookiecutter.python_version|py_tag }}";
{%- if cookiecutter.use_framework %}
python_home = [NSString stringWithFormat:@"%@/Python.framework/Versions/%@", frameworksPath, python_tag, nil];
{%- else %}
python_home = [NSString stringWithFormat:@"%@/python/", resourcePath, nil];
{%- endif %}
debug_log(@"PythonHome: %@", python_home);
wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL);
status = PyConfig_SetString(&config, &config.home, wtmp_str);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
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 */; };
{%- if cookiecutter.use_framework %}
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, ); }; };
{%- else %}
AA0000022AF0B40500C04AE0 /* libpython{{ cookiecutter.python_version|py_tag }}.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AA0000012AF0B14D00C04AE0 /* libpython{{ cookiecutter.python_version|py_tag }}.dylib */; };
{%- endif %}
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand All @@ -29,6 +33,7 @@
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
{%- if cookiecutter.use_framework %}
6060E7742AF0B40500C04AE0 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -40,6 +45,7 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
{%- endif %}
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
Expand All @@ -53,7 +59,11 @@
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; };
{%- if cookiecutter.use_framework %}
6060E76F2AF0B14D00C04AE0 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = "<group>"; };
{%- else %}
AA0000012AF0B14D00C04AE0 /* libpython{{ cookiecutter.python_version|py_tag }}.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpython{{ cookiecutter.python_version|py_tag }}.dylib; path = Python/lib/libpython{{ cookiecutter.python_version|py_tag }}.dylib; sourceTree = "<group>"; };
{%- endif %}
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -64,7 +74,11 @@
0D354FE62551C1E1009178D1 /* AppKit.framework in Frameworks */,
0D354FEF2551C249009178D1 /* Cocoa.framework in Frameworks */,
0D7B44A82555E01500CBC44B /* Foundation.framework in Frameworks */,
{%- if cookiecutter.use_framework %}
6060E7722AF0B40500C04AE0 /* Python.xcframework in Frameworks */,
{%- else %}
AA0000022AF0B40500C04AE0 /* libpython{{ cookiecutter.python_version|py_tag }}.dylib in Frameworks */,
{%- endif %}
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -115,7 +129,11 @@
60A04BBB28AF5E1000DAA9E5 /* Support */ = {
isa = PBXGroup;
children = (
{%- if cookiecutter.use_framework %}
6060E76F2AF0B14D00C04AE0 /* Python.xcframework */,
{%- else %}
AA0000012AF0B14D00C04AE0 /* libpython{{ cookiecutter.python_version|py_tag }}.dylib */,
{%- endif %}
);
path = Support;
sourceTree = "<group>";
Expand All @@ -131,7 +149,11 @@
0D354FC52551BFBA009178D1 /* Frameworks */,
0D354FC62551BFBA009178D1 /* Resources */,
0D7B44EB2556C8B800CBC44B /* Embed Foundation Extensions */,
{%- if cookiecutter.use_framework %}
6060E7742AF0B40500C04AE0 /* Embed Frameworks */,
{%- else %}
AA0000032AF0B40500C04AE0 /* Embed Python lib */,
{%- endif %}
60A04BC128AF640400DAA9E5 /* Sign Python Binary Modules */,
);
buildRules = (
Expand All @@ -150,7 +172,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1540;
LastUpgradeCheck = 2660;
ORGANIZATIONNAME = "{{ cookiecutter.author }}";
TargetAttributes = {
0D354FC72551BFBA009178D1 = {
Expand Down Expand Up @@ -190,6 +212,27 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
{%- if not cookiecutter.use_framework %}
AA0000032AF0B40500C04AE0 /* Embed Python lib */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Embed Python lib";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\nDEST=\"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/python\"\necho \"Embedding Python lib into $DEST\"\nmkdir -p \"$DEST\"\n# Copy the entire lib/ tree, preserving symlinks. Exclude static archives\n# and pkgconfig metadata that are not needed at runtime.\nrsync -a --delete --copy-unsafe-links \\\n --exclude='*.a' \\\n --exclude='pkgconfig' \\\n \"$PROJECT_DIR/Support/Python/lib/\" \"$DEST/lib/\"\n";
};
{%- endif %}
60A04BC128AF640400DAA9E5 /* Sign Python Binary Modules */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
Expand Down Expand Up @@ -286,6 +329,7 @@
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "{{ cookiecutter.formal_name }}";
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
};
name = Debug;
};
Expand Down Expand Up @@ -345,6 +389,7 @@
MTL_FAST_MATH = YES;
PRODUCT_NAME = "{{ cookiecutter.formal_name }}";
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
};
name = Release;
};
Expand All @@ -363,17 +408,37 @@
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
{%- if cookiecutter.use_framework %}
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/Support\"",
);
{%- else %}
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
{%- endif %}
GCC_C_LANGUAGE_STANDARD = gnu99;
{%- if cookiecutter.use_framework %}
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
{%- else %}
HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/Support/Python/include/python{{ cookiecutter.python_version|py_tag }}\"";
{%- endif %}
INFOPLIST_FILE = "{{ cookiecutter.class_name }}/Info.plist";
{%- if cookiecutter.use_framework %}
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
{%- else %}
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../Resources/python/lib",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/Support/Python/lib\"",
);
{%- endif %}
MACOSX_DEPLOYMENT_TARGET = {{ cookiecutter.min_os_version }};
PRODUCT_BUNDLE_IDENTIFIER = "{{ cookiecutter.bundle }}.{{ cookiecutter.app_name }}";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -396,17 +461,37 @@
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_TESTABILITY = YES;
{%- if cookiecutter.use_framework %}
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/Support\"",
);
{%- else %}
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
{%- endif %}
GCC_C_LANGUAGE_STANDARD = gnu99;
{%- if cookiecutter.use_framework %}
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
{%- else %}
HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/Support/Python/include/python{{ cookiecutter.python_version|py_tag }}\"";
{%- endif %}
INFOPLIST_FILE = "{{ cookiecutter.class_name }}/Info.plist";
{%- if cookiecutter.use_framework %}
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
{%- else %}
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../Resources/python/lib",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/Support/Python/lib\"",
);
{%- endif %}
MACOSX_DEPLOYMENT_TARGET = {{ cookiecutter.min_os_version }};
PRODUCT_BUNDLE_IDENTIFIER = "{{ cookiecutter.bundle }}.{{ cookiecutter.app_name }}";
};
Expand Down