17 | Thanks for using Kivy/Buildozer VM. It has been installed only for
18 | packaging Kivy application for Android.
19 | Credentials: username: kivy / password: kivy
20 |
21 |
22 |
How to use the VM
23 |
24 | Buildozer is ready to be used. You'll need internet connection for
25 | download the Android SDK/NDK (automatically done), and during the first
26 | compilation.
27 |
28 | It is preferable to add a share a folder
29 | between your host and the VM, then build from there.
30 |
31 | By the time we shipped the VM and you using it, you may need to
32 | update buildozer.
33 |
34 |
35 | Don't try to use latest Android SDK or NDK. The defaults from buildozer
36 | works: Android SDK 20, Android NDK 9c. Recent Android SDK doesn't work
37 | the same as before (no more android command), and python-for-android
38 | project have issues with it. As for NDK, you can use 13b, it works too.
39 |
40 |
41 |
First time only, in your project directory: buildozer init
42 |
Adjust the buildozer.spec:
43 |
[buildozer]
44 | # change the name of your app
45 | package.name = myapp
46 |
47 | # change the domain of your package
48 | package.domain = com.mydomain
49 |
50 | # specify hostpython2 manually. If you want to use python 3, check buildozer
51 | # README about it, the VM is not preinstalled for that.
52 | requirements = hostpython2,kivy
53 |
54 | [buildozer]
55 | # update the build directory (issue with virtualbox shared folder and symlink)
56 | build_dir = /build/myapp
57 |
58 |
Build your application: buildozer android debug
59 |
Build and deploy, run and get the logs: buildozer android debug deploy run logcat
60 |
61 |
62 |
Share a folder
63 |
64 | Virtualbox allows you to share a folder between your computer and the
65 | VM. To do, just:
66 |
67 |
Go into Devices > Shared Folders > Shared Folders Settings
68 |
Add a new folder, select the automount option
69 |
Reboot the VM (that's easier)
70 |
You'll find your new directory at /media/sf_directoryname
71 |
72 |
73 |
74 |
75 | Virtualbox doesn't support symlink in Shared Folder anymore. So buildozer
76 | will fail during the build.
77 | We already created a /build directory where you can put your
78 | build in it. Edit your buildozer.spec:
79 |
85 | The buildozer version you have may be outdated, as well as the dependencies.
86 | The best is to regularly update buildozer:
87 |
sudo pip install -U buildozer
88 |
89 |
90 |
Cleaning cache
91 |
92 |
93 | The simplest way to update kivy and other modules is to clean all the
94 | buildozer cache, and rebuild everything.
95 |
96 |
rm -rf ~/.buildozer/android/packages
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/buildozer/tools/packer/http/welcome/milligram.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Milligram v1.3.0
3 | * https://milligram.github.io
4 | *
5 | * Copyright (c) 2017 CJ Patoilo
6 | * Licensed under the MIT license
7 | */
8 |
9 | *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}
10 |
11 | /*# sourceMappingURL=milligram.min.css.map */
--------------------------------------------------------------------------------
/buildozer/tools/packer/launch:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | /usr/bin/qemu-system-x86_64 -redir tcp:3213::22 -vga qxl -display sdl -netdev user,id=user.0 -device virtio-net,netdev=user.0 -drive file=output-from-netboot-iso/ubuntu.qcow2,if=virtio -boot once=d -name sanitytest -machine type=pc-1.0,accel=kvm -m 512M -vnc 0.0.0.0:47
4 |
--------------------------------------------------------------------------------
/buildozer/tools/packer/scripts/additional-packages.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -eux
2 | # Don't use openjdk-9, the conf directory is missing, and we get
3 | # an error when using the android sdk:
4 | # "Can't read cryptographic policy directory: unlimited"
5 |
6 | wget https://bootstrap.pypa.io/get-pip.py
7 | python get-pip.py
8 | rm get-pip.py
9 |
10 | apt-get -y install lib32stdc++6 lib32z1 lib32ncurses5
11 | apt-get -y install build-essential
12 | apt-get -y install git openjdk-8-jdk --no-install-recommends zlib1g-dev
13 | pip install cython buildozer python-for-android
14 |
--------------------------------------------------------------------------------
/buildozer/tools/packer/scripts/install-virtualbox-guest-additions.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Mount the disk image
4 | cd /tmp
5 | mkdir /tmp/isomount
6 | mount -t iso9660 /dev/sr1 /tmp/isomount
7 |
8 | # Install the drivers
9 | /tmp/isomount/VBoxLinuxAdditions.run
10 |
11 | # Cleanup
12 | umount isomount
13 |
--------------------------------------------------------------------------------
/buildozer/tools/packer/scripts/minimize.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Remove unwanted applications
4 | apt-get -y remove --purge libreoffice*
5 | apt-get -y remove --purge pidgin*
6 | apt-get -y remove --purge thunderbird*
7 | apt-get -y remove --purge fonts-noto-cjk
8 |
9 | # Remove APT cache
10 | apt-get -y --purge autoremove
11 | apt-get -y clean
12 |
13 | # Cleanup log files
14 | find /var/log -type f | while read f; do echo -ne '' > $f; done;
15 |
16 | # Whiteout root
17 | count=`df --sync -kP / | tail -n1 | awk -F ' ' '{print $4}'`;
18 | count=$(expr $count - 1)
19 | dd if=/dev/zero of=/tmp/whitespace bs=1024 count=$count;
20 | rm /tmp/whitespace;
21 |
22 | # Whiteout /boot
23 | count=`df --sync -kP /boot | tail -n1 | awk -F ' ' '{print $4}'`;
24 | count=$(expr $count - 1)
25 | dd if=/dev/zero of=/boot/whitespace bs=1024 count=$count;
26 | rm /boot/whitespace;
27 |
28 | swappart=`cat /proc/swaps | tail -n1 | awk -F ' ' '{print $1}'`
29 | swapoff $swappart;
30 | dd if=/dev/zero of=$swappart;
31 | mkswap $swappart;
32 | swapon $swappart;
33 |
34 | # Zero free space to aid VM compression
35 | dd if=/dev/zero of=/EMPTY bs=1M
36 | rm -f /EMPTY
37 |
38 | # Remove bash history
39 | unset HISTFILE
40 | rm -f /root/.bash_history
41 |
--------------------------------------------------------------------------------
/buildozer/tools/packer/scripts/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # xfconf doesn't work with sudo, even with XAUTHORITY + DISPLAY
3 | # seems that the user need to log to be able to use them.
4 |
5 | # keep them for reference for now.
6 | # change theme (works better for this wallpaper)
7 | # xfconf-query -c xfce4-desktop \
8 | # --property /backdrop/screen0/monitor0/workspace0/last-image \
9 | # --set /usr/share/backgrounds/kivy-wallpaper.png
10 | # xfconf-query -c xsettings \
11 | # --property /Net/ThemeName \
12 | # --set Adwaita
13 | # xfconf-query -c xsettings \
14 | # --property /Net/IconThemeName \
15 | # --set elementary-xfce-darker
16 |
17 |
18 |
19 | set -x
20 |
21 | # ensure the kivy user can mount shared folders
22 | adduser kivy vboxsf
23 |
24 | # create a space specifically for builds
25 | mkdir /build
26 | chown kivy /build
27 |
28 | # add a little face
29 | wget $PACKER_HTTP_ADDR/kivy-icon-96.png
30 | mv kivy-icon-96.png /home/kivy/.face
31 | chown kivy.kivy /home/kivy/.face
32 |
33 | # set wallpaper
34 | wget $PACKER_HTTP_ADDR/wallpaper.png
35 | mv wallpaper.png /usr/share/backgrounds/kivy-wallpaper.png
36 | sed -i "s:/usr/share/xfce4/backdrops/xubuntu-wallpaper.png:/usr/share/backgrounds/kivy-wallpaper.png:g" /etc/xdg/xdg-xubuntu/xfce4/xfconf/xfce-perchannel-xml/xfce4-desktop.xml
37 | sed -i "s:Greybird:Adwaita:g" /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml
38 | sed -i "s:Greybird:Adwaita:g" /etc/xdg/xdg-xubuntu/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml
39 | sed -i "s:Greybird:Adwaita:g" /etc/xdg/xdg-xubuntu/xfce4/xfconf/xfce-perchannel-xml/xfwm4.xml
40 | sed -i "s:Greybird:Adwaita:g" /etc/xdg/xdg-xubuntu/xfce4/xfconf/xfce-perchannel-xml/xfce4-notifyd.xml
41 | sed -i "s:elementary-xfce-darker:elementary-xfce-darkest:g" /etc/xdg/xdg-xubuntu/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml
42 | sed -i "s:elementary-xfce-dark:elementary-xfce-darkest:g" /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml
43 |
44 | # add desktop icon
45 | wget $PACKER_HTTP_ADDR/buildozer.desktop
46 | mkdir -p /home/kivy/Desktop
47 | cp buildozer.desktop /home/kivy/Desktop/
48 | chown kivy.kivy -R /home/kivy/Desktop
49 | chmod +x /home/kivy/Desktop/buildozer.desktop
50 | mv buildozer.desktop /usr/share/applications/
51 | sed -i "s:^favorites=.*$:favorites=buildozer.desktop,exo-terminal-emulator.desktop,exo-web-browser.desktop,xfce-keyboard-settings.desktop,exo-file-manager.desktop,org.gnome.Software.desktop,xfhelp4.desktop:g" /etc/xdg/xdg-xubuntu/xfce4/whiskermenu/defaults.rc
52 |
53 | # copy welcome directory
54 | mkdir -p /usr/share/applications/buildozer-welcome
55 | cd /usr/share/applications/buildozer-welcome
56 | wget $PACKER_HTTP_ADDR/welcome/milligram.min.css
57 | wget $PACKER_HTTP_ADDR/welcome/buildozer.css
58 | wget $PACKER_HTTP_ADDR/welcome/index.html
59 | wget $PACKER_HTTP_ADDR/kivy-icon-96.png
60 | mv kivy-icon-96.png icon.png
61 | cd -
62 |
--------------------------------------------------------------------------------
/buildozer/tools/packer/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "variables": {
3 | "disk_size": "20480",
4 | "disk_format": "ovf",
5 | "ssh_username": "kivy",
6 | "ssh_password": "kivy",
7 | "hostname": "kivyvm"
8 | },
9 | "description": "Build a Xubuntu Virtual Machine",
10 | "builders": [{
11 | "type": "virtualbox-iso",
12 | "name": "kivy-buildozer-vm",
13 | "http_directory": "http",
14 | "iso_checksum": "6131e2cc90cf30407af18f3f1af16c54bf58ffc8",
15 | "iso_checksum_type": "sha1",
16 | "iso_url": "http://archive.ubuntu.com/ubuntu/dists/zesty/main/installer-amd64/current/images/netboot/mini.iso",
17 | "ssh_username": "{{user `ssh_username`}}",
18 | "ssh_password": "{{user `ssh_password`}}",
19 | "boot_wait": "3s",
20 | "boot_command": [
21 | "",
22 | "/linux noapic preseed/url=http://{{.HTTPIP}}:{{.HTTPPort}}/preseed.cfg ",
23 | "hostname={{user `hostname`}} ",
24 | "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
25 | "fb=false ",
26 | "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
27 | "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
28 | "initrd=/initrd.gz -- "
29 | ],
30 | "disk_size": "{{user `disk_size`}}",
31 | "format": "{{user `disk_format`}}",
32 | "headless": false,
33 | "shutdown_command": "echo {{user `ssh_password`}} | sudo -S shutdown -P now",
34 | "vm_name": "Kivy/Buildozer VM",
35 | "guest_os_type": "Ubuntu_64",
36 | "guest_additions_mode": "attach",
37 | "ssh_wait_timeout": "120m"
38 | }],
39 | "provisioners": [{
40 | "type": "shell",
41 | "execute_command": "echo {{user `ssh_password`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'",
42 | "scripts": [
43 | "scripts/install-virtualbox-guest-additions.sh",
44 | "scripts/setup.sh",
45 | "scripts/additional-packages.sh",
46 | "scripts/minimize.sh"
47 | ]
48 | }],
49 | "post-processors": []
50 | }
51 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://www.sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Buildozer.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Buildozer.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Buildozer"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Buildozer"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
10 | set I18NSPHINXOPTS=%SPHINXOPTS% source
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.https://www.sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Buildozer.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Buildozer.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Buildozer documentation build configuration file, created by
4 | # sphinx-quickstart on Sun Apr 20 16:56:31 2014.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import os
15 | import re
16 |
17 | # If extensions (or modules to document with autodoc) are in another directory,
18 | # add these directories to sys.path here. If the directory is relative to the
19 | # documentation root, use os.path.abspath to make it absolute, like shown here.
20 | #sys.path.insert(0, os.path.abspath('.'))
21 |
22 | # -- General configuration -----------------------------------------------------
23 |
24 | # If your documentation needs a minimal Sphinx version, state it here.
25 | #needs_sphinx = '1.0'
26 |
27 | # Add any Sphinx extension module names here, as strings. They can be extensions
28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
29 | extensions = ['sphinx.ext.autosectionlabel']
30 |
31 | # Add any paths that contain templates here, relative to this directory.
32 | templates_path = ['_templates']
33 |
34 | # The suffix of source filenames.
35 | source_suffix = '.rst'
36 |
37 | # The encoding of source files.
38 | #source_encoding = 'utf-8-sig'
39 |
40 | # The master toctree document.
41 | master_doc = 'index'
42 |
43 | # General information about the project.
44 | project = u'Buildozer'
45 | copyright = u'2014-2023, Kivy Team and other contributors'
46 |
47 | # The version info for the project you're documenting, acts as replacement for
48 | # |version| and |release|, also used in various other places throughout the
49 | # built documents.
50 | #
51 |
52 | # Lookup the version from the Buildozer module, without installing it
53 | # since readthedocs.org may have issue to install it.
54 | # Read the version from the __init__.py file, without importing it.
55 | def get_version():
56 | with open(
57 | os.path.join(os.path.abspath("../.."), "buildozer", "__init__.py")
58 | ) as fp:
59 | for line in fp:
60 | m = re.search(r'^\s*__version__\s*=\s*([\'"])([^\'"]+)\1\s*$', line)
61 | if m:
62 | return m.group(2)
63 |
64 |
65 | # The short X.Y version.
66 | version = get_version()
67 | # The full version, including alpha/beta/rc tags.
68 | release = get_version()
69 |
70 | # The language for content autogenerated by Sphinx. Refer to documentation
71 | # for a list of supported languages.
72 | #language = None
73 |
74 | # There are two options for replacing |today|: either, you set today to some
75 | # non-false value, then it is used:
76 | #today = ''
77 | # Else, today_fmt is used as the format for a strftime call.
78 | #today_fmt = '%B %d, %Y'
79 |
80 | # List of patterns, relative to source directory, that match files and
81 | # directories to ignore when looking for source files.
82 | exclude_patterns = []
83 |
84 | # The reST default role (used for this markup: `text`) to use for all documents.
85 | #default_role = None
86 |
87 | # If true, '()' will be appended to :func: etc. cross-reference text.
88 | #add_function_parentheses = True
89 |
90 | # If true, the current module name will be prepended to all description
91 | # unit titles (such as .. function::).
92 | #add_module_names = True
93 |
94 | # If true, sectionauthor and moduleauthor directives will be shown in the
95 | # output. They are ignored by default.
96 | #show_authors = False
97 |
98 | # The name of the Pygments (syntax highlighting) style to use.
99 | pygments_style = 'sphinx'
100 |
101 | # A list of ignored prefixes for module index sorting.
102 | #modindex_common_prefix = []
103 |
104 | # If true, keep warnings as "system message" paragraphs in the built documents.
105 | #keep_warnings = False
106 |
107 |
108 | # -- Options for HTML output ---------------------------------------------------
109 |
110 | # The theme to use for HTML and HTML Help pages. See the documentation for
111 | # a list of builtin themes.
112 | html_theme = 'default'
113 |
114 | # Theme options are theme-specific and customize the look and feel of a theme
115 | # further. For a list of options available for each theme, see the
116 | # documentation.
117 | #html_theme_options = {}
118 |
119 | # Add any paths that contain custom themes here, relative to this directory.
120 | #html_theme_path = []
121 |
122 | # The name for this set of Sphinx documents. If None, it defaults to
123 | # " v documentation".
124 | #html_title = None
125 |
126 | # A shorter title for the navigation bar. Default is the same as html_title.
127 | #html_short_title = None
128 |
129 | # The name of an image file (relative to this directory) to place at the top
130 | # of the sidebar.
131 | #html_logo = None
132 |
133 | # The name of an image file (within the static path) to use as favicon of the
134 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
135 | # pixels large.
136 | #html_favicon = None
137 |
138 | # Add any paths that contain custom static files (such as style sheets) here,
139 | # relative to this directory. They are copied after the builtin static files,
140 | # so a file named "default.css" will overwrite the builtin "default.css".
141 | # html_static_path = ['_static']
142 |
143 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
144 | # using the given strftime format.
145 | #html_last_updated_fmt = '%b %d, %Y'
146 |
147 | # If true, SmartyPants will be used to convert quotes and dashes to
148 | # typographically correct entities.
149 | #html_use_smartypants = True
150 |
151 | # Custom sidebar templates, maps document names to template names.
152 | #html_sidebars = {}
153 |
154 | # Additional templates that should be rendered to pages, maps page names to
155 | # template names.
156 | #html_additional_pages = {}
157 |
158 | # If false, no module index is generated.
159 | #html_domain_indices = True
160 |
161 | # If false, no index is generated.
162 | #html_use_index = True
163 |
164 | # If true, the index is split into individual pages for each letter.
165 | #html_split_index = False
166 |
167 | # If true, links to the reST sources are added to the pages.
168 | #html_show_sourcelink = True
169 |
170 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
171 | #html_show_sphinx = True
172 |
173 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
174 | #html_show_copyright = True
175 |
176 | # If true, an OpenSearch description file will be output, and all pages will
177 | # contain a tag referring to it. The value of this option must be the
178 | # base URL from which the finished HTML is served.
179 | #html_use_opensearch = ''
180 |
181 | # This is the file name suffix for HTML files (e.g. ".xhtml").
182 | #html_file_suffix = None
183 |
184 | # Output file base name for HTML help builder.
185 | htmlhelp_basename = 'Buildozerdoc'
186 |
187 |
188 | # -- Options for LaTeX output --------------------------------------------------
189 |
190 | latex_elements = {
191 | # The paper size ('letterpaper' or 'a4paper').
192 | #'papersize': 'letterpaper',
193 |
194 | # The font size ('10pt', '11pt' or '12pt').
195 | #'pointsize': '10pt',
196 |
197 | # Additional stuff for the LaTeX preamble.
198 | #'preamble': '',
199 | }
200 |
201 | # Grouping the document tree into LaTeX files. List of tuples
202 | # (source start file, target name, title, author, documentclass [howto/manual]).
203 | latex_documents = [
204 | ('index', 'Buildozer.tex', u'Buildozer Documentation',
205 | u'Kivy Team and other contributors', 'manual'),
206 | ]
207 |
208 | # The name of an image file (relative to this directory) to place at the top of
209 | # the title page.
210 | #latex_logo = None
211 |
212 | # For "manual" documents, if this is true, then toplevel headings are parts,
213 | # not chapters.
214 | #latex_use_parts = False
215 |
216 | # If true, show page references after internal links.
217 | #latex_show_pagerefs = False
218 |
219 | # If true, show URL addresses after external links.
220 | #latex_show_urls = False
221 |
222 | # Documents to append as an appendix to all manuals.
223 | #latex_appendices = []
224 |
225 | # If false, no module index is generated.
226 | #latex_domain_indices = True
227 |
228 |
229 | # -- Options for manual page output --------------------------------------------
230 |
231 | # One entry per manual page. List of tuples
232 | # (source start file, name, description, authors, manual section).
233 | man_pages = [
234 | ('index', 'buildozer', 'Buildozer Documentation',
235 | ['Kivy Team and other contributors'], 1)
236 | ]
237 |
238 | # If true, show URL addresses after external links.
239 | #man_show_urls = False
240 |
241 |
242 | # -- Options for Texinfo output ------------------------------------------------
243 |
244 | # Grouping the document tree into Texinfo files. List of tuples
245 | # (source start file, target name, title, author,
246 | # dir menu entry, description, category)
247 | texinfo_documents = [
248 | ('index', 'Buildozer', u'Buildozer Documentation',
249 | 'Kivy Team and other contributors', 'Buildozer',
250 | 'Turns Python applications into binary packages ready for '
251 | 'installation on a number of platforms.',
252 | 'Miscellaneous'),
253 | ]
254 |
255 | # Documents to append as an appendix to all manuals.
256 | #texinfo_appendices = []
257 |
258 | # If false, no module index is generated.
259 | #texinfo_domain_indices = True
260 |
261 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
262 | #texinfo_show_urls = 'footnote'
263 |
264 | # If true, do not generate a @detailmenu in the "Top" node's menu.
265 | #texinfo_no_detailmenu = False
266 |
--------------------------------------------------------------------------------
/docs/source/contact.rst:
--------------------------------------------------------------------------------
1 | .. _contact:
2 |
3 | Contact Us
4 | ==========
5 |
6 | If you are looking to contact the Kivy Team (who are responsible for managing the
7 | Buildozer project), including looking for support, please see our
8 | `latest contact details `_.
--------------------------------------------------------------------------------
/docs/source/contribute.rst:
--------------------------------------------------------------------------------
1 | .. _contribute:
2 |
3 | Contribution Guidelines
4 | =======================
5 |
6 | Buildozer is part of the `Kivy `_ ecosystem - a large group of
7 | products used by many thousands of developers for free, but it
8 | is built entirely by the contributions of volunteers. We welcome (and rely on)
9 | users who want to give back to the community by contributing to the project.
10 |
11 | Contributions can come in many forms. See the latest
12 | `Contribution Guidelines `_
13 | for general guidelines of how you can help us.
14 |
--------------------------------------------------------------------------------
/docs/source/faq.rst:
--------------------------------------------------------------------------------
1 | FAQ
2 | ===
3 |
4 | Buildozer has an `online FAQ `_. It contains the answers to
5 | questions that repeatedly come up.
6 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to Buildozer's documentation!
2 | =====================================
3 |
4 | Buildozer is a development tool for turning Python
5 | applications into binary packages ready for installation on any of a number of
6 | platforms, including mobile devices. It automates the entire build process.
7 |
8 | The app developer provides a single "buildozer.spec" file, which describes the
9 | application's requirements and settings, such as title and icons. Buildozer can
10 | then create installable packages for Android, iOS, Windows, macOS and/or Linux.
11 |
12 | Buildozer has features to make
13 | building apps using the `Kivy framework `_ easier,
14 | but it can be used independently - even with other GUI frameworks.
15 |
16 | .. note::
17 | python-for-android only runs on Linux or macOS. (On Windows, a Linux emulator is
18 | required.)
19 |
20 | Kivy for iOS only runs on macOS.
21 |
22 | Buildozer is managed by the `Kivy Team `_. It relies
23 | on its sibling projects:
24 | `python-for-android `_ for
25 | Android packaging, and
26 | `Kivy for iOS `_ for iOS packaging.
27 |
28 | Buildozer is released and distributed under the terms of the MIT license. You should have received a
29 | copy of the MIT license alongside your distribution. Our
30 | `latest license `_
31 | is also available.
32 |
33 |
34 | .. note::
35 | This tool is unrelated to the online build service, `buildozer.io`.
36 |
37 | .. toctree::
38 | :maxdepth: 2
39 |
40 | installation
41 | quickstart
42 | specifications
43 | recipes
44 | faq
45 | contribute
46 | contact
47 |
48 |
--------------------------------------------------------------------------------
/docs/source/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | Buildozer is tested on Python 3.8 and above.
5 | Depending the platform you want to target, you might need more tools installed.
6 | Buildozer tries to give you hints or tries to install few things for
7 | you, but it doesn't cover every situation.
8 |
9 | First, install the buildozer project.
10 |
11 | The most-recently released version can be installed with::
12 |
13 | pip install --user --upgrade buildozer
14 |
15 | Add the `--user` option if you are not using a virtual environment (not recommended).
16 |
17 | If you would like to install the latest version still under development::
18 |
19 | pip install https://github.com/kivy/buildozer/archive/master.zip
20 |
21 |
22 | Targeting Android
23 | -----------------
24 |
25 | Android on Ubuntu 20.04 and 22.04 (64bit)
26 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 |
28 | .. note::
29 | Later versions of Ubuntu are expected to work. However only the latest
30 | `Long Term Support (LTS) release `_
31 | is regularly tested.
32 |
33 | Additional installation required to support Android::
34 |
35 | sudo apt update
36 | sudo apt install -y git zip unzip openjdk-17-jdk python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev automake
37 |
38 | # add the following line at the end of your ~/.bashrc file
39 | export PATH=$PATH:~/.local/bin/
40 |
41 | If `openjdk-17 `_ is not compatible with other installed programs,
42 | for Buildozer the minimum compatible openjdk version is 11.
43 |
44 | Then install the buildozer project with::
45 |
46 | pip3 install --user --upgrade buildozer
47 |
48 |
49 | Android on Windows 10 or 11
50 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
51 |
52 | To use Buildozer on Windows, you need first to enable Windows Subsystem for Linux (WSL) and
53 | `install a Linux distribution `_.
54 |
55 | These instructions were tested with WSL 1 and Ubuntu 18.04 LTS, and WSL2 with Ubuntu 20.04 and 22.04.
56 |
57 | After installing WSL and Ubuntu on your Windows machine, open Ubuntu, run the commands listed in the previous section,
58 | and restart your WSL terminal to enable the path change.
59 |
60 | Copy your Kivy project directory from the Windows partition to the WSL partition.
61 |
62 | .. warning::
63 | It is important to use the WSL partition. The Android SDK for Linux does not work on Windows' NTFS drives.
64 | This will lead to obscure failures.
65 |
66 | For debugging, WSL does not have direct access to USB. Copy the .apk file to the Windows partition and run ADB
67 | (Android Debug Bridge) from a Windows prompt. ADB is part of Android Studio, if you do not have this installed
68 | you can install just the platform tools which also contain ADB.
69 |
70 | - Visit the `Android SDK Platform Tools `_ page, and
71 | select "Download SDK Platform-Tools for Windows".
72 |
73 | - Unzip the downloaded file to a new folder. For example, `C:\\platform-tools\\`
74 |
75 | Before Using Buildozer
76 | ~~~~~~~~~~~~~~~~~~~~~~
77 |
78 | If you wish, clone your code to a new folder where the build process will run.
79 |
80 | You don't need to create a virtualenv for your code requirements. But just add these requirements to a configuration
81 | file called `buildozer.spec` as you will see in the following sections.
82 |
83 | Before running Buildozer in your code folder, remember to go into the Buildozer folder and activate the Buildozer
84 | virtualenv.
85 |
86 | Android on macOS
87 | ~~~~~~~~~~~~~~~~
88 |
89 | Additional installation required to support macOS::
90 |
91 | python3 -m pip install --user --upgrade buildozer # the --user should be removed if you do this in a venv
92 |
93 |
94 | TroubleShooting
95 | ~~~~~~~~~~~~~~~
96 |
97 | Buildozer stuck on "Installing/updating SDK platform tools if necessary"
98 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
99 |
100 | Press "y" then enter to continue, the license acceptance system is silently waiting for your input
101 |
102 |
103 | Aidl not found, please install it.
104 | """"""""""""""""""""""""""""""""""
105 |
106 | Buildozer didn't install a necessary package
107 |
108 | ::
109 |
110 | ~/.buildozer/android/platform/android-sdk/tools/bin/sdkmanager "build-tools;29.0.0"
111 |
112 | Then press "y" then enter to accept the license.
113 |
114 | Alternatively, the Android SDK license can be automatically accepted - see `build.spec` for details.
115 |
116 |
117 | python-for-android related errors
118 | """""""""""""""""""""""""""""""""
119 | See the dedicated `p4a troubleshooting documentation
120 | `_.
121 |
122 |
123 | Targeting IOS
124 | -------------
125 |
126 | Additional installation required to support iOS:
127 |
128 | * Install XCode and command line tools (through the AppStore)
129 | * Install `Homebrew `_::
130 |
131 | brew install pkg-config sdl2 sdl2_image sdl2_ttf sdl2_mixer gstreamer autoconf automake
132 |
133 | * Install pip, virtualenv and Kivy for iOS::
134 |
135 | python -m pip install --user --upgrade pip virtualenv kivy-ios
136 |
137 |
--------------------------------------------------------------------------------
/docs/source/quickstart.rst:
--------------------------------------------------------------------------------
1 | Quickstart
2 | ==========
3 |
4 | Let's get started with Buildozer!
5 |
6 | Init and build for Android
7 | --------------------------
8 |
9 | #. Buildozer will try to guess the version of your application, by searching a
10 | line like `__version__ = "1.0.3"` in your `main.py`. Ensure you have one at
11 | the start of your application. It is not mandatory but heavily advised.
12 |
13 | #. Create a `buildozer.spec` file, with::
14 |
15 | buildozer init
16 |
17 | #. Edit the `buildozer.spec` according to the :ref:`specifications`. You should
18 | at least change the `title`, `package.name` and `package.domain` in the
19 | `[app]` section.
20 |
21 | #. Start a Android/debug build with::
22 |
23 | buildozer -v android debug
24 |
25 | #. Now it's time for a coffee / tea, or a dinner if you have a slow computer.
26 | The first build will be slow, as it will download the Android SDK, NDK, and
27 | others tools needed for the compilation.
28 | Don't worry, thoses files will be saved in a global directory and will be
29 | shared across the different project you'll manage with Buildozer.
30 |
31 | #. At the end, you should have an APK or AAB file in the `bin/` directory.
32 |
33 |
34 | Run my application
35 | ------------------
36 |
37 | Buildozer is able to deploy the application on your mobile, run it, and even
38 | get back the log into the console. It will work only if you already compiled
39 | your application at least once::
40 |
41 | buildozer android deploy run logcat
42 |
43 | For iOS, it would look the same::
44 |
45 | buildozer ios deploy run
46 |
47 | You can combine the compilation with the deployment::
48 |
49 | buildozer -v android debug deploy run logcat
50 |
51 | You can also set this line at the default command to do if Buildozer is started
52 | without any arguments::
53 |
54 | buildozer setdefault android debug deploy run logcat
55 |
56 | # now just type buildozer, and it will do the default command
57 | buildozer
58 |
59 | To save the logcat output into a file named `my_log.txt` (the file will appear in your current directory)::
60 |
61 | buildozer -v android debug deploy run logcat > my_log.txt
62 |
63 | To see your running application's print() messages and python's error messages, use:
64 |
65 | ::
66 |
67 | buildozer -v android deploy run logcat | grep python
68 |
69 | Run my application from Windows
70 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
71 |
72 | - Plug your Android device on a USB port.
73 |
74 | - Open Windows PowerShell, go into the folder where you installed the Windows version of ADB, and activate the ADB daemon. When the daemon is started you must see a number besides the word "device" meaning your device was correctly detected. In case of trouble, try another USB port or USB cable.
75 |
76 | ::
77 |
78 | cd C:\platform-tools\
79 | .\adb.exe devices
80 |
81 | - Open the Linux distribution you installed on Windows Subsystem for Linux (WSL) and proceed with the deploy commands:
82 |
83 | ::
84 |
85 | buildozer -v android deploy run
86 |
87 | It is important to notice that Windows ADB and Buildozer-installed ADB must be the same version. To check the versions,
88 | open PowerShell and type::
89 |
90 | cd C:\platform-tools\
91 | .\adb.exe version
92 | wsl
93 | cd ~/.buildozer/android/platform/android-sdk/platform-tools/
94 | ./adb version
95 |
96 | Install on non-connected devices
97 | --------------------------------
98 |
99 | If you have compiled a package, and want to share it easily with others
100 | devices, you might be interested with the `serve` command. It will serve the
101 | `bin/` directory over HTTP. Then you just have to access to the URL showed in
102 | the console from your mobile::
103 |
104 | buildozer serve
105 |
106 |
--------------------------------------------------------------------------------
/docs/source/recipes.rst:
--------------------------------------------------------------------------------
1 | Recipes
2 | =======
3 |
4 | Python apps may depend on third party packages and extensions.
5 |
6 | Most packages are written in pure Python, and Buildozer can generally used them
7 | without any modification.
8 |
9 | However, some packages and Python extensions require modification to work on
10 | mobile platforms.
11 |
12 | For example, for extensions and packages that depend on C or other programming
13 | languages, the default compilation instructions may not work for the target;
14 | The ARM compiler and Android NDK introduce special requirements that the library
15 | may not handle correctly
16 |
17 | For such cases, a "recipe" is required. A recipe allows you to compile libraries
18 | and Python extension for the mobile by patching them before use.
19 |
20 | python-for-android and Kivy for iOS come, batteries included, with a number of
21 | recipes for the most popular packages.
22 |
23 | However, if you use a novel package - and there are no pure Python equivalents that
24 | you can substitute in - you may need to write (or commission) your own recipe. We
25 | would welcome your recipe as a contribution to the project to help the next developer
26 | who wants to use the same library.
27 |
28 | More instructions on how to write your own recipes is available in the
29 | `Kivy for iOS `_ and
30 | `python-for-android documentation `_.
31 |
32 | Instructions on how to test your own recipes from Buildozer is available in the
33 | `latest Buildozer Contribution Guidelines `_.
34 |
--------------------------------------------------------------------------------
/docs/source/specifications.rst:
--------------------------------------------------------------------------------
1 | Specifications
2 | ==============
3 |
4 | This document explains in detail all the configuration tokens you can use in
5 | `buildozer.spec`.
6 |
7 | Section [app]
8 | -------------
9 |
10 | - `title`: String, title of your application.
11 |
12 | It might be possible that some characters are not working depending on the
13 | targeted platform. It's best to try and see if everything works as expected.
14 | Try to avoid too long titles, as they will also not fit in the title
15 | displayed under the icon.
16 |
17 | - `package.name`: String, package name.
18 |
19 | The Package name is one word with only ASCII characters and/or numbers. It
20 | should not contain any special characters. For example, if your application
21 | is named `Flat Jewels`, the package name can be `flatjewels`.
22 |
23 | - `package.domain`: String, package domain.
24 |
25 | Package domain is a string that references the company or individual that
26 | did the app. Both domain+name will become your application identifier for
27 | Android and iOS, choose it carefully. As an example, when the Kivy`s team
28 | is publishing an application, the domain starts with `org.kivy`.
29 |
30 | - `source.dir`: String, location of your application sources.
31 |
32 | The location must be a directory that contains a `main.py` file. It defaults
33 | to the directory where `buildozer.spec` is.
34 |
35 | - Source Inclusion/Exclusion options.
36 |
37 | - `source.include_exts`: List, file extensions to include.
38 | - `source.exclude_exts`: List, file extensions to exclude, even if included by
39 | `source.include_exts`
40 | - `source.exclude_dirs`: List, directories to exclude.
41 | - `source.exclude_patterns`: List, files to exclude if they match a pattern.
42 | - `source.include_patterns`: List, files to include if they match a pattern, even if excluded by
43 | `source.exclude_dirs` or `source.exclude_patterns`
44 |
45 | By default, not all files are in your `source.dir` are included. You can
46 | use these options to alter which files are included in your app and which
47 | are excluded.
48 |
49 | Directories and files starting with a "." are always excluded; this cannot be
50 | overridden.
51 |
52 | Files that have an extension that is not in `source.include_exts` are excluded.
53 | (The default suggestion is `py,png,jpg,kv,atlas`. You may want to include other
54 | file extensions such as resource files: gif, xml, mp3, etc.) File names that
55 | have no extension (i.e contain no ".") are not excluded here.
56 | `source.exclude_exts` takes priority over `source.include_exts` - it excludes any listed extensions
57 | that were previously included.
58 |
59 | Files and directories in directories listed in `source.exclude_dirs` are excluded. For example, you can exclude your
60 | `tests` and `bin` directory with::
61 |
62 | source.exclude_dirs = tests, bin
63 |
64 | `source.exclude_patterns` are also excluded. This is useful for excluding individual
65 | files. For example::
66 |
67 | source.exclude_patterns = license
68 |
69 | These dir and pattern exclusions may be overridden with
70 | `source.include_patterns` - files and directories that match will once again be included.
71 |
72 | However, `source.include_patterns` does not override the `source.include_exts` nor
73 | `source.exclude_exts`. `source.include_patterns` also cannot be used to include files or directories that
74 | start with ".")
75 |
76 | - `version.regex`: Regex, Regular expression to capture the version in
77 | `version.filename`.
78 |
79 | The default capture method of your application version is by grepping a line
80 | like this::
81 |
82 | __version__ = "1.0"
83 |
84 | The `1.0` will be used as a version.
85 |
86 | - `version.filename`: String, defaults to the main.py.
87 |
88 | File to use for capturing the version with `version.regex`.
89 |
90 | - `version`: String, manual application version.
91 |
92 | If you don't want to capture the version, comment out both `version.regex`
93 | and `version.filename`, then put the version you want directly in the
94 | `version` token::
95 |
96 | # version.regex =
97 | # version.filename =
98 | version = 1.0
99 |
100 | - `requirements`: List, Python modules or extensions that your application
101 | requires.
102 |
103 | The requirements can be either a name of a recipe in the Python-for-android
104 | project, or a pure-Python package. For example, if your application requires
105 | Kivy and requests, you need to write::
106 |
107 | requirements = kivy,requests
108 |
109 | If your application tries to install a Python extension (ie, a Python
110 | package that requires compilation), and the extension doesn't have a recipe
111 | associated to Python-for-android, it will not work. We explicitly disable
112 | the compilation here. If you want to make it work, contribute to the
113 | Python-for-android project by creating a recipe. See :doc:`contribute`.
114 |
115 | - `presplash.filename`: String, loading screen of your application.
116 |
117 | Presplash is the image shown on the device during application loading.
118 | It is called presplash on Android, and Loading image on iOS. The image might
119 | have different requirements depending the platform. Currently, Buildozer
120 | works well only with Android, iOS support is not great on this.
121 |
122 | The image must be a JPG or PNG, preferable with Power-of-two size, e.g., a
123 | 512x512 image is perfect to target all the devices. The image is not fitted,
124 | scaled, or anything on the device. If you provide a too-large image, it might
125 | not fit on small screens.
126 |
127 | - `icon.filename`: String, icon of your application.
128 |
129 | The icon of your application. It must be a PNG of 512x512 size to be able to
130 | cover all the various platform requirements.
131 |
132 | - `orientation`: List, supported orientations of the application.
133 |
134 | Indicate the orientations that your application supports.
135 | Valid values are: `portrait`, `landscape`, `portrait-reverse`, `landscape-reverse`.
136 | Defaults to `[landscape]`.
137 |
138 | - `fullscreen`: Boolean, fullscreen mode.
139 |
140 | Defaults to true, your application will run in fullscreen. Means the status
141 | bar will be hidden. If you want to let the user access the status bar,
142 | hour, notifications, use 0 as a value.
143 |
144 | - `home_app`: Boolean, Home App (launcher app) usage.
145 |
146 | Defaults to false, your application will be listed as a Home App (launcher app) if true.
147 |
148 | - `display_cutout`: String, display-cutout mode to be used.
149 |
150 | Defaults to `never`. Application will render around the cutout (notch) if set to either `default`, `shortEdges`.
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | '''
2 | Buildozer
3 | '''
4 |
5 | import sys
6 | from setuptools import setup
7 | from os.path import dirname, join
8 | import codecs
9 | import os
10 | import re
11 | import io
12 |
13 | here = os.path.abspath(os.path.dirname(__file__))
14 |
15 | CURRENT_PYTHON = sys.version_info[:2]
16 | REQUIRED_PYTHON = (3, 8)
17 |
18 | # This check and everything above must remain compatible with Python 2.7.
19 | if CURRENT_PYTHON < REQUIRED_PYTHON:
20 | sys.stderr.write("""
21 | ==========================
22 | Unsupported Python version
23 | ==========================
24 | This version of buildozer requires Python {}.{}, but you're trying to
25 | install it on Python {}.{}.
26 | """.format(*(REQUIRED_PYTHON + CURRENT_PYTHON)))
27 | sys.exit(1)
28 |
29 |
30 | def find_version(*file_paths):
31 | # Open in Latin-1 so that we avoid encoding errors.
32 | # Use codecs.open for Python 2 compatibility
33 | with codecs.open(os.path.join(here, *file_paths), 'r', 'utf-8') as f:
34 | version_file = f.read()
35 |
36 | # The version line must have the form
37 | # __version__ = 'ver'
38 | version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
39 | version_file, re.M)
40 | if version_match:
41 | return version_match.group(1)
42 | raise RuntimeError("Unable to find version string.")
43 |
44 |
45 | curdir = dirname(__file__)
46 | with io.open(join(curdir, "README.md"), encoding="utf-8") as fd:
47 | readme = fd.read()
48 | with io.open(join(curdir, "CHANGELOG.md"), encoding="utf-8") as fd:
49 | changelog = fd.read()
50 |
51 | setup(
52 | name='buildozer',
53 | version=find_version('buildozer', '__init__.py'),
54 | description='Turns Python applications into binary packages ready for '
55 | 'installation on a number of platforms.',
56 | long_description=readme + "\n\n" + changelog,
57 | long_description_content_type='text/markdown',
58 | author='Mathieu Virbel',
59 | author_email='mat@kivy.org',
60 | url='https://github.com/kivy/buildozer',
61 | project_urls={
62 | 'Website': "https://kivy.org",
63 | 'Documentation': "https://buildozer.readthedocs.io/en/stable/#",
64 | 'Source': "https://github.com/kivy/buildozer",
65 | 'Bug Reports': "https://github.com/kivy/buildozer/issues",
66 | },
67 | license='MIT',
68 | packages=[
69 | 'buildozer', 'buildozer.targets', 'buildozer.libs', 'buildozer.scripts'
70 | ],
71 | package_data={'buildozer': ['default.spec']},
72 | include_package_data=True,
73 | install_requires=[
74 | 'pexpect',
75 | 'packaging',
76 | # Cython is required by both kivy-ios and python-for-android.
77 | # However, python-for-android does not include it in its dependencies
78 | # and kivy-ios's dependencies are not always checked, so it is included
79 | # here.
80 | # Restricted version because python-for-android's recipes can't handle
81 | # later versions.
82 | 'cython<3.0'
83 | ],
84 | extras_require={
85 | 'test': ['pytest'],
86 | 'docs': ['sphinx'],
87 | 'ios': ['kivy-ios'],
88 | },
89 | classifiers=[
90 | 'Development Status :: 5 - Production/Stable',
91 | 'Intended Audience :: Developers',
92 | 'Topic :: Software Development :: Build Tools',
93 | 'Programming Language :: Python :: 3',
94 | 'Programming Language :: Python :: 3.8',
95 | 'Programming Language :: Python :: 3.9',
96 | 'Programming Language :: Python :: 3.10',
97 | 'Programming Language :: Python :: 3.11',
98 | ],
99 | entry_points={
100 | 'console_scripts': [
101 | 'buildozer=buildozer.scripts.client:main',
102 | 'buildozer-remote=buildozer.scripts.remote:main'
103 | ]
104 | })
105 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kivy/buildozer/abc2d7e66c8abe096a95ed58befe6617f7efdad0/tests/__init__.py
--------------------------------------------------------------------------------
/tests/scripts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kivy/buildozer/abc2d7e66c8abe096a95ed58befe6617f7efdad0/tests/scripts/__init__.py
--------------------------------------------------------------------------------
/tests/scripts/test_client.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import unittest
3 | from unittest import mock
4 |
5 | from buildozer.exceptions import BuildozerCommandException
6 | from buildozer.scripts import client
7 |
8 |
9 | class TestClient(unittest.TestCase):
10 |
11 | def test_run_command_called(self):
12 | """
13 | Checks Buildozer.run_command() is being called with arguments from command line.
14 | """
15 | with mock.patch('buildozer.Buildozer.run_command') as m_run_command:
16 | client.main()
17 | assert m_run_command.call_args_list == [mock.call(sys.argv[1:])]
18 |
19 | def test_exit_code(self):
20 | """
21 | Makes sure the CLI exits with error code on BuildozerCommandException, refs #674.
22 | """
23 | with mock.patch('buildozer.Buildozer.run_command') as m_run_command:
24 | m_run_command.side_effect = BuildozerCommandException()
25 | with self.assertRaises(SystemExit) as context:
26 | client.main()
27 | assert context.exception.code == 1
28 |
--------------------------------------------------------------------------------
/tests/targets/test_ios.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sys
3 | import tempfile
4 | from unittest import mock
5 |
6 | import pytest
7 |
8 | from buildozer.buildops import CommandResult
9 | from buildozer.exceptions import BuildozerCommandException
10 | from buildozer.targets.ios import TargetIos
11 | from tests.targets.utils import (
12 | init_buildozer,
13 | patch_buildops_checkbin,
14 | patch_buildops_cmd,
15 | patch_buildops_file_exists,
16 | patch_logger_error,
17 | )
18 |
19 |
20 | def patch_target_ios(method):
21 | return mock.patch("buildozer.targets.ios.TargetIos.{method}".format(method=method))
22 |
23 |
24 | def init_target(temp_dir, options=None):
25 | buildozer = init_buildozer(temp_dir, "ios", options)
26 | return TargetIos(buildozer)
27 |
28 |
29 | @pytest.mark.skipif(
30 | sys.platform != "darwin", reason="Only macOS is supported for target iOS"
31 | )
32 | class TestTargetIos:
33 | def setup_method(self):
34 | """
35 | Create a temporary directory that will contain the spec file and will
36 | serve as the root_dir.
37 | """
38 | self.temp_dir = tempfile.TemporaryDirectory()
39 |
40 | def tear_method(self):
41 | """
42 | Remove the temporary directory created in self.setup_method.
43 | """
44 | self.temp_dir.cleanup()
45 |
46 | def test_init(self):
47 | """Tests init defaults."""
48 | target = init_target(self.temp_dir)
49 | assert target.targetname == "ios"
50 | assert target.code_signing_allowed == "CODE_SIGNING_ALLOWED=NO"
51 | assert target.build_mode == "debug"
52 | assert target.platform_update is False
53 |
54 | def test_check_requirements(self):
55 | """Basic tests for the check_requirements() method."""
56 | target = init_target(self.temp_dir)
57 | assert not hasattr(target, "javac_cmd")
58 | with patch_buildops_checkbin() as m_checkbin:
59 | target.check_requirements()
60 | assert m_checkbin.call_args_list == [
61 | mock.call("Xcode xcodebuild", "xcodebuild"),
62 | mock.call("Xcode xcode-select", "xcode-select"),
63 | mock.call("Git git", "git"),
64 | mock.call("Cython cython", "cython"),
65 | mock.call("pkg-config", "pkg-config"),
66 | mock.call("autoconf", "autoconf"),
67 | mock.call("automake", "automake"),
68 | mock.call("libtool", "libtool"),
69 | ]
70 | assert target._toolchain_cmd[-1] == "toolchain.py"
71 | assert target._xcodebuild_cmd == ["xcodebuild"]
72 |
73 | def test_check_configuration_tokens(self):
74 | """Basic tests for the check_configuration_tokens() method."""
75 | target = init_target(self.temp_dir, {"ios.codesign.allowed": "yes"})
76 | with mock.patch(
77 | "buildozer.targets.android.Target.check_configuration_tokens"
78 | ) as m_check_configuration_tokens, mock.patch(
79 | "buildozer.targets.ios.TargetIos._get_available_identities"
80 | ) as m_get_available_identities:
81 | target.check_configuration_tokens()
82 | assert m_get_available_identities.call_args_list == [mock.call()]
83 | assert m_check_configuration_tokens.call_args_list == [
84 | mock.call(
85 | [
86 | '[app] "ios.codesign.debug" key missing, you must give a certificate name to use.',
87 | '[app] "ios.codesign.release" key missing, you must give a certificate name to use.',
88 | ]
89 | )
90 | ]
91 |
92 | def test_get_available_packages(self):
93 | """Checks the toolchain `recipes --compact` output is parsed correctly to return recipe list."""
94 | target = init_target(self.temp_dir)
95 | with patch_target_ios("toolchain") as m_toolchain:
96 | m_toolchain.return_value = ("hostpython3 kivy pillow python3 sdl2", None, 0)
97 | available_packages = target.get_available_packages()
98 | assert m_toolchain.call_args_list == [
99 | mock.call(["recipes", "--compact"], get_stdout=True)
100 | ]
101 | assert available_packages == [
102 | "hostpython3",
103 | "kivy",
104 | "pillow",
105 | "python3",
106 | "sdl2",
107 | ]
108 |
109 | def test_install_platform(self):
110 | """Checks `install_platform()` calls clone commands and sets `ios_dir` and `ios_deploy_dir` attributes."""
111 | target = init_target(self.temp_dir)
112 | assert target.ios_dir is None
113 | assert target.ios_deploy_dir is None
114 | with patch_buildops_cmd() as m_cmd:
115 | target.install_platform()
116 | assert m_cmd.call_args_list == [
117 | mock.call(
118 | [
119 | "git",
120 | "clone",
121 | "--branch",
122 | "master",
123 | "https://github.com/kivy/kivy-ios",
124 | ],
125 | cwd=mock.ANY,
126 | env=mock.ANY,
127 | ),
128 | mock.call(
129 | [
130 | "git",
131 | "clone",
132 | "--branch",
133 | "1.12.2",
134 | "https://github.com/phonegap/ios-deploy",
135 | ],
136 | cwd=mock.ANY,
137 | env=mock.ANY,
138 | ),
139 | ]
140 | assert target.ios_dir.endswith(".buildozer/ios/platform/kivy-ios")
141 | assert target.ios_deploy_dir.endswith(".buildozer/ios/platform/ios-deploy")
142 |
143 | def test_compile_platform(self):
144 | """Checks the `toolchain build` command is called on the ios requirements."""
145 | target = init_target(self.temp_dir)
146 | target.ios_deploy_dir = "/ios/deploy/dir"
147 | # fmt: off
148 | with patch_target_ios("get_available_packages") as m_get_available_packages, \
149 | patch_target_ios("toolchain") as m_toolchain, \
150 | patch_buildops_file_exists() as m_file_exists:
151 | m_get_available_packages.return_value = ["hostpython3", "python3"]
152 | m_file_exists.return_value = True
153 | target.compile_platform()
154 | # fmt: on
155 | assert m_get_available_packages.call_args_list == [mock.call()]
156 | assert m_toolchain.call_args_list == [mock.call(["build", "python3"])]
157 | assert m_file_exists.call_args_list == [
158 | mock.call(os.path.join(target.ios_deploy_dir, "ios-deploy"))
159 | ]
160 |
161 | def test_get_package(self):
162 | """Checks default package values and checks it can be overridden."""
163 | # default value
164 | target = init_target(self.temp_dir)
165 | package = target._get_package()
166 | assert package == "org.test.myapp"
167 | # override
168 | target = init_target(
169 | self.temp_dir,
170 | {"package.domain": "com.github.kivy", "package.name": "buildozer"},
171 | )
172 | package = target._get_package()
173 | assert package == "com.github.kivy.buildozer"
174 |
175 | def test_unlock_keychain_wrong_password(self):
176 | """A `BuildozerCommandException` should be raised on wrong password 3 times."""
177 | target = init_target(self.temp_dir)
178 | # fmt: off
179 | with mock.patch("buildozer.targets.ios.getpass") as m_getpass, \
180 | patch_buildops_cmd() as m_cmd, \
181 | pytest.raises(BuildozerCommandException):
182 | m_getpass.return_value = "password"
183 | # the `security unlock-keychain` command returned an error
184 | # hence we'll get prompted to enter the password
185 | m_cmd.return_value = CommandResult(None, None, 123)
186 | target._unlock_keychain()
187 | # fmt: on
188 | assert m_getpass.call_args_list == [
189 | mock.call("Password to unlock the default keychain:"),
190 | mock.call("Password to unlock the default keychain:"),
191 | mock.call("Password to unlock the default keychain:"),
192 | ]
193 |
194 | def test_build_package_no_signature(self):
195 | """Code signing is currently required to go through final `xcodebuild` step."""
196 | target = init_target(self.temp_dir)
197 | target.ios_dir = "/ios/dir"
198 | # fmt: off
199 | with patch_target_ios("_unlock_keychain") as m_unlock_keychain, \
200 | patch_logger_error() as m_error, \
201 | mock.patch("buildozer.targets.ios.TargetIos.load_plist_from_file") as m_load_plist_from_file, \
202 | mock.patch("buildozer.targets.ios.TargetIos.dump_plist_to_file") as m_dump_plist_to_file, \
203 | patch_buildops_cmd() as m_cmd:
204 | m_load_plist_from_file.return_value = {}
205 | target.build_package()
206 | # fmt: on
207 | assert m_unlock_keychain.call_args_list == [mock.call()]
208 | assert m_error.call_args_list == [
209 | mock.call(
210 | "Cannot create the IPA package without signature. "
211 | 'You must fill the "ios.codesign.debug" token.'
212 | )
213 | ]
214 | assert m_load_plist_from_file.call_args_list == [
215 | mock.call("/ios/dir/myapp-ios/myapp-Info.plist")
216 | ]
217 | assert m_dump_plist_to_file.call_args_list == [
218 | mock.call(
219 | {
220 | "CFBundleIdentifier": "org.test.myapp",
221 | "CFBundleShortVersionString": "0.1",
222 | "CFBundleVersion": "0.1.None",
223 | },
224 | "/ios/dir/myapp-ios/myapp-Info.plist",
225 | )
226 | ]
227 | assert m_cmd.call_args_list == [
228 | mock.call(mock.ANY, cwd=target.ios_dir, env=mock.ANY),
229 | mock.call([
230 | "xcodebuild",
231 | "-configuration",
232 | "Debug",
233 | "-allowProvisioningUpdates",
234 | "ENABLE_BITCODE=NO",
235 | "CODE_SIGNING_ALLOWED=NO",
236 | "clean",
237 | "build"],
238 | cwd="/ios/dir/myapp-ios",
239 | env=mock.ANY,
240 | ),
241 | mock.call([
242 | "xcodebuild",
243 | "-alltargets",
244 | "-configuration",
245 | "Debug",
246 | "-scheme",
247 | "myapp",
248 | "-archivePath",
249 | "/ios/dir/myapp-0.1.intermediates/myapp-0.1.xcarchive",
250 | "-destination",
251 | "generic/platform=iOS",
252 | "archive",
253 | "ENABLE_BITCODE=NO",
254 | "CODE_SIGNING_ALLOWED=NO"],
255 | cwd="/ios/dir/myapp-ios",
256 | env=mock.ANY,
257 | ),
258 | ]
259 |
--------------------------------------------------------------------------------
/tests/targets/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | from unittest import mock
4 |
5 | import buildozer as buildozer_module
6 | from buildozer import Buildozer
7 |
8 |
9 | def patch_buildops_cmd():
10 | return mock.patch("buildozer.buildops.cmd")
11 |
12 |
13 | def patch_buildops_checkbin():
14 | return mock.patch("buildozer.buildops.checkbin")
15 |
16 |
17 | def patch_buildops_file_exists():
18 | return mock.patch("buildozer.buildops.file_exists")
19 |
20 |
21 | def patch_logger_error():
22 | return mock.patch("buildozer.logger.Logger.error")
23 |
24 |
25 | def default_specfile_path():
26 | return os.path.join(os.path.dirname(buildozer_module.__file__), "default.spec")
27 |
28 |
29 | def init_buildozer(temp_dir, target, options=None):
30 | """
31 | Create a buildozer.spec file in the temporary directory and init the
32 | Buildozer instance.
33 |
34 | The optional argument can be used to overwrite the config options in
35 | the buildozer.spec file, e.g.:
36 |
37 | init_buildozer({'title': 'Test App'})
38 |
39 | will replace line 4 of the default spec file.
40 | """
41 | if options is None:
42 | options = {}
43 |
44 | spec_path = os.path.join(temp_dir.name, "buildozer.spec")
45 |
46 | with open(default_specfile_path()) as f:
47 | default_spec = f.readlines()
48 |
49 | spec = []
50 | for line in default_spec:
51 | if line.strip():
52 | match = re.search(r"[#\s]?([0-9a-z_.]+)", line)
53 | key = match and match.group(1)
54 | if key in options:
55 | line = "{} = {}\n".format(key, options[key])
56 |
57 | spec.append(line)
58 |
59 | with open(spec_path, "w") as f:
60 | f.writelines(spec)
61 |
62 | return Buildozer(filename=spec_path, target=target)
63 |
--------------------------------------------------------------------------------
/tests/test_buildozer.py:
--------------------------------------------------------------------------------
1 | import re
2 | import os
3 | import codecs
4 | import unittest
5 | import buildozer as buildozer_module
6 | from buildozer import Buildozer
7 | from io import StringIO
8 | from sys import platform
9 | import tempfile
10 | from unittest import mock
11 |
12 | from buildozer.targets.android import (
13 | TargetAndroid, DEFAULT_ANDROID_NDK_VERSION, MSG_P4A_RECOMMENDED_NDK_ERROR
14 | )
15 |
16 |
17 | class TestBuildozer(unittest.TestCase):
18 |
19 | def setUp(self):
20 | """
21 | Creates a temporary spec file containing the content of the default.spec.
22 | """
23 | self.specfile = tempfile.NamedTemporaryFile(suffix='.spec', delete=False)
24 | self.specfilename = self.specfile.name
25 | default_spec = codecs.open(self.default_specfile_path(), encoding='utf-8')
26 | self.specfile.write(default_spec.read().encode('utf-8'))
27 | self.specfile.close()
28 |
29 | def tearDown(self):
30 | """
31 | Deletes the temporary spec file.
32 | """
33 | os.unlink(self.specfile.name)
34 |
35 | @staticmethod
36 | def default_specfile_path():
37 | return os.path.join(
38 | os.path.dirname(buildozer_module.__file__),
39 | 'default.spec')
40 |
41 | @staticmethod
42 | def file_re_sub(filepath, pattern, replace):
43 | """
44 | Helper method for inplace file regex editing.
45 | """
46 | with open(filepath) as f:
47 | file_content = f.read()
48 | file_content = re.sub(pattern, replace, file_content)
49 | with open(filepath, 'w') as f:
50 | f.write(file_content)
51 |
52 | @classmethod
53 | def set_specfile_log_level(cls, specfilename, log_level):
54 | """
55 | Helper method for setting `log_level` in a given `specfilename`.
56 | """
57 | pattern = 'log_level = [0-9]'
58 | replace = 'log_level = {}'.format(log_level)
59 | cls.file_re_sub(specfilename, pattern, replace)
60 | buildozer = Buildozer(specfilename)
61 | assert buildozer.logger.log_level == log_level
62 |
63 | def test_buildozer_base(self):
64 | """
65 | Basic test making sure the Buildozer object can be instantiated.
66 | """
67 | buildozer = Buildozer()
68 | assert buildozer.specfilename == 'buildozer.spec'
69 | # spec file doesn't have to exist
70 | assert os.path.exists(buildozer.specfilename) is False
71 |
72 | def test_buildozer_read_spec(self):
73 | """
74 | Initializes Buildozer object from existing spec file.
75 | """
76 | buildozer = Buildozer(filename=self.default_specfile_path())
77 | assert os.path.exists(buildozer.specfilename) is True
78 |
79 | def test_buildozer_help(self):
80 | """
81 | Makes sure the help gets display with no error, refs:
82 | https://github.com/kivy/buildozer/issues/813
83 | """
84 | buildozer = Buildozer()
85 | with mock.patch('sys.stdout', new_callable=StringIO) as mock_stdout:
86 | buildozer.usage()
87 | assert 'Usage:' in mock_stdout.getvalue()
88 |
89 | def test_log_get_set(self):
90 | """
91 | Tests reading and setting log level from spec file.
92 | """
93 | # the default log level value is known
94 | buildozer = Buildozer('does_not_exist.spec')
95 | assert buildozer.logger.log_level == 2
96 | # sets log level to 1 on the spec file
97 | self.set_specfile_log_level(self.specfile.name, 1)
98 | buildozer = Buildozer(self.specfile.name)
99 | assert buildozer.logger.log_level == 1
100 |
101 | def test_run_command_unknown(self):
102 | """
103 | Makes sure the unknown command/target is handled gracefully, refs:
104 | https://github.com/kivy/buildozer/issues/812
105 | """
106 | buildozer = Buildozer()
107 | command = 'foobar'
108 | args = [command, 'debug']
109 | with mock.patch('sys.stdout', new_callable=StringIO) as mock_stdout:
110 | with self.assertRaises(SystemExit):
111 | buildozer.run_command(args)
112 | assert mock_stdout.getvalue() == 'Unknown command/target {}\n'.format(command)
113 |
114 | @unittest.skipIf(
115 | platform == "win32",
116 | "Test can't handle when resulting path is normalised on Windows")
117 | def test_android_ant_path(self):
118 | """
119 | Verify that the selected ANT path is being used from the spec file
120 | """
121 | my_ant_path = '/my/ant/path'
122 |
123 | buildozer = Buildozer(filename=self.default_specfile_path(), target='android')
124 | buildozer.config.set('app', 'android.ant_path', my_ant_path) # Set ANT path
125 | target = TargetAndroid(buildozer=buildozer)
126 |
127 | # Mock first run
128 | with mock.patch('buildozer.buildops.download') as download, \
129 | mock.patch('buildozer.buildops.file_extract') as m_file_extract, \
130 | mock.patch('os.makedirs'):
131 | ant_path = target._install_apache_ant()
132 | assert m_file_extract.call_args_list == [
133 | mock.call(mock.ANY, cwd='/my/ant/path', env=mock.ANY)]
134 | assert ant_path == my_ant_path
135 | assert download.call_args_list == [
136 | mock.call("https://archive.apache.org/dist/ant/binaries/", mock.ANY, cwd=my_ant_path)]
137 | # Mock ant already installed
138 | with mock.patch('buildozer.buildops.file_exists', return_value=True):
139 | ant_path = target._install_apache_ant()
140 | assert ant_path == my_ant_path
141 |
142 | def test_p4a_recommended_ndk_version_default_value(self):
143 | self.set_specfile_log_level(self.specfile.name, 1)
144 | buildozer = Buildozer(self.specfile.name, 'android')
145 | assert buildozer.target.p4a_recommended_ndk_version is None
146 |
147 | def test_p4a_recommended_android_ndk_error(self):
148 | self.set_specfile_log_level(self.specfile.name, 1)
149 | buildozer = Buildozer(self.specfile.name, 'android')
150 |
151 | with mock.patch('sys.stdout', new_callable=StringIO) as mock_stdout:
152 | ndk_version = buildozer.target.p4a_recommended_android_ndk
153 | assert MSG_P4A_RECOMMENDED_NDK_ERROR in mock_stdout.getvalue()
154 | # and we should get the default android's ndk version of buildozer
155 | assert ndk_version == DEFAULT_ANDROID_NDK_VERSION
156 |
157 | @mock.patch('buildozer.targets.android.os.path.isfile')
158 | @mock.patch('buildozer.targets.android.os.path.exists')
159 | @mock.patch('buildozer.targets.android.open', create=True)
160 | def test_p4a_recommended_android_ndk_found(
161 | self, mock_open, mock_exists, mock_isfile
162 | ):
163 | self.set_specfile_log_level(self.specfile.name, 1)
164 | buildozer = Buildozer(self.specfile.name, 'android')
165 | expected_ndk = '19b'
166 | recommended_line = 'RECOMMENDED_NDK_VERSION = {expected_ndk}\n'.format(
167 | expected_ndk=expected_ndk)
168 | mock_open.return_value = StringIO(recommended_line)
169 | ndk_version = buildozer.target.p4a_recommended_android_ndk
170 | p4a_dir = os.path.join(
171 | buildozer.platform_dir, buildozer.target.p4a_directory_name)
172 | mock_open.assert_called_once_with(
173 | os.path.join(p4a_dir, "pythonforandroid", "recommendations.py"), 'r'
174 | )
175 | assert ndk_version == expected_ndk
176 |
177 | # now test that we only read one time p4a file, so we call again to
178 | # `p4a_recommended_android_ndk` and we should still have one call to `open`
179 | # file, the performed above
180 | ndk_version = buildozer.target.p4a_recommended_android_ndk
181 | mock_open.assert_called_once()
182 |
--------------------------------------------------------------------------------
/tests/test_logger.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from buildozer.logger import Logger
3 |
4 | from io import StringIO
5 | from unittest import mock
6 |
7 |
8 | class TestLogger(unittest.TestCase):
9 | def test_log_print(self):
10 | """
11 | Checks logger prints different info depending on log level.
12 | """
13 | logger = Logger()
14 |
15 | # Test ERROR Level
16 | Logger.set_level(0)
17 | assert logger.log_level == logger.ERROR
18 |
19 | # at this level, only error messages should be printed
20 | with mock.patch("sys.stdout", new_callable=StringIO) as mock_stdout:
21 | logger.debug("debug message")
22 | logger.info("info message")
23 | logger.error("error message")
24 | # using `in` keyword rather than `==` because of color prefix/suffix
25 | assert "debug message" not in mock_stdout.getvalue()
26 | assert "info message" not in mock_stdout.getvalue()
27 | assert "error message" in mock_stdout.getvalue()
28 |
29 | # Test INFO Level
30 | Logger.set_level(1)
31 | assert logger.log_level == logger.INFO
32 |
33 | # at this level, debug messages should not be printed
34 | with mock.patch("sys.stdout", new_callable=StringIO) as mock_stdout:
35 | logger.debug("debug message")
36 | logger.info("info message")
37 | logger.error("error message")
38 | # using `in` keyword rather than `==` because of color prefix/suffix
39 | assert "debug message" not in mock_stdout.getvalue()
40 | assert "info message" in mock_stdout.getvalue()
41 | assert "error message" in mock_stdout.getvalue()
42 |
43 | # sets log level to 2 in the spec file
44 | Logger.set_level(2)
45 | assert logger.log_level == logger.DEBUG
46 | # at this level all message types should be printed
47 | with mock.patch("sys.stdout", new_callable=StringIO) as mock_stdout:
48 | logger.debug("debug message")
49 | logger.info("info message")
50 | logger.error("error message")
51 | assert "debug message" in mock_stdout.getvalue()
52 | assert "info message" in mock_stdout.getvalue()
53 | assert "error message" in mock_stdout.getvalue()
54 |
--------------------------------------------------------------------------------
/tests/test_specparser.py:
--------------------------------------------------------------------------------
1 | from os import environ
2 | from pathlib import Path
3 | from tempfile import TemporaryDirectory
4 | import unittest
5 |
6 | from buildozer.specparser import SpecParser
7 |
8 |
9 | class TestSpecParser(unittest.TestCase):
10 | def test_overrides(self):
11 | environ["SECTION_1_ATTRIBUTE_1"] = "Env Value"
12 |
13 | # Test as a string.
14 | sp = SpecParser()
15 | sp.read_string(
16 | """
17 | [section.1]
18 | attribute.1=String Value
19 | """
20 | )
21 | assert sp.get("section.1", "attribute.1") == "Env Value"
22 |
23 | # Test as a dict
24 | sp = SpecParser()
25 | sp.read_dict({"section.1": {"attribute.1": "Dict Value"}})
26 | assert sp.get("section.1", "attribute.1") == "Env Value"
27 |
28 | with TemporaryDirectory() as temp_dir:
29 | spec_path = Path(temp_dir) / "test.spec"
30 | with open(spec_path, "w") as spec_file:
31 | spec_file.write(
32 | """
33 | [section.1]
34 | attribute.1=File Value
35 | """
36 | )
37 |
38 | # Test as a file
39 | sp = SpecParser()
40 | with open(spec_path, "r") as spec_file:
41 | sp.read_file(spec_file)
42 | assert sp.get("section.1", "attribute.1") == "Env Value"
43 |
44 | # Test as a list of filenames
45 | sp = SpecParser()
46 | sp.read([spec_path])
47 | assert sp.get("section.1", "attribute.1") == "Env Value"
48 |
49 | del environ["SECTION_1_ATTRIBUTE_1"]
50 |
51 | def test_new_getters(self):
52 | sp = SpecParser()
53 | sp.read_string(
54 | """
55 | [section1]
56 | attribute1=1
57 | attribute2=red, white, blue
58 | attribute3=True
59 | attribute5=large/medium/small
60 |
61 | [section2:attribute4]
62 | red=1
63 | amber=
64 | green=3
65 |
66 |
67 | """
68 | )
69 |
70 | assert sp.get("section1", "attribute1") == "1"
71 | assert sp.getlist("section1", "attribute2") == ["red", "white", "blue"]
72 | assert sp.getlist("section1", "attribute2", strip=False) == [
73 | "red",
74 | " white",
75 | " blue",
76 | ]
77 |
78 | assert sp.getlist("section2", "attribute4") == [
79 | "red",
80 | "amber",
81 | "green",
82 | ]
83 | # Test with_values and section_sep
84 | assert sp.getlistvalues("section2", "attribute4") == [
85 | "red=1",
86 | "amber=",
87 | "green=3",
88 | ]
89 | assert sp.getlist(
90 | "section2", "attribute4", with_values=True, section_sep=":"
91 | ) == [
92 | "red:1",
93 | "amber:",
94 | "green:3",
95 | ]
96 | # Test split_char
97 | assert sp.getlist("section1", "attribute5", with_values=True) == [
98 | "large/medium/small",
99 | ]
100 | assert sp.getlist(
101 | "section1", "attribute5", with_values=True, split_char="/"
102 | ) == [
103 | "large",
104 | "medium",
105 | "small",
106 | ]
107 |
108 | assert sp.getbooldefault("section1", "attribute3") is True
109 |
110 | def test_case_sensitivity(self):
111 | sp = SpecParser()
112 | sp.read_string(
113 | """
114 | [section1]
115 | attribute1=a
116 | Attribute1=A
117 | """
118 | )
119 |
120 | assert sp.get("section1", "attribute1") == "a"
121 | assert sp.get("section1", "Attribute1") == "A"
122 |
123 | def test_profiles(self):
124 | sp = SpecParser()
125 | sp.read_string(
126 | """
127 | [section1]
128 | attribute1=full system
129 | [section1 @demo1, demo2]
130 | attribute1=demo mode
131 | """
132 | )
133 |
134 | # Before a profile is set, return the basic version.
135 | assert sp.get("section1", "attribute1") == "full system"
136 |
137 | # Empty profile makes no difference.
138 | sp.apply_profile(None)
139 | assert sp.get("section1", "attribute1") == "full system"
140 |
141 | # Inapplicable profile makes no difference
142 | sp.apply_profile("doesn't exist")
143 | assert sp.get("section1", "attribute1") == "full system"
144 |
145 | # Applicable profile changes value
146 | sp.apply_profile("demo2")
147 | assert sp.get("section1", "attribute1") == "demo mode"
148 |
149 | def test_profiles_vs_env_var(self):
150 | sp = SpecParser()
151 |
152 | environ["SECTION1_ATTRIBUTE1"] = "simulation mode"
153 |
154 | sp.read_string(
155 | """
156 | [section1]
157 | attribute1=full system
158 | [section1@demo1,demo2]
159 | attribute1=demo mode
160 | """
161 | )
162 |
163 | # Before a profile is set, env var should win.
164 | assert sp.get("section1", "attribute1") == "simulation mode"
165 |
166 | # Applicable profile: env var should still win
167 | sp.apply_profile("demo1")
168 | assert sp.get("section1", "attribute1") == "simulation mode"
169 |
170 | del environ["SECTION1_ATTRIBUTE1"]
171 |
172 | def test_controversial_cases(self):
173 | """Some aspects of the config syntax seem to cause confusion.
174 | This shows what the code is *specified* to do, which might not be
175 | expected.
176 | """
177 | sp = SpecParser()
178 | sp.read_string(
179 | """
180 | [section]
181 | # Comments can be indented.
182 | option1=a # This is not considered a comment
183 | option2=this is
184 | a multiline string (not a list!)
185 | # This is considered a comment.
186 | this_is_not_an_option=it is still part of the multiline
187 | option3=this, is, one, way, of, representing, lists
188 |
189 | [section:option4]
190 | this_is
191 | another_way
192 | # This is a comment.
193 | of # This is not a comment.
194 | representing=4
195 | lists
196 | """
197 | )
198 |
199 | assert (
200 | sp.get("section", "option1") ==
201 | "a # This is not considered a comment"
202 | )
203 | assert (
204 | sp.get("section", "option2") ==
205 | "this is\na multiline string (not a list!)\n"
206 | "this_is_not_an_option=it is still part of the multiline"
207 | )
208 | assert sp.getlist("section", "option3") == [
209 | "this",
210 | "is",
211 | "one",
212 | "way",
213 | "of",
214 | "representing",
215 | "lists",
216 | ]
217 | assert sp.getlist("section", "option4") == [
218 | "this_is",
219 | "another_way",
220 | "of # This is not a comment.",
221 | "representing",
222 | "lists",
223 | ]
224 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = pep8
3 |
4 | [testenv]
5 | deps =
6 | pytest
7 | coverage
8 | commands =
9 | pytest tests/
10 | coverage run --branch --source=buildozer -m pytest {posargs:tests/}
11 | coverage report -m
12 |
13 | [testenv:pep8]
14 | deps = flake8
15 | commands = flake8 buildozer/ tests/
16 |
17 | [flake8]
18 | ignore =
19 | # continuation line missing indentation or outdented
20 | E122,
21 | # continuation line over-indented for hanging indent
22 | E126,
23 | # continuation line over-indented for visual indent
24 | E127,
25 | # continuation line under-indented for visual indent
26 | E128,
27 | # continuation line unaligned for hanging indent
28 | E131,
29 | # module level import not at top of file
30 | E402,
31 | # line too long
32 | E501,
33 | # do not use bare 'except'
34 | E722,
35 | # line break before binary operator
36 | W503,
37 | # line break after binary operator
38 | W504
39 |
--------------------------------------------------------------------------------