Skip to content

Commit 49d5f70

Browse files
committed
examples/sotest: Make CMake fixture generation portable.
Replace the shell-based CMake fixture generation with Python helpers\nso the generated symtab and ROMFS sources are produced in a\nportable way across the supported build hosts.\n\nThis keeps the sotest packaged shared-library fixture flow aligned\nwith the existing Makefile path while making the generated outputs\nexplicit to CMake. Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
1 parent 76ab80f commit 49d5f70

3 files changed

Lines changed: 157 additions & 7 deletions

File tree

examples/sotest/main/CMakeLists.txt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ if(CONFIG_EXAMPLES_SOTEST)
2626
set(SOTEST_ROMFS_SRC ${CMAKE_CURRENT_BINARY_DIR}/sotest_romfs.c)
2727
set(SOTEST_MODPRINT_ELF ${CMAKE_BINARY_DIR}/bin/modprint)
2828
set(SOTEST_SHARED_ELF ${CMAKE_BINARY_DIR}/bin/sotest)
29+
set(SOTEST_GEN_SYMTAB ${CMAKE_CURRENT_SOURCE_DIR}/gen_symtab.py)
30+
set(SOTEST_GEN_ROMFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gen_romfs_source.py)
2931

3032
# Dynamic applications are wrapped in ELF_* helper targets when the build uses
3133
# the non-ELF-capable compiler path.
@@ -39,10 +41,10 @@ if(CONFIG_EXAMPLES_SOTEST)
3941

4042
add_custom_command(
4143
OUTPUT ${SOTEST_SYMTAB}
42-
COMMAND
43-
sh -c
44-
"\"${NUTTX_APPS_DIR}/tools/mksymtab.sh\" \"${SOTEST_MODPRINT_ELF}\" \"${SOTEST_SHARED_ELF}\" g_sot > \"${SOTEST_SYMTAB}\""
44+
COMMAND ${Python3_EXECUTABLE} ${SOTEST_GEN_SYMTAB} ${SOTEST_MODPRINT_ELF}
45+
${SOTEST_SHARED_ELF} g_sot ${SOTEST_SYMTAB}
4546
DEPENDS ${SOTEST_MODPRINT_TARGET} ${SOTEST_SHARED_TARGET}
47+
${SOTEST_GEN_SYMTAB}
4648
VERBATIM)
4749

4850
set_source_files_properties(${SOTEST_SYMTAB} PROPERTIES GENERATED TRUE)
@@ -80,10 +82,9 @@ if(CONFIG_EXAMPLES_SOTEST)
8082

8183
add_custom_command(
8284
OUTPUT ${SOTEST_ROMFS_SRC}
83-
COMMAND
84-
sh -c
85-
"echo '#include <nuttx/compiler.h>' > \"${SOTEST_ROMFS_SRC}\" && xxd -i \"${SOTEST_ROMFS_IMG}\" | sed -e 's/^unsigned char/const unsigned char aligned_data(4)/g' >> \"${SOTEST_ROMFS_SRC}\""
86-
DEPENDS ${SOTEST_ROMFS_IMG}
85+
COMMAND ${Python3_EXECUTABLE} ${SOTEST_GEN_ROMFS_SRC} ${SOTEST_ROMFS_IMG}
86+
${SOTEST_ROMFS_SRC}
87+
DEPENDS ${SOTEST_ROMFS_IMG} ${SOTEST_GEN_ROMFS_SRC}
8788
VERBATIM)
8889

