Skip to content

Commit 4e8d6e5

Browse files
committed
feat: autogenerate the user's CMakeLists.txt
The script uses its location and arduino-cli to write a template of the sketch's CMakeLists.txt. * Defines: - CORE_PATH - USER_LIBS -> for use with `external_library()` * Includes all user libraries * Usable on a given sketch or generically (use placeholders) * Optionally: run CMake (config+build)
1 parent 8c0a382 commit 4e8d6e5

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

CI/update/templates/easy_cmake.cmake

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# This file was autogenerated by {{scriptfile}}.
2+
# Use it in your CMake configuration by `include()`'ing it.
3+
# You can also copy it in your sketch's folder and edit it to fit your project.
4+
5+
cmake_minimum_required(VERSION 3.13)
6+
7+
# STEP 1: set up bases of environment
8+
# -----------------------------------------------------------------------------
9+
10+
set(CORE_PATH "{{corepath}}")
11+
set(USER_LIBS "{{userlibs}}")
12+
set(BOARDNAME "{{boardname or "@board_name_here@"}}")
13+
14+
list(APPEND CMAKE_MODULE_PATH {{"${CORE_PATH}"}}/cmake)
15+
set(CMAKE_TOOLCHAIN_FILE toolchain)
16+
17+
18+
# You may remove this block when using this file as the sketch's CMakeLists.txt
19+
if (NOT ${CMAKE_PARENT_LIST_FILE} STREQUAL ${CMAKE_CURRENT_LIST_FILE})
20+
# When we are imported from the main CMakeLists.txt, we should stop here
21+
# not to interfere with the true build config.
22+
return()
23+
endif()
24+
25+
26+
27+
# STEP 2: configure the build
28+
# -----------------------------------------------------------------------------
29+
30+
include(set_board)
31+
set_board("{{"${BOARDNAME}"}}"
32+
# SERIAL generic
33+
# USB none
34+
# XUSB FS
35+
# VIRTIO disabled
36+
)
37+
38+
include(overall_settings)
39+
overall_settings(
40+
# STANDARD_LIBC
41+
# PRINTF_FLOAT
42+
# SCANF_FLOAT
43+
# DEBUG_SYMBOLS
44+
# LTO
45+
# NO_RELATIVE_MACRO
46+
# UNDEF_NDEBUG
47+
# OPTIMIZATION "s"
48+
# BUILD_OPT ./build.opt
49+
# DISABLE_HAL_MODULES ADC I2C RTC SPI TIM DAC EXTI ETH SD QSPI
50+
)
51+
52+
# STEP 3: configure your sketch
53+
# -----------------------------------------------------------------------------
54+
project("{{tgtname+"_project" if tgtname else "@project_name_here@"}}")
55+
56+
include(external_library)
57+
# I cannot tell the dependencies of the library ahead-of-time
58+
# Please write them in using the DEPENDS ... clause
59+
{% for libdir in libs | sort %}
60+
external_library(PATH "{{"${USER_LIBS}"}}/{{libdir}}")
61+
{% endfor %}
62+
63+
include(build_sketch)
64+
build_sketch(TARGET "{{tgtname or "@binary_name_here@"}}"
65+
SOURCES
66+
{% for file in scrcfiles | sort %}
67+
{{file}}
68+
{% else %}
69+
./file1.ino
70+
./file2.ino
71+
./file3.cpp
72+
./file4.c
73+
./file5.S
74+
{% endfor %}
75+
76+
# DEPENDS
77+
# SD
78+
# Wire
79+
)
80+
81+
# STEP 4: optional features
82+
# -----------------------------------------------------------------------------
83+
84+
include(insights)
85+
insights(TARGET "{{tgtname or "@binary_name_here@"}}"
86+
# DIRECT_INCLUDES
87+
# TRANSITIVE_INCLUDES
88+
# SYMBOLS
89+
# ARCHIVES
90+
# LOGIC_STRUCTURE
91+
)

