Skip to content

Commit 1696b8a

Browse files
authored
Merge pull request #1625 from JonasT/run_setup_py
[WIP] Run project's setup.py if present, unless --ignore-setup-py was set
2 parents 427f2d8 + 6034168 commit 1696b8a

File tree

13 files changed

+1538
-74
lines changed

13 files changed

+1538
-74
lines changed

.travis.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
sudo: required
22

3+
dist: xenial # needed for more recent python 3 and python3-venv
4+
35
language: generic
46

57
services:
68
- docker
79

810
before_install:
911
- travis_retry sudo apt update -qq
10-
- travis_retry sudo apt install -qq --no-install-recommends python2.7 python3
12+
- travis_retry sudo apt install -qq --no-install-recommends python2.7 python3 python3-venv python3-virtualenv
13+
# (venv/virtualenv are both used by tests/test_pythonpackage.py)
1114
- sudo pip install tox>=2.0
1215
# https://github.com/travis-ci/travis-ci/issues/6069#issuecomment-266546552
1316
- git remote set-branches --add origin master
@@ -28,7 +31,8 @@ env:
2831

2932
before_script:
3033
# we want to fail fast on tox errors without having to `docker build` first
31-
- tox
34+
- tox -- tests/ --ignore tests/test_pythonpackage.py
35+
# (we ignore test_pythonpackage.py since these run way too long!! test_pythonpackage_basic.py will still be run.)
3236

3337
script:
3438
- docker build --tag=p4a --file Dockerfile.py3 .

Dockerfile.py3

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ ENV WORK_DIR="${HOME_DIR}" \
9595

9696
# install system dependencies
9797
RUN ${RETRY} apt -y install -qq --no-install-recommends \
98-
python3 virtualenv python3-pip wget lbzip2 patch sudo \
98+
python3 virtualenv python3-pip python3-venv \
99+
wget lbzip2 patch sudo \
99100
&& apt -y autoremove
100101

101102
# build dependencies

doc/source/distutils.rst

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,71 @@
22
distutils/setuptools integration
33
================================
44

5-
Instead of running p4a via the command line, you can integrate with
6-
distutils and setup.py.
5+
Have `p4a apk` run setup.py (replaces ``--requirements``)
6+
---------------------------------------------------------
7+
8+
If your project has a `setup.py` file, then it can be executed by
9+
`p4a` when your app is packaged such that your app properly ends up
10+
in the packaged site-packages. (Use ``--use-setup-py`` to enable this,
11+
``--ignore-setup-py`` to prevent it)
12+
13+
This is functionality to run **setup.py INSIDE `p4a apk`,** as opposed
14+
to the other section below, which is about running
15+
*p4a inside setup.py*.
16+
17+
This however has these caveats:
18+
19+
- **Only your ``main.py`` from your app's ``--private`` data is copied
20+
into the .apk!** Everything else needs to be installed by your
21+
``setup.py`` into the site-packages, or it won't be packaged.
22+
23+
- All dependencies that map to recipes can only be pinned to exact
24+
versions, all other constraints will either just plain not work
25+
or even cause build errors. (Sorry, our internal processing is
26+
just not smart enough to honor them properly at this point)
27+
28+
- If you don't use Python 3 per default, you still need to specify
29+
``--requirements python2`` (without any additional dependencies)
30+
31+
- The dependency analysis at the start may be quite slow and delay
32+
your build
33+
34+
Reasons why you would want to use a `setup.py` to be processed (and
35+
omit specifying ``--requirements``):
36+
37+
- You want to use a more standard mechanism to specify dependencies
38+
instead of ``--requirements``
39+
40+
- You already use a `setup.py` for other platforms
41+
42+
- Your application imports itself
43+
in a way that won't work unless installed to site-packages)
44+
45+
46+
Reasons **not** to use a `setup.py` (that is to use the usual
47+
``--requirements`` mechanism instead):
48+
49+
- You don't use a `setup.py` yet, and prefer the simplicity of
50+
just specifying ``--requirements``
51+
52+
- Your `setup.py` assumes a desktop platform and pulls in
53+
Android-incompatible dependencies, and you are not willing
54+
to change this, or you want to keep it separate from Android
55+
deployment for other organizational reasons
56+
57+
- You need data files to be around that aren't installed by
58+
your `setup.py` into the site-packages folder
59+
60+
61+
Use your setup.py to call p4a
62+
-----------------------------
63+
64+
Instead of running p4a via the command line, you can call it via
65+
`setup.py` instead, by it integrating with distutils and setup.py.
66+
67+
This is functionality to run **p4a INSIDE setup.py,** as opposed
68+
to the other section above, which is about running
69+
*setup.py inside `p4a apk`*.
770

871
The base command is::
972

@@ -44,7 +107,7 @@ All of these automatic arguments can be overridden by passing them manually on t
44107
python setup.py apk --name="Testapp Setup" --version=2.5
45108

46109
Adding p4a arguments in setup.py
47-
--------------------------------
110+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48111