8990
set_source_files_properties(${SOTEST_ROMFS_SRC} PROPERTIES GENERATED TRUE)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env python3
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import pathlib
6+
import sys
7+
8+
9+
def usage() -> int:
10+
print("usage: gen_romfs_source.py <input> <output>", file=sys.stderr)
11+
return 1
12+
13+
14+
def symbol_from_path(path: pathlib.Path) -> str:
15+
return path.name.replace(".", "_")
16+
17+
18+
def format_bytes(data: bytes) -> str:
19+
rows = []
20+
for index in range(0, len(data), 12):
21+
chunk = data[index : index + 12]
22+
rows.append(" " + ", ".join(f"0x{byte:02x}" for byte in chunk))
23+
return ",\n".join(rows)
24+
25+
26+
def write_output(input_path: pathlib.Path, output_path: pathlib.Path) -> None:
27+
symbol = symbol_from_path(input_path)
28+
data = input_path.read_bytes()
29+
text = "\n".join(
30+
[
31+
"#include <nuttx/compiler.h>",
32+
"",
33+
f"const unsigned char aligned_data(4) {symbol}[] = {{",
34+
format_bytes(data),
35+
"};",
36+
f"const unsigned int {symbol}_len = {len(data)};",
37+
"",
38+
]
39+
)
40+
41+
if output_path.exists() and output_path.read_text(encoding="utf-8") == text:
42+
return
43+
44+
output_path.write_text(text, encoding="utf-8")
45+
46+
47+
def main() -> int:
48+
if len(sys.argv) != 3:
49+
return usage()
50+
51+
input_path = pathlib.Path(sys.argv[1])
52+
output_path = pathlib.Path(sys.argv[2])
53+
write_output(input_path, output_path)
54+
return 0
55+
56+
57+
if __name__ == "__main__":
58+
raise SystemExit(main())

examples/sotest/main/gen_symtab.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env python3
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import pathlib
6+
import subprocess
7+
import sys
8+
9+
10+
def usage() -> int:
11+
print(
12+
"usage: gen_symtab.py <input> [<input> ...] <prefix> <output>",
13+
file=sys.stderr,
14+
)
15+
return 1
16+
17+
18+
def run_nm(path: pathlib.Path) -> subprocess.CompletedProcess[str]:
19+
return subprocess.run(
20+
["nm", str(path)],
21+
check=False,
22+
text=True,
23+
capture_output=True,
24+
)
25+
26+
27+
def symbol_name(line: str) -> str:
28+
return line.split()[-1].replace('"', "")
29+
30+
31+
def collect_symbols(paths):
32+
undefined: set[str] = set()
33+
defined: set[str] = set()
34+
35+
for path in paths:
36+
result = run_nm(path)
37+
if result.returncode not in (0, 1):
38+
raise RuntimeError(result.stderr.strip() or f"nm failed for {path}")
39+
40+
for line in result.stdout.splitlines():
41+
if " U " in line:
42+
undefined.add(symbol_name(line))
43+
elif line and not line.endswith(":"):
44+
defined.add(symbol_name(line))
45+
46+
return sorted(undefined - defined)
47+
48+
49+
def write_output(path: pathlib.Path, prefix: str, symbols) -> None:
50+
lines = [
51+
"#include <nuttx/compiler.h>",
52+
"#include <nuttx/symtab.h>",
53+
"",
54+
]
55+
56+
for symbol in symbols:
57+
lines.append(f"extern void *{symbol};")
58+
59+
lines.append("")
60+
lines.append(f"const struct symtab_s {prefix}_exports[] = ")
61+
lines.append("{")
62+
63+
for symbol in symbols:
64+
lines.append(f' {{"{symbol}", &{symbol}}},')
65+
66+
lines.append("};")
67+
lines.append("")
68+
lines.append(
69+
f"const int {prefix}_nexports = sizeof({prefix}_exports) / sizeof(struct symtab_s);"
70+
)
71+
lines.append("")
72+
73+
text = "\n".join(lines)
74+
if path.exists() and path.read_text(encoding="utf-8") == text:
75+
return
76+
77+
path.write_text(text, encoding="utf-8")
78+
79+
80+
def main() -> int:
81+
if len(sys.argv) < 4:
82+
return usage()
83+
84+
*input_paths, prefix, output = sys.argv[1:]
85+
symbols = collect_symbols([pathlib.Path(item) for item in input_paths])
86+
write_output(pathlib.Path(output), prefix, symbols)
87+
return 0
88+
89+
90+
if __name__ == "__main__":
91+
raise SystemExit(main())

0 commit comments

Comments
 (0)