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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ logs/
.specify/templates
.specify/extensions.yml
.specify/extensions
.specify/feature.json
.specify/init-options.json
.specify/integration.json
.specify/integrations
.specify/workflows
.specify/workflow.yml
specs
2 changes: 1 addition & 1 deletion automation/src/automation/bootstraps/pyside6.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ def pyproject_table_macOS(self):
min_os_version = "13.0"
requires = [
"PySide6-Addons~=6.8",
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
"""
1 change: 1 addition & 0 deletions changes/2877.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Briefcase is now able to display application logs when running apps under macOS 26. If you are experiencing this problem, you need to either (a) upgrade `std-nslog` to 2.0.0+, or target your application at Python 3.14 (or newer).
2 changes: 1 addition & 1 deletion src/briefcase/bootstraps/pygame.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def pyproject_table_macOS(self):
return """\
universal_build = true
requires = [
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
"""

Expand Down
2 changes: 1 addition & 1 deletion src/briefcase/bootstraps/pyside6.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def pyproject_table_macOS(self):
# Pyside 6.10 (required for Python 3.14 support) enforces a macOS 13 minimum.
min_os_version = "13.0"
requires = [
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
"""

Expand Down
3 changes: 1 addition & 2 deletions src/briefcase/bootstraps/toga.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def pyproject_table_macOS(self):
universal_build = true
requires = [
"toga-cocoa~=0.5.0",
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
"""

Expand Down Expand Up @@ -206,7 +206,6 @@ def pyproject_table_iOS(self):
return """\
requires = [
"toga-iOS~=0.5.0",
"std-nslog~=1.0.3",
]
"""

Expand Down
5 changes: 5 additions & 0 deletions src/briefcase/integrations/cookiecutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def py_libtag(obj):
"""A Python version library tag (311)"""
return "".join(obj.split(".")[:2])

def minor_version(obj):
"""The Python minor version, as an integer (e.g., 11)"""
return int(obj.split(".")[1])

def nuget_version(obj):
"""A Python version in Nuget format (3.14.0-rc1)."""
parts = obj.split(".")[:3]
Expand All @@ -32,6 +36,7 @@ def nuget_version(obj):

environment.filters["py_tag"] = py_tag
environment.filters["py_libtag"] = py_libtag
environment.filters["minor_version"] = minor_version
environment.filters["nuget_version"] = nuget_version


Expand Down
30 changes: 23 additions & 7 deletions src/briefcase/platforms/iOS/xcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,13 @@ def run_app(
"Uninstalling any existing app version..."
) as keep_alive,
self.tools.subprocess.Popen(
["xcrun", "simctl", "uninstall", udid, app.bundle_identifier]
[
"xcrun",
"simctl",
"uninstall",
udid,
app.bundle_identifier,
]
) as uninstall_popen,
):
while (ret_code := uninstall_popen.poll()) is None:
Expand All @@ -641,7 +647,13 @@ def run_app(
with (
self.console.wait_bar(f"Installing new {label} version...") as keep_alive,
self.tools.subprocess.Popen(
["xcrun", "simctl", "install", udid, self.binary_path(app)]
[
"xcrun",
"simctl",
"install",
udid,
self.binary_path(app),
]
) as install_popen,
):
while (ret_code := install_popen.poll()) is None:
Expand All @@ -661,7 +673,8 @@ def run_app(
# and for native NSLog() calls in the bootstrap binary
# Case (2) works when the standard library is dynamically linked,
# and ctypes (which handles the NSLog integration) is an
# extension module.
# extension module. It also catches the case for the CPython
# builtin behavior of redirecting to the system log.
# It's not enough to filter on *just* the processImagePath,
# as the process will generate lots of system-level messages.
# We can't filter on *just* the senderImagePath, because other
Expand All @@ -679,10 +692,13 @@ def run_app(
"--predicate",
(
f'senderImagePath ENDSWITH "/{app.formal_name}"'
f' OR (processImagePath ENDSWITH "/{app.formal_name}"'
' AND (senderImagePath ENDSWITH "-iphonesimulator.so"'
' OR senderImagePath ENDSWITH "-iphonesimulator.dylib"'
' OR senderImagePath ENDSWITH "_ctypes.framework/_ctypes"))'
f'OR (processImagePath ENDSWITH "/{app.formal_name}"'
' AND (senderImagePath ENDSWITH "-iphonesimulator.so"'
' OR senderImagePath ENDSWITH "-iphonesimulator.dylib"'
' OR senderImagePath ENDSWITH "_ctypes.framework/_ctypes"'
' OR senderImagePath ENDSWITH "/Python"'
" )"
")"
),
],
stdout=subprocess.PIPE,
Expand Down
5 changes: 4 additions & 1 deletion src/briefcase/platforms/macOS/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,10 @@ def run_gui_app(
(
f'senderImagePath=="{sender}"'
f' OR (processImagePath=="{sender}"'
' AND senderImagePath=="/usr/lib/libffi.dylib")'
' AND (senderImagePath=="/usr/lib/libffi.dylib" '
' OR senderImagePath ENDSWITH "/Python" '
' OR senderImagePath ENDSWITH ".abi3.so")'
" )"
),
],
stdout=subprocess.PIPE,
Expand Down
6 changes: 4 additions & 2 deletions src/briefcase/platforms/macOS/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

MACOS_LOG_PREFIX_REGEX = re.compile(
r"\d{4}-\d{2}-\d{2} (?P<timestamp>\d{2}:\d{2}:\d{2}.\d{3}) Df (.*?)\[.*?:.*?\]"
r"(?P<subsystem>( \(libffi\.dylib\))|("
r" \(_ctypes(\.cpython-3\d{1,2}-.*?\.(so|dylib))?\)))? (?P<content>.*)"
r"(?P<subsystem>( \(libffi\.dylib\))|( \(Python\))"
r"|( \(_ctypes(\.cpython-3\d{1,2}-.*?\.(so|dylib))?\))"
r"|( \(_oslog_shim\..*?\.(so|dylib)?\))"
r")? (?P<content>.*)"
)


Expand Down
7 changes: 3 additions & 4 deletions tests/commands/new/test_build_gui_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def main():
universal_build = true
requires = [
"toga-cocoa~=0.5.0",
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
""",
"pyproject_table_linux": """\
Expand Down Expand Up @@ -203,7 +203,6 @@ def main():
"pyproject_table_iOS": """\
requires = [
"toga-iOS~=0.5.0",
"std-nslog~=1.0.3",
]
""",
"pyproject_table_android": '''\
Expand Down Expand Up @@ -414,7 +413,7 @@ def main():
# Pyside 6.10 (required for Python 3.14 support) enforces a macOS 13 minimum.
min_os_version = "13.0"
requires = [
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
""",
"pyproject_table_linux": """\
Expand Down Expand Up @@ -567,7 +566,7 @@ def main():
"pyproject_table_macOS": """\
universal_build = true
requires = [
"std-nslog~=1.0.3",
"std-nslog~=2.0.0",
]
""",
"pyproject_table_linux": """\
Expand Down
24 changes: 24 additions & 0 deletions tests/integrations/cookiecutter/test_PythonVersionExtension.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ def test_py_libtag(value, expected):
assert env.filters["py_libtag"](value) == expected


@pytest.mark.parametrize(
("value", "expected"),
[
# Single digit minor
("3.8.4.dev5", 8),
("3.8.4a1", 8),
("3.8.4b2", 8),
("3.8.4rc3", 8),
("3.8.4.post6", 8),
# Two digit minor
("3.11.4.dev5", 11),
("3.11.4a1", 11),
("3.11.4b2", 11),
("3.11.4rc3", 11),
("3.11.4.post6", 11),
],
)
def test_minor_version(value, expected):
env = MagicMock()
env.filters = {}
PythonVersionExtension(env)
assert env.filters["minor_version"](value) == expected


@pytest.mark.parametrize(
("value", "expected"),
[
Expand Down
Loading
Loading