49112
Instead of providing extra arguments on the command line, you can
50113
store them in setup.py by passing the ``options`` parameter to
@@ -79,7 +142,7 @@ setup.py apk``. Any options passed on the command line will override
79142
these values.
80143

81144
Adding p4a arguments in setup.cfg
82-
---------------------------------
145+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83146

84147
You can also provide p4a arguments in the setup.cfg file, as normal
85148
for distutils. The syntax is::

doc/source/quickstart.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,14 @@ your own Kivy branch you might set::
298298
The specified directory will be copied into python-for-android instead
299299
of downloading from the normal url specified in the recipe.
300300

301+
setup.py file (experimental)
302+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
303+
304+
If your application is also packaged for desktop using `setup.py`,
305+
you may want to use your `setup.py` instead of the
306+
``--requirements`` option to avoid specifying things twice.
307+
For that purpose, check out :doc:`distutils`
308+
301309
Going further
302310
~~~~~~~~~~~~~
303311

pythonforandroid/bootstraps/common/build/build.py

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
import jinja2
2424

2525

26-
def get_dist_info_for(key):
26+
def get_dist_info_for(key, error_if_missing=True):
2727
try:
2828
with open(join(dirname(__file__), 'dist_info.json'), 'r') as fileh:
2929
info = json.load(fileh)
30-
value = str(info[key])
30+
value = info[key]
3131
except (OSError, KeyError) as e:
32+
if not error_if_missing:
33+
return None
3234
print("BUILD FAILURE: Couldn't extract the key `" + key + "` " +
3335
"from dist_info.json: " + str(e))
3436
sys.exit(1)
@@ -304,18 +306,45 @@ def make_package(args):
304306
f.write("P4A_MINSDK=" + str(args.min_sdk_version) + "\n")
305307

306308
# Package up the private data (public not supported).
309+
use_setup_py = get_dist_info_for("use_setup_py",
310+
error_if_missing=False) is True
307311
tar_dirs = [env_vars_tarpath]
308-
if args.private:
309-
tar_dirs.append(args.private)
310-
for python_bundle_dir in ('private', 'crystax_python', '_python_bundle'):
311-
if exists(python_bundle_dir):
312-
tar_dirs.append(python_bundle_dir)
313-
if get_bootstrap_name() == "webview":
314-
tar_dirs.append('webview_includes')
315-
if args.private or args.launcher:
316-
make_tar(
317-
join(assets_dir, 'private.mp3'), tar_dirs, args.ignore_path,
318-
optimize_python=args.optimize_python)
312+
_temp_dirs_to_clean = []
313+
try:
314+
if args.private:
315+
if not use_setup_py or (
316+
not exists(join(args.private, "setup.py")) and
317+
not exists(join(args.private, "pyproject.toml"))
318+
):
319+
print('No setup.py/pyproject.toml used, copying '
320+
'full private data into .apk.')
321+
tar_dirs.append(args.private)
322+
else:
323+
print('Copying main.py ONLY, since other app data is '
324+
'expected in site-packages.')
325+
main_py_only_dir = tempfile.mkdtemp()
326+
_temp_dirs_to_clean.append(main_py_only_dir)
327+
if exists(join(args.private, "main.pyo")):
328+
shutil.copyfile(join(args.private, "main.pyo"),
329+
join(main_py_only_dir, "main.pyo"))
330+
elif exists(join(args.private, "main.py")):
331+
shutil.copyfile(join(args.private, "main.py"),
332+
join(main_py_only_dir, "main.py"))
333+
tar_dirs.append(main_py_only_dir)
334+
for python_bundle_dir in ('private',
335+
'crystax_python',
336+
'_python_bundle'):
337+
if exists(python_bundle_dir):
338+
tar_dirs.append(python_bundle_dir)
339+
if get_bootstrap_name() == "webview":
340+
tar_dirs.append('webview_includes')
341+
if args.private or args.launcher:
342+
make_tar(
343+
join(assets_dir, 'private.mp3'), tar_dirs, args.ignore_path,
344+
optimize_python=args.optimize_python)
345+
finally:
346+
for directory in _temp_dirs_to_clean:
347+
shutil.rmtree(directory)
319348

320349
# Remove extra env vars tar-able directory:
321350
shutil.rmtree(env_vars_tarpath)
@@ -361,9 +390,7 @@ def make_package(args):
361390
version_code = 0
362391
if not args.numeric_version:
363392
# Set version code in format (arch-minsdk-app_version)
364-
with open(join(dirname(__file__), 'dist_info.json'), 'r') as dist_info:
365-
dist_data = json.load(dist_info)
366-
arch = dist_data["archs"][0]
393+
arch = get_dist_info_for("archs")[0]
367394
arch_dict = {"x86_64": "9", "arm64-v8a": "8", "armeabi-v7a": "7", "x86": "6"}
368395
arch_code = arch_dict.get(arch, '1')
369396
min_sdk = args.min_sdk_version

0 commit comments

Comments
 (0)