Skip to content

Commit 45f563a

Browse files
committed
feat: easy_setup prompts board codenames
Using a fuzzy find after parsing boards.txt. This has required refactoring the board parser code in a dedicated modules, with minor impacts in the files that depended on it. ============= Doc changes: expand on the syntax in easy_setup: Added a comment block to explain the use of the DEPENDS clause. Added "usual" values for board settings. Add a link to the wiki in easy_setup. Also update link in the README_CMAKE - update link to point to the new location for the examples; - unify the displayed text for the links; - add a link to the wiki.
1 parent 12c6d99 commit 45f563a

File tree

7 files changed

+189
-126
lines changed

7 files changed

+189
-126
lines changed

Diff for: README_CMAKE.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
CMake can now be used to build Arduino sketches with this core.
2-
Examples of use can be found on [this repo](https://github.com/massonal/STM32CMake_workspace).
2+
Examples of use can be found on this repo: [stm32duino/CMake_workspace](https://github.com/stm32duino/CMake_workspace).
3+
4+
This README only provides a quick walk-through.
5+
For all the glorious details, please head over to [the wiki](https://github.com/massonal/Arduino_Core_STM32/wiki).
36

47
# Prerequisites
58

@@ -17,7 +20,7 @@ If your system does not provide a recent enough version of CMake, a suitable ver
1720

1821
This section will describe the process of building a sketch "by hand", with a system shell. Other methods, such as with an IDE plug-in, may require adaptations.
1922

20-
Please see [STM32CMake_workspace](https://github.com/massonal/STM32CMake_workspace) for some quick examples; more may be added over time.
23+
Please see [stm32duino/CMake_workspace](https://github.com/stm32duino/CMake_workspace) for some quick examples; more may be added over time.
2124

2225
First of all, there has to be a CMakeLists.txt in the sketch folder.
2326

Diff for: cmake/scripts/cmake_easy_setup.py

+42
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import pathlib
99
import json
1010
from jinja2 import Environment, FileSystemLoader
11+
import difflib
12+
13+
from parse_boards import parse_file
1114

1215
parser = argparse.ArgumentParser()
1316
parser.add_argument("--cli", "-x", type=pathlib.Path, required=False, default=shutil.which("arduino-cli"), help="path to arduino-cli")
@@ -18,6 +21,7 @@
1821
output_args.add_argument("--sketch", "-s", type=pathlib.Path, help="output file (CMake) filled given a sketch folder")
1922

2023
shargs = parser.parse_args()
24+
shargs.board = shargs.board.upper()
2125

2226
if shargs.sketch and not shargs.board :
2327
print("""
@@ -51,9 +55,39 @@ def get_log(fname) :
5155
for line in file :
5256
yield json.loads(line)
5357

58+
def get_boards(boardstxt) :
59+
60+
# we "reject" everything because we don't care about the values, only the keys
61+
families = parse_file(boardstxt, lambda x:True)
62+
del families["menu"]
63+
64+
boards = set()
65+
for fam, famcfg in families.items() :
66+
boards.update(famcfg.menu.pnum.keys())
67+
68+
return boards
5469

5570
_, logf = arduino("lib", "list")
5671

72+
allboards = get_boards(pathlib.Path(__file__).parent.parent.parent/"boards.txt")
73+
74+
75+
if shargs.board and shargs.board not in allboards :
76+
print(f"Unrecognized board name: '{shargs.board}'")
77+
print("Possible matches:")
78+
options = difflib.get_close_matches(shargs.board, allboards, n=9, cutoff=0.0)
79+
print("0. (keep as-is)")
80+
for i, x in enumerate(options, start=1) :
81+
print(f"{i}. {x}")
82+
choice = input("Choice number: ")
83+
while not choice.isdecimal() :
84+
choice = input("Invalid choice *number*. Select a board: ")
85+
choice = int(choice)
86+
if choice != 0 :
87+
choice -= 1
88+
shargs.board = options[choice]
89+
90+
5791
libpaths = dict()
5892
for line in get_log(logf) :
5993
if line["msg"] == "Adding libraries dir" :
@@ -109,6 +143,14 @@ def get_log(fname) :
109143
))
110144

111145

146+
print("Generated", shargs.output or shargs.sketch/"CMakeLists.txt")
147+
print("""
148+
Unless you are building a very simple sketch with no library (e.g., Blink),
149+
you are advised to edit this file to fill in any missing dependency relationship.
150+
For details, please refer to
151+
https://github.com/massonal/Arduino_Core_STM32/wiki/Arduino-(in)compatibility#library-management
152+
""")
153+
112154
if shargs.fire :
113155
if not (shargs.sketch and shargs.board) :
114156
print("There remains some placeholder in the output file; I won't build _that_.")

Diff for: cmake/scripts/cmake_updater_hook.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
)
3333
print("Updating board database...")
3434
subprocess.run(
35-
("python3", script_dir/"parse_boards.py",
35+
("python3", script_dir/"update_boarddb.py",
3636
"-b", base_dir/"boards.txt",
3737
"-p", base_dir/"platform.txt",
3838
"-t", templates_dir/"boards_db.cmake",

Diff for: cmake/scripts/parse_boards.py

+4-118
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
#!/usr/bin/env python3
22

3-
import pathlib
4-
import argparse
5-
6-
from jinja2 import Environment, FileSystemLoader
7-
3+
"""
4+
Utility module to parse Arduino config files
5+
such as boards.txt/platform.txt.
6+
"""
87

98
class Configuration(dict) :
109

@@ -51,49 +50,6 @@ def evaluate_entries(self, wrt=None) :
5150
self[k].evaluate_entries(wrt)
5251

5352

54-
def get_fpconf(config) :
55-
fpu = (config.build.get("fpu") or "-mfpu=").rsplit("=", 1)[1]
56-
abi = (config.build.get("float-abi") or "-mfloat-abi=").rsplit("=", 1)[1]
57-
return f"{fpu}-{abi}"
58-
59-
def boardstxt_filter(key) :
60-
# Remove menu entry labels
61-
# In our data model, they conflict with the actual configuration they are associated to
62-
# i.e. Nucleo_144.menu.pnum.NUCLEO_F207ZG would be both a string ("Nucleo F207ZG")
63-
# and a dict (.build.variant_h=..., .upload.maximum_size=...)
64-
65-
66-
if key[0] == "menu" :
67-
# menu.xserial=U(S)ART support
68-
return True
69-
if len(key) == 4 and key[1] == "menu":
70-
# Nucleo_144.menu.pnum.NUCLEO_F207ZG=Nucleo F207ZG
71-
# Midatronics.menu.upload_method.MassStorage=Mass Storage
72-
return True
73-
74-
# Remove upload_method also, that's out of our scope and requires more logic
75-
if len(key) >= 3 and key[2] == "upload_method" :
76-
return True
77-
78-
return False
79-
80-
def platformtxt_filter(key) :
81-
# reject everything but build.**
82-
# and also build.info (that's specific to the build system, we'll hard-code it)
83-
# we don't need anything else from platform.txt
84-
# +additional stuff might confuse later parts of the script
85-
# e.g.:
86-
87-
# compiler.warning_flags=-w
88-
# compiler.warning_flags.none=-w
89-
# compiler.warning_flags.default=
90-
# compiler.warning_flags.more=-Wall
91-
# compiler.warning_flags.all=-Wall -Wextra
92-
93-
if key[0] == "build" and key[1] != "info" :
94-
return False
95-
return True
96-
9753
def parse_file(infile, reject=None) :
9854
if reject is None :
9955
reject = lambda x:False
@@ -118,73 +74,3 @@ def parse_file(infile, reject=None) :
11874
else :
11975
ptr[key[-1]] = value
12076
return config
121-
122-
def regenerate_template(config, infile, outfile) :
123-
j2_env = Environment(
124-
loader=FileSystemLoader(str(infile.parent)), trim_blocks=True, lstrip_blocks=True
125-
)
126-
cmake_template = j2_env.get_template(infile.name)
127-
128-
with open(outfile, "w") as out :
129-
out.write(cmake_template.render(
130-
allcfg = config,
131-
))
132-
133-
134-
if __name__ == "__main__":
135-
parser = argparse.ArgumentParser()
136-
parser.add_argument("-b", "--boards", type=pathlib.Path, required=True, help="path to boards.txt")
137-
parser.add_argument("-p", "--platform", type=pathlib.Path, required=True, help="path to platform.txt")
138-
parser.add_argument("-t", "--template", type=pathlib.Path, required=True, help="path to the jinja template")
139-
parser.add_argument("-o", "--outfile", type=pathlib.Path, required=True, help="path to the cmake database to generate")
140-
141-
shargs = parser.parse_args()
142-
143-
platformtxt_cfg = parse_file(shargs.platform, reject=platformtxt_filter)
144-
platformtxt_cfg = {"build":platformtxt_cfg["build"]} # whitelist what we need
145-
146-
boardstxt_cfg = parse_file(shargs.boards, reject=boardstxt_filter)
147-
del boardstxt_cfg["menu"] # blacklist what we don't need
148-
149-
# these are optional features to be picked out by the user
150-
BOARD_FEATURES = [
151-
"enable_virtio",
152-
"enable_usb",
153-
"usb_speed",
154-
"xSerial",
155-
]
156-
157-
allboards = dict()
158-
159-
for fam, famcfg in boardstxt_cfg.items() :
160-
famcfg.set_default_entries(platformtxt_cfg)
161-
162-
inherit_fam = famcfg.copy()
163-
# shallow copy; we don't want to impact famcfg so we have to copy before edit/del
164-
inherit_fam["menu"] = inherit_fam["menu"].copy()
165-
# del what you iterate over (otherwise you get infinite nesting)
166-
del inherit_fam["menu"]["pnum"]
167-
168-
for board, boardcfg in famcfg.menu.pnum.items() :
169-
boardcfg.set_default_entries(inherit_fam)
170-
171-
inherit_board = boardcfg.copy()
172-
del inherit_board["menu"]
173-
174-
board_feature_names = tuple(boardcfg["menu"].keys())
175-
176-
for fname in board_feature_names :
177-
for label, labelcfg in boardcfg["menu"][fname].items() :
178-
labelcfg.set_default_entries(inherit_board)
179-
labelcfg.evaluate_entries()
180-
181-
# base config won't manage all the board features, we thus have to mask them out
182-
for feat in BOARD_FEATURES :
183-
boardcfg.build[feat] = ""
184-
185-
boardcfg.evaluate_entries()
186-
187-
boardcfg["_fpconf"] = get_fpconf(boardcfg)
188-
allboards[board] = boardcfg
189-
190-
regenerate_template(allboards, shargs.template, shargs.outfile)

Diff for: cmake/scripts/update_boarddb.py

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/usr/bin/env python3
2+
3+
import pathlib
4+
import argparse
5+
6+
from jinja2 import Environment, FileSystemLoader
7+
8+
from parse_boards import parse_file
9+
10+
11+
def get_fpconf(config) :
12+
fpu = (config.build.get("fpu") or "-mfpu=").rsplit("=", 1)[1]
13+
abi = (config.build.get("float-abi") or "-mfloat-abi=").rsplit("=", 1)[1]
14+
return f"{fpu}-{abi}"
15+
16+
def boardstxt_filter(key) :
17+
# Remove menu entry labels
18+
# In our data model, they conflict with the actual configuration they are associated to
19+
# i.e. Nucleo_144.menu.pnum.NUCLEO_F207ZG would be both a string ("Nucleo F207ZG")
20+
# and a dict (.build.variant_h=..., .upload.maximum_size=...)
21+
22+
23+
if key[0] == "menu" :
24+
# menu.xserial=U(S)ART support
25+
return True
26+
if len(key) == 4 and key[1] == "menu":
27+
# Nucleo_144.menu.pnum.NUCLEO_F207ZG=Nucleo F207ZG
28+
# Midatronics.menu.upload_method.MassStorage=Mass Storage
29+
return True
30+
31+
# Remove upload_method also, that's out of our scope and requires more logic
32+
if len(key) >= 3 and key[2] == "upload_method" :
33+
return True
34+
35+
return False
36+
37+
def platformtxt_filter(key) :
38+
# reject everything but build.**
39+
# and also build.info (that's specific to the build system, we'll hard-code it)
40+
# we don't need anything else from platform.txt
41+
# +additional stuff might confuse later parts of the script
42+
# e.g.:
43+
44+
# compiler.warning_flags=-w
45+
# compiler.warning_flags.none=-w
46+
# compiler.warning_flags.default=
47+
# compiler.warning_flags.more=-Wall
48+
# compiler.warning_flags.all=-Wall -Wextra
49+
50+
if key[0] == "build" and key[1] != "info" :
51+
return False
52+
return True
53+
54+
def regenerate_template(config, infile, outfile) :
55+
j2_env = Environment(
56+
loader=FileSystemLoader(str(infile.parent)), trim_blocks=True, lstrip_blocks=True
57+
)
58+
cmake_template = j2_env.get_template(infile.name)
59+
60+
with open(outfile, "w") as out :
61+
out.write(cmake_template.render(
62+
allcfg = config,
63+
))
64+
65+
66+
if __name__ == "__main__":
67+
parser = argparse.ArgumentParser()
68+
parser.add_argument("-b", "--boards", type=pathlib.Path, required=True, help="path to boards.txt")
69+
parser.add_argument("-p", "--platform", type=pathlib.Path, required=True, help="path to platform.txt")
70+
parser.add_argument("-t", "--template", type=pathlib.Path, required=True, help="path to the jinja template")
71+
parser.add_argument("-o", "--outfile", type=pathlib.Path, required=True, help="path to the cmake database to generate")
72+
73+
shargs = parser.parse_args()
74+
75+
platformtxt_cfg = parse_file(shargs.platform, reject=platformtxt_filter)
76+
platformtxt_cfg = {"build":platformtxt_cfg["build"]} # whitelist what we need
77+
78+
boardstxt_cfg = parse_file(shargs.boards, reject=boardstxt_filter)
79+
del boardstxt_cfg["menu"] # blacklist what we don't need
80+
81+
# these are optional features to be picked out by the user
82+
BOARD_FEATURES = [
83+
"enable_virtio",
84+
"enable_usb",
85+
"usb_speed",
86+
"xSerial",
87+
]
88+
89+
allboards = dict()
90+
91+
for fam, famcfg in boardstxt_cfg.items() :
92+
famcfg.set_default_entries(platformtxt_cfg)
93+
94+
inherit_fam = famcfg.copy()
95+
# shallow copy; we don't want to impact famcfg so we have to copy before edit/del
96+
inherit_fam["menu"] = inherit_fam["menu"].copy()
97+
# del what you iterate over (otherwise you get infinite nesting)
98+
del inherit_fam["menu"]["pnum"]
99+
100+
for board, boardcfg in famcfg.menu.pnum.items() :
101+
boardcfg.set_default_entries(inherit_fam)
102+
103+
inherit_board = boardcfg.copy()
104+
del inherit_board["menu"]
105+
106+
board_feature_names = tuple(boardcfg["menu"].keys())
107+
108+
for fname in board_feature_names :
109+
for label, labelcfg in boardcfg["menu"][fname].items() :
110+
labelcfg.set_default_entries(inherit_board)
111+
labelcfg.evaluate_entries()
112+
113+
# base config won't manage all the board features, we thus have to mask them out
114+
for feat in BOARD_FEATURES :
115+
boardcfg.build[feat] = ""
116+
117+
boardcfg.evaluate_entries()
118+
119+
boardcfg["_fpconf"] = get_fpconf(boardcfg)
120+
allboards[board] = boardcfg
121+
122+
regenerate_template(allboards, shargs.template, shargs.outfile)

0 commit comments

Comments
 (0)