Skip to content

Commit 9dd56b1

Browse files
authored
Merge branch 'master' into snekboard
2 parents c6e4ddc + bb04fbf commit 9dd56b1

File tree

19 files changed

+666
-209
lines changed

19 files changed

+666
-209
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ env:
2424
- TRAVIS_TESTS="unix docs translations website" TRAVIS_BOARDS="trinket_m0_haxpress circuitplayground_express mini_sam_m4 grandcentral_m4_express capablerobot_usbhub pygamer pca10056 pca10059 feather_nrf52840_express makerdiary_nrf52840_mdk makerdiary_nrf52840_mdk_usb_dongle particle_boron particle_argon particle_xenon sparkfun_nrf52840_mini electronut_labs_papyr electronut_labs_blip" TRAVIS_SDK=arm:nrf
2525
- TRAVIS_BOARDS="metro_m0_express metro_m4_express metro_m4_airlift_lite pirkey_m0 trellis_m4_express trinket_m0 sparkfun_lumidrive sparkfun_redboard_turbo bast_pro_mini_m0 datum_distance pyruler" TRAVIS_SDK=arm
2626
- TRAVIS_BOARDS="cp32-m4 feather_radiofruit_zigbee gemma_m0 hallowing_m0_express itsybitsy_m0_express itsybitsy_m4_express meowmeow sam32 uchip escornabot_makech pygamer_advance datum_imu snekboard" TRAVIS_SDK=arm
27-
- TRAVIS_BOARDS="feather_m0_supersized feather_m0_express_crickit feather_m0_rfm69 feather_m0_rfm9x feather_m4_express arduino_zero arduino_mkr1300 arduino_mkrzero pewpew10 kicksat-sprite ugame10 robohatmm1 datum_light" TRAVIS_SDK=arm
27+
- TRAVIS_BOARDS="feather_m0_supersized feather_m0_express_crickit feather_m0_rfm69 feather_m0_rfm9x feather_m4_express arduino_zero arduino_mkr1300 arduino_mkrzero pewpew10 kicksat-sprite ugame10 robohatmm1_m0 robohatmm1_m4 datum_light" TRAVIS_SDK=arm
2828
- TRAVIS_BOARDS="datalore_ip_m4 circuitplayground_express_crickit feather_m0_adalogger feather_m0_basic feather_m0_express catwan_usbstick pyportal sparkfun_samd21_mini sparkfun_samd21_dev pybadge pybadge_airlift datum_weather" TRAVIS_SDK=arm
2929

3030
addons:
@@ -79,10 +79,10 @@ before_script:
7979

8080
# For coverage testing (upgrade is used to get latest urllib3 version)
8181
- sudo apt-get install -y python3-pip
82-
- pip3 install --user sh click
82+
- pip3 install --user sh click setuptools
8383
- ([[ -z "$TRAVIS_TESTS" ]] || sudo pip install --upgrade cpp-coveralls)
8484
- (! var_search "${TRAVIS_TESTS-}" docs || sudo apt-get install -y librsvg2-bin)
85-
- (! var_search "${TRAVIS_TESTS-}" docs || pip install --user Sphinx sphinx-rtd-theme recommonmark sphinxcontrib-svg2pdfconverter)
85+
- (! var_search "${TRAVIS_TESTS-}" docs || pip3 install --user Sphinx sphinx-rtd-theme recommonmark sphinxcontrib-svg2pdfconverter)
8686
- (! var_search "${TRAVIS_TESTS-}" translations || pip3 install --user polib)
8787

8888
# Check if there's any board missing in TRAVIS_BOARDS

conf.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# All configuration values have a default; values that are commented out
1414
# serve to show the default.
1515

16+
import json
1617
import sys
1718
import os
1819

@@ -24,8 +25,20 @@
2425
sys.path.insert(0, os.path.abspath('docs'))
2526
sys.path.insert(0, os.path.abspath('.'))
2627

28+
import shared_bindings_matrix
29+
2730
master_doc = 'docs/index'
2831

