Skip to content
Draft
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/2430.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create Kivy Bootstrap
4 changes: 4 additions & 0 deletions src/briefcase/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,10 @@ def _pip_install(
encoding="UTF-8",
**pip_kwargs,
)

# move kivy share deps to briefcase default sys.prefix
if self.platform.lower() == "window" and "kivy" in pip_args:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If something like this is needed, it needs to either be a generic feature, or refactored into being feature of the backend, rather than being part of the generic create command.

However, I'd be a lot more interested in understanding why this is required at all. Why does the install work for Kivy "by default", but not for Briefcase?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@freakboy3742 Kivy works by default because, when installed without the --target flag, it places its core dependencies in the share directory (on Windows only). If you check the metadata for Kivy’s core dependencies, you’ll see binaries like ../../share/glew/bin/glew32.dll,sha256=mkzO8kYHqSwXhGhQjbKXKCnvjfO94EsWyv5ZEr9TZTM,464896 are located there. In Kivy, the following code:

_root = sys.prefix
dep_bins = [join(_root, 'share', '{}', 'bin')]

looks for these dependencies in the .venv directory, not in .venv/Lib/site-packages. Here, .venv corresponds to sys.prefix. However, in Briefcase, sys.prefix points to <app-name>/build/<app-name>/windows/app/src. When the --target flag is used, the share folder ends up in app_package instead of src (which is Briefcase’s default sys.prefix), causing the difference in behavior.

Please can you explain more about the backend. I'm a little bit confused and I'm new to briefcase codebase structure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@freakboy3742 Kivy works by default because, when installed without the --target flag, it places its core dependencies in the share directory (on Windows only). If you check the metadata for Kivy’s core dependencies, you’ll see binaries like ../../share/glew/bin/glew32.dll,sha256=mkzO8kYHqSwXhGhQjbKXKCnvjfO94EsWyv5ZEr9TZTM,464896 are located there. In Kivy, the following code:

_root = sys.prefix
dep_bins = [join(_root, 'share', '{}', 'bin')]

I'd argue this is a bug in Kivy, and how it's packaging its dependencies. There's no guarantee that ../../share exists, is writable, or that ../../share is guaranteed to be equivalent to {sys.prefix}/share. It will be if you're installing into a full Python install, or as part of a virtual environment - but that's not a hard guarantee that Python makes. Kivy is missing an edge case of PEP 376 interpretation.

PEP 376 defines the entries in RECORD as:

the file’s path:

  • a ‘/’-separated path, relative to the base location, if the file is under the base location.
  • a ‘/’-separated path, relative to the base location, if the file is under the installation prefix AND if the base location is a subpath of the installation prefix.
  • an absolute path, using the local platform separator

The base location is defined as "the path defined by the --install-lib option, which defaults to the site-packages directory."; the installation prefix is defined as "path defined by the --prefix option, which defaults to sys.prefix."

Kivy is leaning on the second definition. In a standard Windows virtual environment, the installation prefix (sys.prefix) is the venv directory, and the base location is Lib/site-packages relative to that path, so it works, and it's legal.

However, there's no requirement that the base location is a subpath of the installation prefix - and when you use --target to install, it won't be.

Unfortunately, the PEP doesn't specify the fallback behavior in this case; evidently pip is stripping the relative path portions if the base location isn't a subpath of the installation prefix.

That said - there are evidently other packages that make a similar assumption - pywin32, for example. So - it would appear Briefcase needs to make a change here.

One possible workaround would be to restructure the Windows app template so that Briefcase uses Lib/app_packages rather than a bare app_packages folder. That would ensure that ../../share would resolve to a consistent location - but would also require us to redirect sys.prefix at time of installation to ensure that pip believes the base location is a subpath of the installation prefix.

Please can you explain more about the backend. I'm a little bit confused and I'm new to briefcase codebase structure.

The point I'm trying to make is that we're not going to add code to Briefcase that makes a special case of one specific package on one specific platform. Either we add a feature that can be used by all packages, based on a clearly documented standard (or, at least, commonly understood convention in the Python ecosystem), or the feature that is added needs to be isolated into a context where it only applies where it is needed (in this case, windows, when building a Kivy app).

shutil.move(app_packages_path / "share", app_packages_path.parent)
except subprocess.CalledProcessError as e:
raise RequirementsInstallError(install_hint=install_hint) from e

Expand Down
Loading