From 1fc48dd785c6a6f2659ac4cd3c98030d1247eff6 Mon Sep 17 00:00:00 2001 From: Karel Kočí Date: Mon, 12 Oct 2026 22:31:15 +0200 Subject: Add presentation: 2026 Community Over Code Glasgow --- 2026-coc/pres.typ | 485 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 2026-coc/pres.typ (limited to '2026-coc/pres.typ') diff --git a/2026-coc/pres.typ b/2026-coc/pres.typ new file mode 100644 index 0000000..fbbf097 --- /dev/null +++ b/2026-coc/pres.typ @@ -0,0 +1,485 @@ +#import "@preview/polylux:0.4.0": * +#import "@preview/metropolis-polylux:0.1.0" as metropolis + +#show: metropolis.setup +#show: metropolis.setup.with(footer: [Firmware project with Meson build system]) +#show raw: set text(ligatures: false, features: (calt: 0)) + +#slide[ + #set page(header: none, footer: none, margin: 3em) + + #text(size: 1.3em)[*Firmware project with Meson build system*] + + Community Over Code Glasgow 2026 + + #metropolis.divider + + #set text(size: .8em, weight: "light") + Karel Kočí + + 13.10.2026 +] + +#slide[ + = The Point + + #set text(size: 0.95em) + POSIX gives ability to run same programs on NuttX and Linux. + + Advantages: + - Options when selecting libraries + - Easier and faster testing (including CI/CD) + - Possibility to test with tools such as Valgrind + + Workflow: + - Develop on PC including libraries + - Unit testing on PC + - Sw/sw integration on PC + - Sw/hw integration on real hardware + + We need a good way to build project for both targets (for NuttX flat build). +] + +#metropolis.new-section[NuttX external] + +#slide[ + = Standard NuttX + ``` + . + ├── nuttx + │   └── ... + └── apps + └── ... + ``` + \ + ```bash + cd nuttx + ./tools/configure.sh rv-virt:nsh + make + ``` +] + +#slide[ + = External Applications and Libraries + ``` + ├── nuttx + │   ├── external -> ../project-libs + │   └── ... + ├── apps + │   ├── external -> ../project-apps + │   └── ... + ├── project-libs + │   └── ... + └── project-apps + └── ... + ``` + #text(weight: "bold")[`configure-nuttx.sh`] + ```bash + ln -sf ../project-libs nuttx/external + ln -sf ../project-apps apps/external + ``` +] + +#slide[ + = Apps Directory + + #text(weight: "bold")[`project-apps/Make.defs`] + ```make + include $(wildcard $(APPDIR)/external/*/Make.defs) + ``` + + #text(weight: "bold")[`project-apps/Makefile`] + ```make + MENUDESC = "Project Applications" + include $(APPDIR)/Directory.mk + ``` +] + +#slide[ + = A Project App + + #text(weight: "bold")[`project-apps/project_hello/Make.defs`] + ```make + CONFIGURED_APPS += $(APPDIR)/external/project_hello + ``` + + #text(weight: "bold")[`project-apps/project_hello/Makefile`] + ```make + include $(APPDIR)/Make.defs + PROGNAME = $(CONFIG_NUTTX_USBNSH_PROGNAME) + PRIORITY = $(CONFIG_NUTTX_USBNSH_PRIORITY) + STACKSIZE = $(CONFIG_NUTTX_USBNSH_STACKSIZE) + MODULE = $(CONFIG_NUTTX_USBNSH) + MAINSRC = project_hello_main.c + include $(APPDIR)/Application.mk + ``` + + #text(weight: "bold")[`project-apps/project_hello/Kconfig`] ... +] + +#slide[ + = A Project App: In More Depth + #text(size: 0.9em)[ + ```make + all:: .built + context:: + register:: + $(call REGISTER,$(PROGNAME_$@),$(PRIORITY_$@),$(STACKSIZE_$@),$@_main) + + .built: $(AROBJS) + $(call ARLOCK, $(call CONVERT_PATH,$(BIN)), $^) + + CFLAGS += ${DEFINE_PREFIX}main=$(addsuffix _main,$(PROGSYM_$@)) + ``` + ] +] + +#slide[ + = External Board + + ```bash + mkdir board + cp -r nuttx/boards/risc-v/qemu-rv/rv-virt/{src,include,scripts} board/ + ``` + + #text(weight: "bold")[`board/defconfig`] (replace `CONFIG_ARCH_BOARD*`) + ```Kconfig + ... + CONFIG_ARCH_BOARD_CUSTOM=y + CONFIG_ARCH_BOARD_CUSTOM_DIR="../board" + CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y + CONFIG_ARCH_BOARD_CUSTOM_NAME="TEMPLATE" + ``` + + #text(weight: "bold")[`configure-nuttx.sh`] + ```bash + ... + cp -f board/defconfig nuttx/.config + make -C nuttx olddefconfig + ``` +] + +#slide[ + = Bonus Round: Config verification + ```bash + while read -r line; do + [[ "$cnf" == CONFIG_* ]] || continue + if [[ "${line#*=}" == "n" ]]; then + if grep -q "^${line%=*}=(y|m)$" nuttx/.config; then + echo "Not correctly set: $line" >&2; exit 1 + fi + else + if ! grep -qF "$line" nuttx/.config; then + echo "Not correctly set: $line" >&2; exit 1 + fi + fi + done <"board/defconfig" + done + ``` +] + +#metropolis.new-section[Meson] + +#slide[ + = Requirements for Build System + - Build libraries and applications + - No special rules in build system for NuttX + - Support for external projects with minimal NuttX boilerplate +] + +#slide[ + = Quick Overview + + Leading build system for building libraries and executables for Linux. + + \ + + #text(weight: "bold")[`meson.build`] + ```python + project('tutorial', 'c') + libfoo = library('foo', files('foo.c'), + include_directories=include_directories('.')) + executable('demo', files('main.c'), dependencies: [libfoo], + install: true) + ``` + \ + + Outputs ninja or other build systems. +] + +#slide[ + = Compare with make and cmake + #text(size: 0.9em)[ + #toolbox.side-by-side[ + #text(weight: "bold")[Make] + - 🙂 Generic build system (language) + - 😕 Can be tweaked to build almost anything + - 😢 Good build systems are complex + - 😢 Build targets standardization old and obsolete + - 🫣 No standard way to set toolchain + - 😕 No standard way to manage build time dependencies + ][ + #text(weight: "bold")[CMake] + - 🙂 Generic build system (language) + - 😕 Can be tweaked to build almost anything + - 😊 Has build target standardization + - 😢 Build target standardization is optional + - 😊 Toolchain.cmake + - 😢 Alternative ways to manage toolchain + - 😊 FetchContent + ] + ] +] +#slide[ + = While Meson... + - 😊 Can build only predefined types + - 😊 Can't be tweaked that much + - 🙃 Not a complete language + - 😊 crossfile.ini + - 😍 makes it hard to not use standard way + - 😊 subprojects with even cmake support +] + +#metropolis.new-section[Meson Integration] + +#slide[ + = Two Ways + #toolbox.side-by-side[ + #text(weight: "bold")[NuttX as project's library] + + Use exported libraries and linker script from NuttX in any other build + system. + + ```bash + make -C nuttx export + ``` + ][ + #text(weight: "bold")[Project as app in NuttX] + + NuttX build system deals with Firmware build. + + App integration uses our project's build system and adds results to NuttX + linkage. + ] +] + +#slide[ + = Phase 1: crossfile + #text(size: 0.8em)[ + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + crossfile.ini: .FORCE + ./crossfile.sh '$(CC)' '$(AR)' '$(CFLAGS)' + ``` + #text(weight: "bold")[`project-apps/meson-apps/crossfile.sh`] + ```bash + march="${3/*-march=/}"; march="${march/ */}" + flags_sanit() { sed -E "s#-Wshadow##;s#\"##g; s#-I #-I#g" <<<"$1" } + cat >crossfile.ini <<-EOF + [binaries] + c = '$1' + ar = '$2' + [built-in options] + c_args = [$(flags_sanit "$cflags" | sed -E "s#([^ ]+)#'\1'#g; s# +#, #g")] + c_link_args = ['-nostdlib', '-static', '-r'] + [host_machine] + system = 'nuttx' + cpu_family = '${1%%-*}' + cpu = '${march}' + EOF + ``` + ] +] +#slide[ + = Phase 1.1: Toolchain.cmake + #text(weight: "bold")[`project-apps/meson-apps/crossfile.sh`] + ```bash + cat >>crossfile.ini <<-EOF + [properties] + cmake_toolchain_file = '$(pwd)/Toolchain.cmake' + EOF + cat >Toolchain.cmake <<-EOF + add_compile_options($(flags_sanit "$cflags")) + add_link_options(-nostdlib -static -r) + EOF + ``` +] + +#slide[ + = Phase 2: context + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + context:: build/build.ninja + build/build.ninja: crossfile.ini meson-setup.sh + ./meson-setup.sh + ``` + #text(weight: "bold")[`project-apps/meson-apps/meson-setup.sh`] + ```bash + meson setup --default-library static -Db_staticpic=false \ + --backend ninja --cross-file crossfile.ini build ../.. + ``` +] + +#slide[ + = Phase 3: build / build.ninja.mk + #text(size: 0.8em)[ + #text(weight: "bold")[`project-apps/meson-apps/meson-setup.sh`] + ```make + { + meson introspect --targets "build" | + jq -r '.[] + | select((.type == "static library" or .type == "executable") and .installed) + | .name, .filename[0], .type' | + while read -r name && read -r file && read -r type; do + target="${file#"$sdir/build/"}" + if [[ "$type" == "executable" ]]; then + echo "\$(eval \$(call EXECUTABLE,$target,$name,${name//-/_}_main))" + else + echo "\$(eval \$(call STATIC_LIBRARY,$target))" + fi + done + meson introspect --buildsystem-files build | jq -r '.[]' | + sed 's#^#build/build.ninja: #' + } >"build.ninja.mk" + ``` + ] +] + +#slide[ + = Phase 3.1: built + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + all:: .built + @ + + .built: build.ninja.mk $(BUILDS) + touch $@ + ``` +] + +#slide[ + = Phase 3.2: build NINJA_TARGET + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + define NINJA_TARGET + + .NOTPARALLEL: build/$(1) + build/$(1): build/build.ninja .FORCE + flock build ninja $$(if $$(V),,--quiet) -C build $(1) + + endef + ``` +] + +#slide[ + = Phase 3.3: build STATIC_LIBRARY + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + define STATIC_LIBRARY + BUILDS += build/$(1).built + LIBRARIES += build/$(1) + $(call NINJA_TARGET,$(1)) + build/$(1).built: build/$(1) + mkdir -p build/$(1).a + $$(UNAR) --output build/$(1).a build/$(1) + cd build/$(1).a && for f in *.o; do mv "$$$$f" "$(subst $(DELIM),.,$(1))_$$f"; done + $$(call ARLOCK, $$(call CONVERT_PATH,$$(BIN)), build/$(1).a/*) + $$(call DELDIR, build/$(1).a) + touch $$@ + endef + ``` +] + +#slide[ + = Phase 3.4: libsyms + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + libsyms: $(LIBRARIES) + $(NM) --quiet --just-symbols --defined-only $^ | sort -u > $@ + ``` +] + +#slide[ + = Phase 3.5: register EXECUTABLE + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + define EXECUTABLE + + register:: $$(BUILTIN_REGISTRY)$$(DELIM)$(2).bdat + + $$(BUILTIN_REGISTRY)$$(DELIM)$(2).bdat: build/build.ninja + $$(call REGISTER,$(2),$$(PRIORITY),$$(STACKSIZE),$(3)) + + endef + ``` +] + +#slide[ + = Phase 3.5: build EXECUTABLE + #text(weight: "bold")[`project-apps/meson-apps/Makefile`] + ```make + define EXECUTABLE + BUILDS += build/$(1).built + $(call NINJA_TARGET,$(1)) + build/$(1).built: build/$(1) libsyms + $$(NM) --quiet --just-symbols --defined-only build/$(1) \ + | sort -u | comm -2 - libsyms >build/$(1).syms + $$(OBJCOPY) --redefine-sym main=$(3) \ + --localize-symbols=build/$(1).syms \ + --weaken-symbols=libsyms \ + build/$(1) build/$(1).o + $$(call ARLOCK, $$(call CONVERT_PATH,$$(BIN)), build/$(1).o) + touch $$@ + + ``` +] + +#slide[ + #show: metropolis.focus + + Demonstration +] + +#slide[ + = LTO + With LTO we can't modify symbols. + + Workaround: add `-flinker-output=nolto-rel` to `c_link_args` + + That removes LTO from linked objects objects (executables). + + LTO not applied to calls from executables to libraries and nuttx.\ + LTO is still applied to calls between libraries and from libraries to NuttX + +] + +#slide[ + = Conclusion + Demonstrated possibilities: + - How to have project specific applications + - How to use off-tree board + - How to convince meson to build for NuttX + - How to merge meson built files to NuttX + + All this without forking NuttX or Meson. + + Actual implementation provides resiliency against parameters change compared + to the examples. +] + +#slide[ + #show: metropolis.focus + Thank you + + Karel Kočí + + #metropolis.divider + + #text(size: 0.6em)[ + https://gitlab.com/Cynerd/nuttx-meson-template/-/tree/master + + https://gitlab.com/Cynerd/nixos-personal + + https://git.cynerd.cz/presentations/tree/2026-communitycvercode + ] +] -- cgit v1.3.1