32+
# Grab the JSON values to use while building the module support matrix
33+
# in 'shared-bindings/index.rst'
34+
35+
#modules_support_matrix = shared_bindings_matrix.support_matrix_excluded_boards()
36+
modules_support_matrix = shared_bindings_matrix.support_matrix_by_board()
37+
38+
html_context = {
39+
'support_matrix': modules_support_matrix
40+
}
41+
2942
# -- General configuration ------------------------------------------------
3043

3144
# If your documentation needs a minimal Sphinx version, state it here.
@@ -40,7 +53,9 @@
4053
'sphinxcontrib.rsvgconverter',
4154
'sphinx.ext.intersphinx',
4255
'sphinx.ext.todo',
43-
'sphinx.ext.coverage'
56+
'sphinx.ext.coverage',
57+
'rstjinja',
58+
'c2rst'
4459
]
4560

4661
# Add any paths that contain templates here, relative to this directory.
@@ -49,8 +64,7 @@
4964
# The suffix of source filenames.
5065
source_suffix = ['.rst', '.md', '.c', '.h']
5166

52-
source_parsers = {'.md': CommonMarkParser,
53-
'.c': "c2rst.CStrip", '.h': "c2rst.CStrip"}
67+
source_parsers = {'.md': CommonMarkParser}
5468

5569
# The encoding of source files.
5670
#source_encoding = 'utf-8-sig'

docs/c2rst.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
1-
import sphinx.parsers
1+
def c2rst(app, docname, source):
2+
""" Pre-parse '.c' & '.h' files that contain rST source.
3+
"""
4+
# Make sure we're outputting HTML
5+
if app.builder.format != 'html':
6+
return
27

3-
class CStrip(sphinx.parsers.Parser):
4-
def __init__(self):
5-
self.rst_parser = sphinx.parsers.RSTParser()
8+
fname = app.env.doc2path(docname)
9+
if (not fname.endswith(".c") and
10+
not fname.endswith(".h")):
11+
#print("skipping:", fname)
12+
return
613

7-
def parse(self, inputstring, document):
8-
# This setting is missing starting with Sphinx 1.7.1 so we set it ourself.
9-
document.settings.tab_width = 4
10-
document.settings.character_level_inline_markup = False
11-
stripped = []
12-
for line in inputstring.split("\n"):
13-
line = line.strip()
14-
if line == "//|":
15-
stripped.append("")
16-
elif line.startswith("//| "):
17-
stripped.append(line[len("//| "):])
18-
stripped = "\r\n".join(stripped)
19-
self.rst_parser.parse(stripped, document)
14+
src = source[0]
15+
16+
stripped = []
17+
for line in src.split("\n"):
18+
line = line.strip()
19+
if line == "//|":
20+
stripped.append("")
21+
elif line.startswith("//| "):
22+
stripped.append(line[len("//| "):])
23+
stripped = "\r\n".join(stripped)
24+
25+
rendered = app.builder.templates.render_string(
26+
stripped, app.config.html_context
27+
)
28+
source[0] = rendered
29+
30+
def setup(app):
31+
app.connect("source-read", c2rst)

docs/library/network.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
.. include:: ../templates/unsupported_in_circuitpython.inc
66

77
.. module:: network
8+
:noindex:
89
:synopsis: network configuration
910

1011
This module provides network drivers and routing configuration. To use this

docs/rstjinja.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Derived from code on Eric Holscher's blog, found at:
2+
# https://www.ericholscher.com/blog/2016/jul/25/integrating-jinja-rst-sphinx/
3+
4+
def rstjinja(app, docname, source):
5+
"""
6+
Render our pages as a jinja template for fancy templating goodness.
7+
"""
8+
# Make sure we're outputting HTML
9+
if app.builder.format != 'html':
10+
return
11+
12+
# we only want our one jinja template to run through this func
13+
if "shared-bindings/support_matrix" not in docname:
14+
return
15+
16+
src = source[0]
17+
print(docname)
18+
rendered = app.builder.templates.render_string(
19+
src, app.config.html_context
20+
)
21+
source[0] = rendered
22+
23+
def setup(app):
24+
app.connect("source-read", rstjinja)