scripts/cmake_easy_setup.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import subprocess
5+
import tempfile
6+
import os
7+
import pathlib
8+
import json
9+
from jinja2 import Environment, FileSystemLoader
10+
11+
parser = argparse.ArgumentParser()
12+
parser.add_argument("--cli", "-x", type=pathlib.Path, required=False, default="arduino-cli", help="path to arduino-cli")
13+
parser.add_argument("--board", "-b", type=str, default="", help="board name")
14+
parser.add_argument("--fire", action="store_true", default=False, help="launch the build immediately (use with caution!)")
15+
output_args = parser.add_mutually_exclusive_group(required=True)
16+
output_args.add_argument("--output", "-o", type=pathlib.Path, help="output file (CMake) with placeholders")
17+
output_args.add_argument("--sketch", "-s", type=pathlib.Path, help="output file (CMake) filled given a sketch folder")
18+
19+
shargs = parser.parse_args()
20+
21+
if shargs.sketch and not shargs.board :
22+
print("""
23+
Warning: you did not specify which board you were targeting;
24+
please review the generated CMakeLists.txt to remove the placeholder value before calling `cmake`.
25+
""")
26+
27+
def arduino(*args) :
28+
# return (out.stdout, logfile)
29+
# raises an exception if the command fails
30+
handle, logfile = tempfile.mkstemp()
31+
os.close(handle)
32+
out = subprocess.run(
33+
(shargs.cli, "--log-file", logfile, "--log-format", "json", *args),
34+
capture_output=True,
35+
encoding="utf-8",
36+
check=True,
37+
).stdout
38+
return (out, logfile)
39+
40+
def get_log(fname) :
41+
with open(fname) as file :
42+
for line in file :
43+
yield json.loads(line)
44+
45+
46+
_, logf = arduino("lib", "list")
47+
48+
libpaths = dict()
49+
for line in get_log(logf) :
50+
if line["msg"] == "Adding libraries dir" :
51+
libpaths[line["location"]] = pathlib.Path(line["dir"])
52+
53+
# platform lib path is already known, obviously, since that's where this script resides
54+
userlibs = pathlib.Path(libpaths["user"]).resolve()
55+
libs = [u.name for u in userlibs.iterdir() if u.is_dir()]
56+
corepath = pathlib.Path(__file__).parent.parent.resolve()
57+
58+
j2_env = Environment(
59+
loader=FileSystemLoader(str(corepath/"CI"/"update"/"templates")), trim_blocks=True, lstrip_blocks=True
60+
)
61+
cmake_template = j2_env.get_template("easy_cmake.cmake")
62+
63+
64+
userhome = pathlib.Path.home()
65+
if userlibs.is_relative_to(userhome) :
66+
userlibs = "$ENV{HOME}/" + str(userlibs.relative_to(userhome))
67+
if corepath.is_relative_to(userhome) :
68+
corepath = "$ENV{HOME}/" + str(corepath.relative_to(userhome))
69+
70+
if shargs.sketch :
71+
SOURCEFILE_EXTS = (".c", ".cpp", ".S", ".ino")
72+
sources = {
73+
file.relative_to(shargs.sketch)
74+
for file in shargs.sketch.glob("*")
75+
if file.is_file() and file.suffix in SOURCEFILE_EXTS
76+
}
77+
sources |= {
78+
file.relative_to(shargs.sketch)
79+
for file in shargs.sketch.rglob("src/*")
80+
if file.is_file() and file.suffix in SOURCEFILE_EXTS
81+
}
82+
tgtname = shargs.sketch.name
83+
else :
84+
tgtname = ""
85+
sources = set()
86+
87+
scriptname = pathlib.Path(__file__)
88+
if scriptname.is_relative_to(userhome) :
89+
scriptname = "~/" + str(scriptname.relative_to(userhome))
90+
91+
with open(shargs.output or shargs.sketch/"CMakeLists.txt", "w") as out :
92+
out.write(cmake_template.render(
93+
corepath=corepath,
94+
userlibs=userlibs,
95+
libs=libs,
96+
scriptfile=scriptname,
97+
tgtname=tgtname,
98+
scrcfiles=sources,
99+
boardname=shargs.board,
100+
))
101+
102+
103+
if shargs.fire :
104+
if not (shargs.sketch and shargs.board) :
105+
print("There remains some placeholder in the output file; I won't build _that_.")
106+
exit(1)
107+
subprocess.run(("cmake", "-S", shargs.sketch, "-B", shargs.sketch/"build"))
108+
subprocess.run(("cmake", "--build", shargs.sketch/"build"))

0 commit comments

Comments
 (0)