Compare commits

...

100 commits

Author SHA1 Message Date
4f50d277da
enable standard zip ("deflate") compression
This saves ~50% on the main bundle and will save ~85% on the fonts bundle
2023-12-19 11:23:53 -06:00
7dadcc70aa
ensure tags are present
this should fix the message during CI
```
.../setuptools_scm/git.py:163: UserWarning: "/home/runner/work/circuitpython-build-tools/circuitpython-build-tools" is shallow and may cause errors
  warnings.warn(f'"{wd.path}" is shallow and may cause errors')
```

This may have been introduced when upgrading action/checkout recently,
or it might be pre-existing.
2023-12-17 08:59:45 -06:00
6a4079769f
string vs number 2023-12-17 08:12:48 -06:00
e896099f09
bump minimum python version to 3.10
.. this is what is used during the build of the adafruit bundle right now.
2023-12-17 08:10:45 -06:00
87ba4b679b
we now recommend github actions as the CI system of choice 2023-12-17 08:04:05 -06:00
b49cd8413a
Merge pull request #105 from 2bndy5/replace-pkg_resources
addresses #103
2023-12-17 07:59:47 -06:00
Brendan
1e64df5969 remove travis CI specific code 2023-12-16 20:02:27 -08:00
Brendan
4d4844451c upgrade github actions to latest major 2023-12-15 21:32:16 -08:00
Brendan
7cd8379d01 addresses #103 2023-12-15 21:32:16 -08:00
Dan Halbert
bcb5427418
Merge pull request #104 from jepler/require-tomli-setup.py
require tomli at install time
2023-12-14 14:52:31 -05:00
951b47c132
require tomli at install time 2023-12-14 10:58:14 -06:00
Dan Halbert
9f02e3a1c9
Merge pull request #102 from adafruit/correct-naming
Change terminology
2023-12-05 10:08:59 -05:00
Dan Halbert
f9a8aa3b1a
Change remaining blacklist to blocklist 2023-12-05 09:55:11 -05:00
0032a80387
Change terminology 2023-12-05 08:02:34 -06:00
Dan Halbert
988ed9803f
Merge pull request #101 from adafruit/include-binary-files-in-package
Get package name from pyproject.toml, allow arbitrary files in packages
2023-12-02 23:19:58 -05:00
b51d905d9d
blacklist should let this pass CI 2023-12-02 15:23:07 -06:00
373b256441
typo 2023-12-02 15:23:07 -06:00
ca779868d8
pyproject is the name of the file 2023-11-30 10:34:45 -06:00
0c58704d65
Restore behavior of shipping 0-byte py files as .py, not .mpy
(it's smaller on disk)
2023-11-30 09:11:58 -06:00
d8719e4d04
Get package name from settings.toml, allow arbitrary files in packages
This is more dependable, and when we know the package name we can
glob inside it to get all files such as bin or ttf files.

This will allow e.g., 5x8.bin & ov5640_autofocus.bin within
bundles.

the behavior of bundlefly and circup when encountering .bin files
needs to be checked.

Tested by building modified pycamera bundle and the autofocus.bin file
appears in the generated zip files:
```
pycamera-py-ec67bde/lib/adafruit_pycamera/ov5640_autofocus.bin 4077 4096
pycamera-8.x-mpy-ec67bde/lib/adafruit_pycamera/ov5640_autofocus.bin 4077 4096
pycamera-9.x-mpy-ec67bde/lib/adafruit_pycamera/ov5640_autofocus.bin 4077 4096
```

There's at least one library in the bundle that has incorrect metadata
and that leads to an error:
    https://github.com/adafruit/Adafruit_CircuitPython_Colorsys/pull/29
2023-11-30 09:06:27 -06:00
Scott Shawcroft
34c259dfe9
Merge pull request #100 from FoamyGuy/include_pyproject_toml
include pyproject.toml file in the bundle if it exists.
2023-10-31 12:16:29 -07:00
foamyguy
86b60039f2 include pyproject.toml file in the bundle if it exists. 2023-10-30 16:32:50 -05:00
Dan Halbert
20b08fbd76
Merge pull request #99 from adafruit/tannewt-patch-8
Add 9.x bundles
2023-10-27 15:16:20 -04:00
Dan Halbert
653f4e9e50
Use 8.2.0 mpy-cross instead of 8.0.0
Its pathname will match the 9.0.0 name.
2023-10-27 14:58:29 -04:00
Scott Shawcroft
af26b78ea9
Update mpy-cross locations 2023-10-27 10:11:11 -07:00
Scott Shawcroft
4d5881b8c3
Add 9.x bundles 2023-10-27 09:34:40 -07:00
Scott Shawcroft
efd487a89a
Merge pull request #98 from adafruit/tannewt-patch-8
Stop building bundles for 7.x
2023-10-03 09:41:52 -07:00
Scott Shawcroft
39377e3e2b
Stop building bundles for 7.x 2023-10-02 12:07:30 -07:00
Alec Delaney
bc2d2a0c7d
Merge pull request #97 from tekktrik/dev/bundle-build-ignore
Allow bundles to be skipped
2023-07-27 21:14:11 -04:00
Tekktrik
123186b725 Use click.Choice 2023-07-27 20:58:13 -04:00
Tekktrik
fa235526a8 Allow bundles to be skipped 2023-07-10 21:50:06 -04:00
Scott Shawcroft
312d8d090e
Merge pull request #96 from FoamyGuy/remote_name_arg
--remote_name argument
2023-06-28 09:04:04 -07:00
foamyguy
0b44929f49 remote name argument 2023-06-27 19:12:21 -05:00
Dan Halbert
565482692b
Merge pull request #95 from adafruit/dhalbert-patch-1
Update to use mpy-cross-8.0.0 instead of 8.0.0-alpha.1
2023-02-16 14:54:41 -05:00
Dan Halbert
d10761a18f
Update to use mpy-cross-8.0.0 instead of 8.0.0-alpha.1 2023-02-16 14:23:35 -05:00
Dan Halbert
0a2ac582ee
Merge pull request #93 from Neradoc/dont-normalize-names
Fix listing dependencies in json
2022-08-19 07:36:35 -04:00
Neradoc
ad4908adbf add spidev to skipped pypi modules 2022-08-18 16:17:02 +02:00
Neradoc
795e3d2ff9 change normalize_dist_name to get back the bundle libraries 2022-08-18 15:55:22 +02:00
Alec Delaney
687af8c22b
Merge pull request #92 from tekktrik/dev/change-version-string
Update version string
2022-08-16 12:44:29 -04:00
Alec Delaney
c3cf652f31
Change both version strings
Doesn't require a patch to be done simultaneously
2022-08-16 12:42:51 -04:00
Alec Delaney
26375aaa16 Update version string 2022-08-15 20:47:21 -04:00
Melissa LeBlanc-Williams
97895d21a3
Merge pull request #90 from adafruit/ignore-more-package-names
build_bundles: Ignore more packages that exist only on pypi
2022-07-18 13:02:43 -07:00
38931db45e
Don't ignore busdevice
This is a weird one since some devices have it in core now, and others don't.

This happened in part because I copypasted the list currently in pipkin instead of tweaking the existing list, which was initially added with this module in the list: b9957133a9
2022-07-18 14:41:15 -05:00
9fb6ffb1ea
build_bundles: Ignore more packages that exist only on pypi
…and sort the existing list and use 'normalized' names

The implementation of normalize_dist_name is taken from pipkin.
2022-07-18 12:55:47 -05:00
foamyguy
3552198336
Merge pull request #88 from Neradoc/fix-single-file-libraries-mpy
Fix the single file libraries being copied as source.
2022-06-18 15:34:20 -05:00
Neradoc
a0e40b58da Fix the single file libraries being copied as source.
The code generates a temp file with the library version.
By mistake that was ALWAYS copied back to the mpy file for single file libraries.
The temporary files for the package libraries were also not deleted.

For windows compatibility, the temporary file must be copied (and therefore erased) outside of the with block. In this PR we only keep the temporary file open the time to write to it, and then do the rest.
2022-06-17 23:47:04 +02:00
Scott Shawcroft
2b255aa47d
Merge pull request #87 from Neradoc/add-cp8-to-bundles
Generate Circuitpython 8.x bundle
2022-06-14 09:23:52 -07:00
Neradoc
d98fb9082c Generate Circuitpython 8.X bundle 2022-06-14 16:26:04 +02:00
Dan Halbert
8c66f80024
Merge pull request #86 from Neradoc/patch-1
Bump the mpy-cross version to 7.3.0
2022-06-14 09:37:12 -04:00
Neradoc
f91e0108a4
Bump the mpy-cross version for fixes to the language
In Micropython 18 (and CP 7.3.0) there are some language fixes to previous syntax errors in f-strings, and maybe others.
A string like this would not compile prior to 7.3.0:
```py
string = "123456789"
print(f"{string[2:6]}")
```
Note that an MPY file with that code generated with mpy-cross 7.3.0 runs properly on 7.0.0 according to my tests.
2022-06-14 15:20:54 +02:00
Jeff Epler
de44a709f6
Merge pull request #84 from 2bndy5/make-windows-compatible
Windows compatibility
2022-01-26 08:26:28 -06:00
2bndy5
a55a92367e self reviewed 2022-01-25 17:19:03 -08:00
2bndy5
bac3e8056f fix url for win x64 pre-built mpy-cross 2022-01-25 14:57:42 -08:00
2bndy5
2d53879c1a solution for #75 2022-01-21 14:29:44 -08:00
Dan Halbert
7da9c1d789
Merge pull request #83 from adafruit/tannewt-patch-8
Stop supporting 6.x in the bundles
2021-12-14 08:27:40 -05:00
Scott Shawcroft
ec2e342ef2
Stop supporting 6.x
Libraries are being updated for 7.0.0 only which has been out since September 20th, 2021.

I've updated the FAQ with the last bundle: https://learn.adafruit.com/welcome-to-circuitpython/frequently-asked-questions
2021-12-13 15:05:19 -08:00
Dan Halbert
80489a6142
Merge pull request #81 from jepler/update-7.0.0-final
update tag for 7.x
2021-10-08 23:18:57 -04:00
35880f474b update tag for 7.x 2021-10-08 21:55:52 -05:00
Scott Shawcroft
cde80f6c85
Merge pull request #73 from lesamouraipourpre/github-actions
Convert from Travis to Github Actions
2021-09-14 11:22:21 -07:00
James Carr
734890c799
Merge branch 'main' of github.com:adafruit/circuitpython-build-tools into github-actions 2021-09-13 20:29:48 +01:00
Scott Shawcroft
1b3ea7d630
Merge pull request #77 from FoamyGuy/circuitpython_org_pypi_name
fix pypi names for circuitpython org libraries
2021-09-13 12:10:33 -07:00
foamyguy
3845cee485 fix pypi names for circuitpython org libraries 2021-09-11 11:43:09 -05:00
Scott Shawcroft
bdcdafdb90
Merge pull request #76 from lesamouraipourpre/patch-1
Update target_versions.py to the latest versions
2021-06-08 13:58:42 -07:00
James Carr
baec9067b8
Update target_versions.py to the latest versions 2021-06-07 23:59:03 +01:00
Jeff Epler
4d055bb535
Merge pull request #72 from lesamouraipourpre/aws-mpy-cross
Use mpy-cross from S3 if possible
2021-05-30 12:19:00 -06:00
James Carr
eadbc29632
Add 'if not quiet' to the print statements 2021-05-30 15:45:33 +01:00
James Carr
853dc57e87
Update Python in Travis to 3.7 2021-05-30 13:05:45 +01:00
James Carr
743c550128
Convert from Travis to Github Actions (Closes #59)
BREAKING CHANGES
This REQUIRES releases to generate the artifacts not just tags.
This DOES NOT generate the data directory containing mpy-cross versions.
2021-05-30 12:50:51 +01:00
James Carr
385cbc607d
Add author info 2021-05-30 12:42:37 +01:00
James Carr
98b12389a8
Replace tab with spaces 2021-05-30 11:27:09 +01:00
James Carr
f69bc7b660
Add basic exception handling around the S3 request 2021-05-30 11:24:43 +01:00
James Carr
39b1fe192f
Add Linux/x86_64 to mpy-cross S3 check 2021-05-30 00:28:56 +01:00
James Carr
c9a7d6323e
Update Python requirement to 3.7 2021-05-30 00:27:06 +01:00
James Carr
385b448490
Merge branch 'aws-mpy-cross' of github.com:lesamouraipourpre/circuitpython-build-tools into aws-mpy-cross 2021-05-29 20:10:39 +01:00
James Carr
f4532c667c
Add macos support to S3 mpy-cross 2021-05-29 20:08:03 +01:00
James Carr
1b45018a49
Update setup.py to install requests 2021-05-29 20:08:03 +01:00
James Carr
4c8cf86ebb
Pull mpy-cross from AWS S3 instead of building. (Closes #55)
Fall back to building if fetching fails.
2021-05-29 20:07:11 +01:00
James Carr
4aafea594f
Update setup.py to install requests 2021-05-29 18:35:56 +01:00
James Carr
03fd236dd4
Pull mpy-cross from AWS S3 instead of building.
Fall back to building if fetching fails.
2021-05-29 18:25:49 +01:00
Jeff Epler
716a38c713
Merge pull request #71 from adafruit/tannewt-patch-7
Update 7.x tag for the (hopefully) final MPY version
2021-05-10 17:43:00 -05:00
Scott Shawcroft
59651e993f
Update 7.x tag for the (hopefully) final MPY version 2021-05-10 15:13:02 -07:00
Melissa LeBlanc-Williams
f8bbac8d81
Merge pull request #69 from makermelissa/master
Fix PyPI names in json when .git is missing
2021-04-07 13:46:16 -07:00
Melissa LeBlanc-Williams
e9ba66eb93 Fix PyPI names in json when .git is missing 2021-04-07 13:41:15 -07:00
Kattni
9a523be681
Merge pull request #68 from kattni/update-versions
Update to 6.2.0 and add 7.
2021-04-07 16:14:47 -04:00
Kattni Rembor
28072e3995 Update to 6.2.0 and add 7. 2021-04-07 16:04:41 -04:00
Dan Halbert
7b0b16d2be
Merge pull request #66 from makermelissa/master
Bundle JSON improvements
2021-03-31 13:07:13 -04:00
Melissa LeBlanc-Williams
336d6b9a53 Changed LINUX LIBS to BLINKA LIBS and added a couple more 2021-03-31 09:59:15 -07:00
Melissa LeBlanc-Williams
243a717ff4 Added PyPI Name for external package matching 2021-03-31 09:31:16 -07:00
Melissa LeBlanc-Williams
3bb57a8c58 Remove quotes used for testing 2021-03-31 09:27:59 -07:00
Melissa LeBlanc-Williams
6866d2609a Bundle JSON improvements 2021-03-31 09:23:13 -07:00
Melissa LeBlanc-Williams
248bc56a4a
Merge pull request #65 from makermelissa/master
Add library versions to JSON
2021-03-24 16:24:32 -07:00
Melissa LeBlanc-Williams
ffa8fe9324 Add library versions to JSON 2021-03-24 16:17:53 -07:00
Melissa LeBlanc-Williams
3b529b4dfb
Merge pull request #64 from makermelissa/master 2021-03-23 20:22:42 -07:00
Melissa LeBlanc-Williams
e457d835c0 Fix some packages were incorrectly detected 2021-03-23 15:43:29 -07:00
Melissa LeBlanc-Williams
9d9d4ef222
Merge pull request #63 from makermelissa/master
Added JSON generator for bundle
2021-03-23 12:01:42 -07:00
Melissa LeBlanc-Williams
85a47fc9c1 Added JSON generator for bundle 2021-03-23 11:53:31 -07:00
Jeff Epler
91a63134a4
Merge pull request #62 from Neradoc/fix-missing-requirements
Fix requirements missing for single file modules
2021-03-05 12:58:06 -06:00
Neradoc
ddbd38f9a3 fix requirements for single file modules (fixes identifying single file modules) 2021-03-05 06:33:57 +01:00
Jeff Epler
00468b1fc3
Merge pull request #60 from jepler/include-requirements-in-bundle
library: Include requirements.txt metadata
2021-02-11 08:17:30 -06:00
Jeff Epler
d34e5226e7 library: Include requirements.txt metadata
It is hoped that this data will be useful to circup, enabling it to find
dependency information without phoning out to github.
2021-02-09 11:58:33 -06:00
10 changed files with 456 additions and 201 deletions

55
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,55 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: 2021 James Carr
#
# SPDX-License-Identifier: MIT
name: Build CI
on: [pull_request, push]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Versions
run: |
python3 --version
- name: Checkout Current Repo
uses: actions/checkout@v4
with:
filter: 'blob:none'
depth: 0
- name: Install requirements
run: |
sudo apt-get update
sudo apt-get install libudev-dev libusb-1.0
sudo apt-get install -y gettext
pip install -r requirements.txt
- name: Library version
run: git describe --dirty --always --tags
- name: Install package locally
run: pip install -e .
- name: Test building single package
run: |
git clone https://github.com/adafruit/Adafruit_CircuitPython_FeatherWing.git
cd Adafruit_CircuitPython_FeatherWing
circuitpython-build-bundles --filename_prefix test-single --library_location .
- name: Test building bundle
run: |
# Use the community bundle because it's smaller and faster
git clone --recurse-submodules https://github.com/adafruit/CircuitPython_Community_Bundle.git
cd CircuitPython_Community_Bundle
circuitpython-build-bundles --filename_prefix test-bundle --library_location libraries --library_depth 2
- name: Build Python package
run: |
pip install --upgrade setuptools wheel twine readme_renderer testresources
python setup.py sdist
twine check dist/*

34
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: 2021 James Carr
#
# SPDX-License-Identifier: MIT
name: Release Actions
on:
release:
types: [published]
jobs:
upload-pypi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
filter: 'blob:none'
depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.pypi_username }}
TWINE_PASSWORD: ${{ secrets.pypi_password }}
run: |
python setup.py sdist
twine upload dist/*

2
.gitignore vendored
View file

@ -8,3 +8,5 @@ circuitpython_build_tools/data/
.eggs
version.py
.env/*
.DS_Store
.idea/*

View file

@ -1,50 +0,0 @@
dist: xenial
language: python
python:
- '3.6'
stages:
- name: Tests
if: type = pull_request
- name: deploy
if: tag IS present
jobs:
include:
- stage: Tests
name: "Test CircuitPython Bundle"
python: "3.6"
script:
- echo "Building mpy-cross" && echo "travis_fold:start:mpy-cross"
- python3 -u -m circuitpython_build_tools.scripts.build_mpy_cross circuitpython_build_tools/data/
- echo "travis_fold:end:mpy-cross"
- pip install -e .
- echo "Cloning Adafruit_CircuitPython_Bundle" && echo "travis_fold:start:clone"
- git clone --recurse-submodules https://github.com/adafruit/Adafruit_CircuitPython_Bundle.git
- echo "travis_fold:end:clone"
- cd Adafruit_CircuitPython_Bundle
- circuitpython-build-bundles --filename_prefix test-bundle --library_location libraries --library_depth 2
- stage: Tests
name: "Test Single Library Bundle"
script:
- echo "Building mpy-cross" && echo "travis_fold:start:mpy-cross"
- python3 -u -m circuitpython_build_tools.scripts.build_mpy_cross circuitpython_build_tools/data/
- echo "travis_fold:end:mpy-cross"
- pip install -e .
- git clone https://github.com/adafruit/Adafruit_CircuitPython_FeatherWing.git
- cd Adafruit_CircuitPython_FeatherWing
- circuitpython-build-bundles --filename_prefix test-single --library_location .
- stage: deploy
script:
- python3 -u -m circuitpython_build_tools.scripts.build_mpy_cross circuitpython_build_tools/data/
deploy:
provider: pypi
user: "adafruit-travis"
password:
secure: tCjXwD8YeitG0HZLxW1D1QlVv4Xbj8mfRoqW0CM9ikPp3KY1PCz6Axj0PiOcyVwKdnxcUQ0EGRl16wEqwkObrmo9MboYrPuPqN00ULmyCQCRvJa2abIN6jDoLtBuf6bcze88t0XY2LdMOcj2Udv5Iijgf95zUgE+Z6BqT9Rgche78JEOeANJ7BlAJ6nRCA4whDdG7J9s7SmFtIjKWtMxig2J3X+Qy0bZ+Armtfp9/CRvjLJ8juDrcCBSysWLnAYLS4u8e/rbSTh8YwFeoeJ1pp9qSmME5NuwScY18QmfESNSqz8wVVXtAFKdoMOCoN+/CodTxp9aB0QsXX6yOYg74ahDIaci239wgnuUqxSaeLxeSwWkkVCXWdQVuP4vgq3GZwm2yNOQ1ZjfFbXF156yv0uSVw5nuaxv0YblQTinJtL4x9hwOdPDJio3b6UT3H1ue9l1qK0LT2OSkzDgn12WmTnTfRUH3BkU6onsYsdP33PK1YhepeQnfbT1P3ikrRHIwGYb7XqcjOtJh413kid6YezCXRqccl8kAxegnqX+cQG7K9ilpZtWaVYLu4RRBJ37H4vpuOb3SV686Y62sWPUXEbI3MR1OxU+RrRr/9DCH1EFXnlYT9LF986wXFJtWuSc+pbXuxY7qduai0hn5Pft6XH31exyiOwAHBIFeYebnVM=
skip_cleanup: true
on:
tags: true

View file

@ -4,40 +4,20 @@
This repo contains build scripts used to build the
[Adafruit CircuitPython bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle), [CircuitPython Community bundle](https://github.com/adafruit/CircuitPython_Community_Bundle)
and individual library release zips. Its focused on Travis CI support but will also work locally
and individual library release zips. Its focused on Github Actions support but will also work locally
when a gcc compiler is present.
The pip package includes mpy-crosses that run on Travis. When building locally, the scripts will
The scripts will either fetch a pre-built mpy-cross from s3 or
automatically clone the [CircuitPython repo](https://github.com/adafruit/circuitpython) and attempt
to build mpy-crosses. You'll need some version of gcc for this to work.
to build mpy-cross. You'll need some version of gcc for this to work.
## Setting up libraries
These build tools are intended for use with [Travis CI](https://travis-ci.org)
to automatically build .mpy files and zip them up for CircuitPython when a new
tagged release is created. To add support to a repo you need to:
1. Use the [CircuitPython cookiecutter](https://github.com/adafruit/cookiecutter-adafruit-circuitpython) to generate .travis.yml.
2. For adafruit repositories, simply give the CircuitPythonLibrarians team
write access to the repo and Adabot will do the rest.
Otherwise, go to travis-ci.org and find the repository (it needs to be
setup to access your github account, and your github account needs access
to write to the repo). Flip the 'ON' switch on for Travis and the repo,
see the Travis docs for more details: https://docs.travis-ci.com/user/getting-started/
3. Get a GitHub 'personal access token' which has at least 'public_repo' or
'repo' scope: https://help.github.com/articles/creating-an-access-token-for-command-line-use/
Keep this token safe and secure! Anyone with the token will be able to
access and write to your GitHub repositories. Travis will use the token
to attach the .mpy files to the release.
4. In the Travis CI settings for the repository that was enabled find the
environment variable editing page: https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings
Add an environment variable named GITHUB_TOKEN and set it to the value
of the GitHub personal access token above. Keep 'Display value in build
log' flipped off.
5. That's it! Tag a release and Travis should go to work to add zipped .mpy files
to the release. It takes about a 2-3 minutes for a worker to spin up,
build mpy-cross, and add the binaries to the release.
These build tools automatically build .mpy files and zip them up for
CircuitPython when a new tagged release is created. To add support to a repo
you need to use the [CircuitPython
cookiecutter](https://github.com/adafruit/cookiecutter-adafruit-circuitpython)
to generate `.github/workflows/*.yml`.
The bundle build will produce one zip file for every major CircuitPython
release supported containing compatible mpy files and a zip with human readable py files.
@ -71,5 +51,5 @@ circuitpython-build-bundles --filename_prefix <output file prefix> --library_loc
## Contributing
Contributions are welcome! Please read our [Code of Conduct]
(https://github.com/adafruit/Adafruit_CircuitPython_adabot/blob/master/CODE_OF_CONDUCT.md)
(https://github.com/adafruit/Adafruit\_CircuitPython\_adabot/blob/master/CODE\_OF\_CONDUCT.md)
before contributing to help this project stay welcoming.

View file

@ -4,6 +4,7 @@
#
# Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
# 2018, 2019 Michael Schroeder
# 2021 James Carr
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@ -25,15 +26,59 @@
import os
import os.path
import platform
import pathlib
import requests
import semver
import shutil
import stat
import sys
import subprocess
import tempfile
# pyproject.toml `py_modules` values that are incorrect. These should all have PRs filed!
# and should be removed when the fixed version is incorporated in its respective bundle.
pyproject_py_modules_blocklist = set((
# adafruit bundle
"adafruit_colorsys",
# community bundle
"at24mac_eeprom",
"circuitpython_Candlesticks",
"CircuitPython_Color_Picker",
"CircuitPython_Equalizer",
"CircuitPython_Scales",
"circuitPython_Slider",
"circuitpython_uboxplot",
"P1AM",
"p1am_200_helpers",
))
if sys.version_info >= (3, 11):
from tomllib import loads as load_toml
else:
from tomli import loads as load_toml
def load_pyproject_toml(lib_path: pathlib.Path):
try:
return load_toml((lib_path / "pyproject.toml") .read_text(encoding="utf-8"))
except FileNotFoundError:
print(f"No pyproject.toml in {lib_path}")
return {}
def get_nested(doc, *args, default=None):
for a in args:
if doc is None: return default
try:
doc = doc[a]
except (KeyError, IndexError) as e:
return default
return doc
IGNORE_PY = ["setup.py", "conf.py", "__init__.py"]
GLOB_PATTERNS = ["*.py", "font5x8.bin"]
GLOB_PATTERNS = ["*.py", "*.bin"]
S3_MPY_PREFIX = "https://adafruit-circuit-python.s3.amazonaws.com/bin/mpy-cross"
def version_string(path=None, *, valid_semver=False):
version = None
@ -64,6 +109,40 @@ def version_string(path=None, *, valid_semver=False):
def mpy_cross(mpy_cross_filename, circuitpython_tag, quiet=False):
if os.path.isfile(mpy_cross_filename):
return
# Try to pull from S3
uname = platform.uname()
s3_url = None
if uname[0].title() == 'Linux' and uname[4].lower() in ('amd64', 'x86_64'):
s3_url = f"{S3_MPY_PREFIX}/linux-amd64/mpy-cross-linux-amd64-{circuitpython_tag}.static"
elif uname[0].title() == 'Linux' and uname[4].lower() == 'armv7l':
s3_url = f"{S3_MPY_PREFIX}/linux-raspbian/mpy-cross-linux-raspbian-{circuitpython_tag}.static-raspbian"
elif uname[0].title() == 'Darwin':
s3_url = f"{S3_MPY_PREFIX}/macos-11/mpy-cross-macos-11-{circuitpython_tag}-universal"
elif uname[0].title() == "Windows" and uname[4].lower() in ("amd64", "x86_64"):
s3_url = f"{S3_MPY_PREFIX}/windows/mpy-cross-windows-{circuitpython_tag}.static.exe"
elif not quiet:
print(f"Pre-built mpy-cross not available for sysname='{uname[0]}' release='{uname[2]}' machine='{uname[4]}'.")
if s3_url is not None:
if not quiet:
print(f"Checking S3 for {s3_url}")
try:
r = requests.get(s3_url)
if r.status_code == 200:
with open(mpy_cross_filename, "wb") as f:
f.write(r.content)
# Set the User Execute bit
os.chmod(mpy_cross_filename, os.stat(mpy_cross_filename)[0] | stat.S_IXUSR)
if not quiet:
print(" FOUND")
return
except Exception as e:
if not quiet:
print(f" exception fetching from S3: {e}")
if not quiet:
print(" NOT FOUND")
if not quiet:
title = "Building mpy-cross for circuitpython " + circuitpython_tag
print()
@ -85,48 +164,78 @@ def mpy_cross(mpy_cross_filename, circuitpython_tag, quiet=False):
make = subprocess.run("make clean && make", shell=True)
os.chdir(current_dir)
shutil.copy("build_deps/circuitpython/mpy-cross/mpy-cross", mpy_cross_filename)
if make.returncode != 0:
print("Failed to build mpy-cross from source... bailing out")
sys.exit(make.returncode)
shutil.copy("build_deps/circuitpython/mpy-cross/mpy-cross", mpy_cross_filename)
def _munge_to_temp(original_path, temp_file, library_version):
with open(original_path, "rb") as original_file:
with open(original_path, "r", encoding="utf-8") as original_file:
for line in original_file:
if original_path.endswith(".bin"):
# this is solely for adafruit_framebuf/examples/font5x8.bin
temp_file.write(line)
else:
line = line.decode("utf-8").strip("\n")
if line.startswith("__version__"):
line = line.replace("0.0.0-auto.0", library_version)
temp_file.write(line.encode("utf-8") + b"\r\n")
line = line.strip("\n")
if line.startswith("__version__"):
line = line.replace("0.0.0-auto.0", library_version)
line = line.replace("0.0.0+auto.0", library_version)
print(line, file=temp_file)
temp_file.flush()
def library(library_path, output_directory, package_folder_prefix,
mpy_cross=None, example_bundle=False):
py_files = []
package_files = []
example_files = []
total_size = 512
def get_package_info(library_path, package_folder_prefix):
lib_path = pathlib.Path(library_path)
parent_idx = len(lib_path.parts)
py_files = []
package_files = []
package_info = {}
glob_search = []
for pattern in GLOB_PATTERNS:
glob_search.extend(list(lib_path.rglob(pattern)))
for file in glob_search:
if file.parts[parent_idx] == "examples":
example_files.append(file)
else:
if not example_bundle:
is_package = False
for prefix in package_folder_prefix:
if file.parts[parent_idx].startswith(prefix):
is_package = True
pyproject_toml = load_pyproject_toml(lib_path)
py_modules = get_nested(pyproject_toml, "tool", "setuptools", "py-modules", default=[])
packages = get_nested(pyproject_toml, "tool", "setuptools", "packages", default=[])
if is_package:
blocklisted = [name for name in py_modules if name in pyproject_py_modules_blocklist]
if blocklisted:
print(f"{lib_path}/settings.toml:1: {blocklisted[0]} blocklisted: not using metadata from pyproject.toml")
py_modules = packages = ()
example_files = [sub_path for sub_path in (lib_path / "examples").rglob("*")
if sub_path.is_file()]
if packages and py_modules:
raise ValueError("Cannot specify both tool.setuptools.py-modules and .packages")
elif packages:
if len(packages) > 1:
raise ValueError("Only a single package is supported")
package_name = packages[0]
#print(f"Using package name from pyproject.toml: {package_name}")
package_info["is_package"] = True
package_info["module_name"] = package_name
package_files = [sub_path for sub_path in (lib_path / package_name).rglob("*")
if sub_path.is_file()]
elif py_modules:
if len(py_modules) > 1:
raise ValueError("Only a single module is supported")
py_module = py_modules[0]
#print(f"Using module name from pyproject.toml: {py_module}")
package_name = py_module
package_info["is_package"] = False
package_info["module_name"] = py_module
py_files = [lib_path / f"{py_module}.py"]
else:
print(f"{lib_path}: Using legacy autodetection")
package_info["is_package"] = False
for file in glob_search:
if file.parts[parent_idx] != "examples":
if len(file.parts) > parent_idx + 1:
for prefix in package_folder_prefix:
if file.parts[parent_idx].startswith(prefix):
package_info["is_package"] = True
if package_info["is_package"]:
package_files.append(file)
else:
if file.name in IGNORE_PY:
@ -135,83 +244,100 @@ def library(library_path, output_directory, package_folder_prefix,
if file.parent == lib_path:
py_files.append(file)
if package_files:
package_info["module_name"] = package_files[0].relative_to(library_path).parent.name
elif py_files:
package_info["module_name"] = py_files[0].relative_to(library_path).name[:-3]
else:
package_info["module_name"] = None
if len(py_files) > 1:
raise ValueError("Multiple top level py files not allowed. Please put "
"them in a package or combine them into a single file.")
package_info["package_files"] = package_files
package_info["py_files"] = py_files
package_info["example_files"] = example_files
try:
package_info["version"] = version_string(library_path, valid_semver=True)
except ValueError as e:
print(library_path + " has version that doesn't follow SemVer (semver.org)")
print(e)
package_info["version"] = version_string(library_path)
return package_info
def library(library_path, output_directory, package_folder_prefix,
mpy_cross=None, example_bundle=False):
lib_path = pathlib.Path(library_path)
package_info = get_package_info(library_path, package_folder_prefix)
py_package_files = package_info["package_files"] + package_info["py_files"]
example_files = package_info["example_files"]
module_name = package_info["module_name"]
for fn in example_files:
base_dir = os.path.join(output_directory.replace("/lib", "/"),
fn.relative_to(library_path).parent)
if not os.path.isdir(base_dir):
os.makedirs(base_dir)
total_size += 512
for fn in package_files:
for fn in py_package_files:
base_dir = os.path.join(output_directory,
fn.relative_to(library_path).parent)
if not os.path.isdir(base_dir):
os.makedirs(base_dir)
total_size += 512
new_extension = ".py"
if mpy_cross:
new_extension = ".mpy"
library_version = package_info['version']
try:
library_version = version_string(library_path, valid_semver=True)
except ValueError as e:
print(library_path + " has version that doesn't follow SemVer (semver.org)")
print(e)
library_version = version_string(library_path)
for filename in py_files:
full_path = os.path.join(library_path, filename)
output_file = os.path.join(
output_directory,
filename.relative_to(library_path).with_suffix(new_extension)
)
with tempfile.NamedTemporaryFile() as temp_file:
_munge_to_temp(full_path, temp_file, library_version)
if mpy_cross:
mpy_success = subprocess.call([
mpy_cross,
"-o", output_file,
"-s", str(filename.relative_to(library_path)),
temp_file.name
])
if mpy_success != 0:
raise RuntimeError("mpy-cross failed on", full_path)
if not example_bundle:
for filename in py_package_files:
full_path = os.path.join(library_path, filename)
output_file = output_directory / filename.relative_to(library_path)
if filename.suffix == ".py":
with tempfile.NamedTemporaryFile(delete=False, mode="w+") as temp_file:
temp_file_name = temp_file.name
try:
_munge_to_temp(full_path, temp_file, library_version)
temp_file.close()
if mpy_cross and os.stat(temp_file.name).st_size != 0:
output_file = output_file.with_suffix(".mpy")
mpy_success = subprocess.call([
mpy_cross,
"-o", output_file,
"-s", str(filename.relative_to(library_path)),
temp_file.name
])
if mpy_success != 0:
raise RuntimeError("mpy-cross failed on", full_path)
else:
shutil.copyfile(full_path, output_file)
finally:
os.remove(temp_file_name)
else:
shutil.copyfile(temp_file.name, output_file)
shutil.copyfile(full_path, output_file)
for filename in package_files:
full_path = os.path.join(library_path, filename)
with tempfile.NamedTemporaryFile() as temp_file:
_munge_to_temp(full_path, temp_file, library_version)
if not mpy_cross or os.stat(full_path).st_size == 0:
output_file = os.path.join(output_directory,
filename.relative_to(library_path))
shutil.copyfile(temp_file.name, output_file)
else:
output_file = os.path.join(
output_directory,
filename.relative_to(library_path).with_suffix(new_extension)
)
requirements_files = lib_path.glob("requirements.txt*")
requirements_files = [f for f in requirements_files if f.stat().st_size > 0]
mpy_success = subprocess.call([
mpy_cross,
"-o", output_file,
"-s", str(filename.relative_to(library_path)),
temp_file.name
])
if mpy_success != 0:
raise RuntimeError("mpy-cross failed on", full_path)
toml_files = lib_path.glob("pyproject.toml*")
toml_files = [f for f in toml_files if f.stat().st_size > 0]
requirements_files.extend(toml_files)
if module_name and requirements_files and not example_bundle:
requirements_dir = pathlib.Path(output_directory).parent / "requirements"
if not os.path.isdir(requirements_dir):
os.makedirs(requirements_dir, exist_ok=True)
requirements_subdir = f"{requirements_dir}/{module_name}"
if not os.path.isdir(requirements_subdir):
os.makedirs(requirements_subdir, exist_ok=True)
for filename in requirements_files:
full_path = os.path.join(library_path, filename)
output_file = os.path.join(requirements_subdir, filename.name)
shutil.copyfile(full_path, output_file)
for filename in example_files:
full_path = os.path.join(library_path, filename)
output_file = os.path.join(output_directory.replace("/lib", "/"),
filename.relative_to(library_path))
with tempfile.NamedTemporaryFile() as temp_file:
_munge_to_temp(full_path, temp_file, library_version)
shutil.copyfile(temp_file.name, output_file)
shutil.copyfile(full_path, output_file)

View file

@ -25,7 +25,7 @@
import json
import os
import os.path
import shlex
import re
import shutil
import subprocess
import sys
@ -36,7 +36,29 @@ import click
from circuitpython_build_tools import build
from circuitpython_build_tools import target_versions
import pkg_resources
if sys.version_info < (3, 8):
import importlib_metadata
else:
import importlib.metadata as importlib_metadata
BLINKA_LIBRARIES = [
"adafruit-blinka",
"adafruit-blinka-bleio",
"adafruit-blinka-displayio",
"adafruit-blinka-pyportal",
"adafruit-python-extended-bus",
"numpy",
"pillow",
"pyasn1",
"pyserial",
"scipy",
"spidev",
]
def normalize_dist_name(name: str) -> str:
"""Return a normalized pip name"""
return name.lower().replace("_", "-")
def add_file(bundle, src_file, zip_name):
bundle.write(src_file, zip_name)
@ -47,9 +69,85 @@ def add_file(bundle, src_file, zip_name):
print(zip_name, file_size, file_sector_size)
return file_sector_size
def get_module_name(library_path, remote_name):
"""Figure out the module or package name and return it"""
repo = subprocess.run(f'git remote get-url {remote_name}', shell=True, stdout=subprocess.PIPE, cwd=library_path)
repo = repo.stdout.decode("utf-8", errors="ignore").strip().lower()
if repo[-4:] == ".git":
repo = repo[:-4]
module_name = normalize_dist_name(repo.split("/")[-1])
# circuitpython org repos are deployed to pypi without "org" in the pypi name
module_name = re.sub(r"^circuitpython-org-", "circuitpython-", module_name)
return module_name, repo
def get_bundle_requirements(directory, package_list):
"""
Open the requirements.txt if it exists
Remove anything that shouldn't be a requirement like Adafruit_Blinka
Return the list
"""
pypi_reqs = set() # For multiple bundle dependency
dependencies = set() # For intra-bundle dependency
path = directory + "/requirements.txt"
if os.path.exists(path):
with open(path, "r") as file:
requirements = file.read()
file.close()
for line in requirements.split("\n"):
line = line.lower().strip()
if line.startswith("#") or line == "":
# skip comments
pass
else:
# Remove any pip version and platform specifiers
original_name = re.split("[<>=~[;]", line)[0].strip()
# Normalize to match the indexes in package_list
line = normalize_dist_name(original_name)
if line in package_list:
dependencies.add(package_list[line]["module_name"])
elif line not in BLINKA_LIBRARIES:
# add with the exact spelling from requirements.txt
pypi_reqs.add(original_name)
return sorted(dependencies), sorted(pypi_reqs)
def build_bundle_json(libs, bundle_version, output_filename, package_folder_prefix, remote_name="origin"):
"""
Generate a JSON file of all the libraries in libs
"""
packages = {}
for library_path in libs:
package = {}
package_info = build.get_package_info(library_path, package_folder_prefix)
module_name, repo = get_module_name(library_path, remote_name)
if package_info["module_name"] is not None:
package["module_name"] = package_info["module_name"]
package["pypi_name"] = module_name
package["repo"] = repo
package["is_folder"] = package_info["is_package"]
package["version"] = package_info["version"]
package["path"] = "lib/" + package_info["module_name"]
package["library_path"] = library_path
packages[module_name] = package
library_submodules = {}
for id in packages:
library = {}
library["package"] = packages[id]["is_folder"]
library["pypi_name"] = packages[id]["pypi_name"]
library["version"] = packages[id]["version"]
library["repo"] = packages[id]["repo"]
library["path"] = packages[id]["path"]
library["dependencies"], library["external_dependencies"] = get_bundle_requirements(packages[id]["library_path"], packages)
library_submodules[packages[id]["module_name"]] = library
out_file = open(output_filename, "w")
json.dump(library_submodules, out_file, sort_keys=True)
out_file.close()
def build_bundle(libs, bundle_version, output_filename, package_folder_prefix,
build_tools_version="devel", mpy_cross=None, example_bundle=False):
build_tools_version="devel", mpy_cross=None, example_bundle=False, remote_name="origin"):
build_dir = "build-" + os.path.basename(output_filename)
top_folder = os.path.basename(output_filename).replace(".zip", "")
build_lib_dir = os.path.join(build_dir, top_folder, "lib")
@ -81,7 +179,7 @@ def build_bundle(libs, bundle_version, output_filename, package_folder_prefix,
if multiple_libs:
with open(os.path.join(build_dir, top_folder, "VERSIONS.txt"), "w") as f:
f.write(bundle_version + "\r\n")
versions = subprocess.run('git submodule --quiet foreach \"git remote get-url origin && git describe --tags\"', shell=True, stdout=subprocess.PIPE, cwd=os.path.commonpath(libs))
versions = subprocess.run(f'git submodule --quiet foreach \"git remote get-url {remote_name} && git describe --tags\"', shell=True, stdout=subprocess.PIPE, cwd=os.path.commonpath(libs))
if versions.returncode != 0:
print("Failed to generate versions file. Its likely a library hasn't been "
"released yet.")
@ -107,7 +205,7 @@ def build_bundle(libs, bundle_version, output_filename, package_folder_prefix,
print()
print("Zipping")
with zipfile.ZipFile(output_filename, 'w') as bundle:
with zipfile.ZipFile(output_filename, 'w', compression=zipfile.ZIP_DEFLATED) as bundle:
build_metadata = {"build-tools-version": build_tools_version}
bundle.comment = json.dumps(build_metadata).encode("utf-8")
if multiple_libs:
@ -138,7 +236,9 @@ def _find_libraries(current_path, depth):
@click.option('--library_location', required=True, help="Location of libraries to bundle.")
@click.option('--library_depth', default=0, help="Depth of library folders. This is useful when multiple libraries are bundled together but are initially in separate subfolders.")
@click.option('--package_folder_prefix', default="adafruit_", help="Prefix string used to determine package folders to bundle.")
def build_bundles(filename_prefix, output_directory, library_location, library_depth, package_folder_prefix):
@click.option('--remote_name', default="origin", help="Git remote name to use during building")
@click.option('--ignore', "-i", multiple=True, type=click.Choice(["py", "mpy", "example", "json"]), help="Bundles to ignore building")
def build_bundles(filename_prefix, output_directory, library_location, library_depth, package_folder_prefix, remote_name, ignore):
os.makedirs(output_directory, exist_ok=True)
package_folder_prefix = package_folder_prefix.split(", ")
@ -147,10 +247,10 @@ def build_bundles(filename_prefix, output_directory, library_location, library_d
libs = _find_libraries(os.path.abspath(library_location), library_depth)
pkg = pkg_resources.get_distribution("circuitpython-build-tools")
build_tools_version = "devel"
if pkg:
build_tools_version = pkg.version
try:
build_tools_version = importlib_metadata.version("circuitpython-build-tools")
except importlib_metadata.PackageNotFoundError:
build_tools_version = "devel"
build_tools_fn = "z-build_tools_version-{}.ignore".format(
build_tools_version)
@ -159,32 +259,37 @@ def build_bundles(filename_prefix, output_directory, library_location, library_d
f.write(build_tools_version)
# Build raw source .py bundle
zip_filename = os.path.join(output_directory,
filename_prefix + '-py-{VERSION}.zip'.format(
VERSION=bundle_version))
build_bundle(libs, bundle_version, zip_filename, package_folder_prefix,
build_tools_version=build_tools_version)
# Build .mpy bundle(s)
os.makedirs("build_deps", exist_ok=True)
for version in target_versions.VERSIONS:
# Use prebuilt mpy-cross on Travis, otherwise build our own.
if "TRAVIS" in os.environ:
mpy_cross = pkg_resources.resource_filename(
target_versions.__name__, "data/mpy-cross-" + version["name"])
else:
mpy_cross = "build_deps/mpy-cross-" + version["name"]
build.mpy_cross(mpy_cross, version["tag"])
if "py" not in ignore:
zip_filename = os.path.join(output_directory,
filename_prefix + '-{TAG}-mpy-{VERSION}.zip'.format(
TAG=version["name"],
filename_prefix + '-py-{VERSION}.zip'.format(
VERSION=bundle_version))
build_bundle(libs, bundle_version, zip_filename, package_folder_prefix,
mpy_cross=mpy_cross, build_tools_version=build_tools_version)
build_tools_version=build_tools_version, remote_name=remote_name)
# Build .mpy bundle(s)
if "mpy" not in ignore:
os.makedirs("build_deps", exist_ok=True)
for version in target_versions.VERSIONS:
mpy_cross = "build_deps/mpy-cross-" + version["name"] + (".exe" * (os.name == "nt"))
build.mpy_cross(mpy_cross, version["tag"])
zip_filename = os.path.join(output_directory,
filename_prefix + '-{TAG}-mpy-{VERSION}.zip'.format(
TAG=version["name"],
VERSION=bundle_version))
build_bundle(libs, bundle_version, zip_filename, package_folder_prefix,
mpy_cross=mpy_cross, build_tools_version=build_tools_version, remote_name=remote_name)
# Build example bundle
zip_filename = os.path.join(output_directory,
filename_prefix + '-examples-{VERSION}.zip'.format(
VERSION=bundle_version))
build_bundle(libs, bundle_version, zip_filename, package_folder_prefix,
build_tools_version=build_tools_version, example_bundle=True)
if "example" not in ignore:
zip_filename = os.path.join(output_directory,
filename_prefix + '-examples-{VERSION}.zip'.format(
VERSION=bundle_version))
build_bundle(libs, bundle_version, zip_filename, package_folder_prefix,
build_tools_version=build_tools_version, example_bundle=True, remote_name=remote_name)
# Build Bundle JSON
if "json" not in ignore:
json_filename = os.path.join(output_directory,
filename_prefix + '-{VERSION}.json'.format(
VERSION=bundle_version))
build_bundle_json(libs, bundle_version, json_filename, package_folder_prefix, remote_name=remote_name)

View file

@ -25,5 +25,6 @@
# The tag specifies which version of CircuitPython to use for mpy-cross.
# The name is used when constructing the zip file names.
VERSIONS = [
{"tag": "6.1.0", "name": "6.x"},
{"tag": "8.2.0", "name": "8.x"},
{"tag": "9.0.0-alpha.2", "name": "9.x"},
]

View file

@ -1,3 +1,5 @@
Click
requests
semver
wheel
tomli; python_version < "3.11"

View file

@ -13,8 +13,8 @@ setup(name='circuitpython-build-tools',
'circuitpython_build_tools.scripts'],
package_data={'circuitpython_build_tools': ['data/mpy-cross-*']},
zip_safe=False,
python_requires='>=3.4',
install_requires=['Click', 'semver'],
python_requires='>=3.10',
install_requires=['Click', 'requests', 'semver', 'tomli; python_version < "3.11"'],
entry_points='''
[console_scripts]
circuitpython-build-bundles=circuitpython_build_tools.scripts.build_bundles:build_bundles