docs/shared_bindings_matrix.py

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Michael Schroeder
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
#
23+
24+
import json
25+
import os
26+
import re
27+
28+
29+
SUPPORTED_PORTS = ["atmel-samd", "nrf"]
30+
31+
32+
def parse_port_config(contents, chip_keyword=None):
33+
""" Compile a dictionary of port-wide module configs, which may
34+
be categorized by chipset.
35+
"""
36+
chip_fam = "all"
37+
ifeq_found = False
38+
port_config_results = {"all": []}
39+
40+
chip_pattern = ""
41+
if chip_keyword:
42+
chip_pattern = (
43+
re.compile("(?<=ifeq\s\(\$\({}\)\,)(\w+)".format(chip_keyword))
44+
)
45+
46+
for line in contents:
47+
if chip_keyword:
48+
if not ifeq_found:
49+
check_ifeq = chip_pattern.search(line)
50+
if check_ifeq:
51+
ifeq_found = True
52+
chip_fam = check_ifeq.group(1)
53+
#print("found chip:", chip_fam)
54+
else:
55+
ifeq_found = False
56+
chip_fam = "all"
57+
else:
58+
if "endif" in line:
59+
ifeq_found = False
60+
chip_fam = "all"
61+
62+
if "CIRCUITPY_" in line:
63+
if chip_fam in port_config_results:
64+
port_config_results[chip_fam].append(line.rstrip("\n"))
65+
else:
66+
port_config_results[chip_fam] = [line.rstrip("\n")]
67+
68+
#print(port_config_results)
69+
return port_config_results
70+
71+
def get_shared_bindings():
72+
""" Get a list of modules in shared-bindings based on folder names
73+
"""
74+
return [item for item in os.listdir("./shared-bindings")]
75+
76+
77+
def read_mpconfig():
78+
""" Open 'circuitpy_mpconfig.mk' and return the contents.
79+
"""
80+
configs = []
81+
with open("py/circuitpy_mpconfig.mk") as mpconfig:
82+
configs = mpconfig.read()
83+
84+
return configs
85+
86+
87+
def build_module_map():
88+
""" Establish the base of the JSON file, based on the contents from
89+
`configs`. Base will contain module names, if they're part of
90+
the `FULL_BUILD`, or their default value (0 | 1).
91+
92+
"""
93+
base = dict()
94+
modules = get_shared_bindings()
95+
configs = read_mpconfig()
96+
full_build = False
97+
for module in modules:
98+
full_name = module
99+
search_name = module.lstrip("_")
100+
re_pattern = "CIRCUITPY_{}\s=\s(.+)".format(search_name.upper())
101+
find_config = re.search(re_pattern, configs)
102+
#print(module, "|", find_config)
103+
if not find_config:
104+
continue
105+
full_build = int("FULL_BUILD" in find_config.group(0))
106+
#print(find_config[1])
107+
if not full_build:
108+
default_val = find_config.group(1)
109+
else:
110+
default_val = "None"
111+
base[search_name] = {
112+
"name": full_name,
113+
"full_build": str(full_build),
114+
"default_value": default_val,
115+
"excluded": {}
116+
}
117+
118+
return base
119+
120+
121+
def get_excluded_boards(base):
122+
""" Cycles through each board's `mpconfigboard.mk` file to determine
123+
if each module is included or not. Boards are selected by existence
124+
in a port listed in `SUPPORTED_PORTS` (e.g. `/port/nrf/feather_52840`)
125+
126+
Boards are further categorized by their respective chipset (SAMD21,
127+
SAMD51, nRF52840, etc.)
128+
"""
129+
modules = list(base.keys())
130+
131+
re_board_chip = None
132+
chip_keyword = None
133+
for port in SUPPORTED_PORTS:
134+
# each port appears to use its own define for the chipset
135+
if port in ["atmel-samd"]:
136+
re_board_chip = re.compile("CHIP_FAMILY\s=\s(\w+)")
137+
chip_keyword = "CHIP_FAMILY"
138+
elif port in ["nrf"]:
139+
re_board_chip = re.compile("MCU_VARIANT\s=\s(\w+)")
140+
141+
port_dir = "ports/{}".format(port)
142+
143+
port_config_contents = ""
144+
with open(os.path.join(port_dir, "mpconfigport.mk")) as port_config:
145+
port_config_contents = port_config.readlines()
146+
port_config = parse_port_config(port_config_contents, chip_keyword)
147+
148+
for entry in os.scandir(os.path.join(port_dir, "boards")):
149+
if not entry.is_dir():
150+
continue
151+
152+
contents = ""
153+
board_dir = os.path.join(entry.path, "mpconfigboard.mk")
154+
with open(board_dir) as board:
155+
contents = board.read()
156+
157+
board_chip = re_board_chip.search(contents)
158+
#print(entry.name, board_chip.group(1))
159+
if not board_chip:
160+
board_chip = "Unknown Chip"
161+
else:
162+
board_chip = board_chip.group(1)
163+
164+
# add port_config results to contents
165+
contents += "\n" + "\n".join(port_config["all"])
166+
if board_chip in port_config:
167+
contents += "\n" + "\n".join(port_config[board_chip])
168+
169+
for module in modules:
170+
board_is_excluded = False
171+
# check if board uses `SMALL_BUILD`. if yes, and current
172+
# module is marked as `FULL_BUILD`, board is excluded
173+
small_build = re.search("CIRCUITPY_SMALL_BUILD = 1", contents)
174+
if small_build and base[module]["full_build"] == "1":
175+
board_is_excluded = True
176+
# check if module is specifically disabled for this board
177+
re_pattern = "CIRCUITPY_{}\s=\s(\w)".format(module.upper())
178+
find_module = re.search(re_pattern, contents)
179+
if not find_module:
180+
# check if default inclusion is off ('0'). if the board doesn't
181+
# have it explicitly enabled, its excluded.
182+
if base[module]["default_value"] == "0":
183+
board_is_excluded = True
184+
else:
185+
if (find_module.group(1) == "0" and
186+
find_module.group(1) != base[module]["default_value"]):
187+
board_is_excluded = True
188+
189+
if board_is_excluded:
190+
if board_chip in base[module]["excluded"]:
191+
base[module]["excluded"][board_chip].append(entry.name)
192+
else:
193+
base[module]["excluded"][board_chip] = [entry.name]
194+
#print(json.dumps(base, indent=2))
195+
return base
196+
197+
198+
def support_matrix_excluded_boards():
199+
""" Compiles a list of available modules, and which board definitions
200+
do not include them.
201+
"""
202+
base = build_module_map()
203+
204+
return get_excluded_boards(base)
205+
206+
def support_matrix_by_board():
207+
""" Compiles a list of the available core modules available for each
208+
board.
209+
"""
210+
base = build_module_map()
211+
base_with_exclusions = get_excluded_boards(base)
212+
213+
boards = dict()
214+
for port in SUPPORTED_PORTS:
215+
port_dir = "ports/{}/boards".format(port)
216+
for entry in os.scandir(port_dir):
217+
if not entry.is_dir():
218+
continue
219+
board_modules = []
220+
221+
board_name = entry.name
222+
board_contents = ""
223+
with open(os.path.join(entry.path, "mpconfigboard.h")) as get_name:
224+
board_contents = get_name.read()
225+
board_name_re = re.search("(?<=MICROPY_HW_BOARD_NAME)\s+(.+)",
226+
board_contents)
227+
if board_name_re:
228+
board_name = board_name_re.group(1).strip('"')
229+
230+
for module in base_with_exclusions.keys():
231+
#print(module)
232+
board_has_module = True
233+
if base_with_exclusions[module]["excluded"]:
234+
for port in base_with_exclusions[module]["excluded"].values():
235+
#print(port)
236+
if entry.name in port:
237+
board_has_module = False
238+
239+
if board_has_module:
240+
board_modules.append(base_with_exclusions[module]["name"])
241+
boards[board_name] = sorted(board_modules)
242+
243+
#print(json.dumps(boards, indent=2))
244+
return boards

0 commit comments

Comments
 (0)