This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit ac8c43942fb25a96e748a886eeeca0d9ce0f474d Author: Marco Casaroli <[email protected]> AuthorDate: Thu Oct 8 21:57:41 2026 +0200 tools/mkexport.sh: Add a compiler wrapper to the export package. make export now installs bin/nuttx-cc and bin/nuttx-c++ in the package, and writes the flags of the configuration to scripts/nuttx-cc.conf. A program outside the tree can then be built with a single compiler command: nuttx-cc -O2 -o hello hello.c ./configure --host=aarch64-none-elf CC=$EXPORT/bin/nuttx-cc The wrapper compiles with -nostdinc and the exported headers, so that a header that NuttX does not have is not taken from the C library of the toolchain. It keeps the CPU, ABI and define options and leaves out the warning options of the NuttX build. When it links, it adds crt0.o, gnu-elf.ld (if the package has it) and, in a kernel build, the user-space libraries. Paths into the build tree, which some boards put in LDELFFLAGS, are left out of the package. Build systems pass -pthread to any POSIX compiler, and long link commands in a response file (@file). The wrapper expands the response files before it looks at the options, and drops -pthread: libc has the POSIX threads. A kernel-build program is a relocatable (-r) ELF, and a relocatable link does not fail on an undefined symbol. In a kernel build nothing resolves the symbol at load time, so the wrapper checks with nm and fails the link. The wrapper finds the package from its own location, so the package can be moved to another computer. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- Documentation/components/tools/mkexport.rst | 4 + Documentation/components/tools/nuttx-cc.rst | 59 +++++++++ tools/mkexport.sh | 92 +++++++++++++ tools/nuttx-cc.sh | 199 ++++++++++++++++++++++++++++ 4 files changed, 354 insertions(+) diff --git a/Documentation/components/tools/mkexport.rst b/Documentation/components/tools/mkexport.rst index 41fe277b845..84c428a2ed4 100644 --- a/Documentation/components/tools/mkexport.rst +++ b/Documentation/components/tools/mkexport.rst @@ -13,3 +13,7 @@ USAGE: tools/mkexport.sh [-d] [-z] [-u] -t <top-dir> [-x <lib-ext>] -l "lib1 [li This script also depends on the environment variable MAKE which is set in the top-level Makefile before starting mkexport.sh. If MAKE is not defined, the script will set it to `which make`. + +The package also contains ``bin/nuttx-cc`` and ``bin/nuttx-c++``, a compiler +wrapper for programs that are built outside the NuttX tree. See +:doc:`nuttx-cc`. diff --git a/Documentation/components/tools/nuttx-cc.rst b/Documentation/components/tools/nuttx-cc.rst new file mode 100644 index 00000000000..ec037b2e43b --- /dev/null +++ b/Documentation/components/tools/nuttx-cc.rst @@ -0,0 +1,59 @@ +================= +``nuttx-cc.sh`` +================= + +A compiler wrapper for programs that are built outside the NuttX tree. +``make export`` installs it in the export package as ``bin/nuttx-cc`` (C) +and ``bin/nuttx-c++`` (C++), and writes the flags of the exported +configuration to ``scripts/nuttx-cc.conf``. + +Use the wrapper like a normal cross compiler. It finds the package from its +own location, so you can move the package to another directory or another +computer. The compiler named in the package (for example +``aarch64-none-elf-gcc``) must be on ``PATH`` there. To use a different one, +set ``NUTTX_CC`` or ``NUTTX_CXX``. + +.. code-block:: console + + $ make export + $ tar xzf nuttx-export-*.tar.gz + $ E=$PWD/nuttx-export-13.0.1 + $ $E/bin/nuttx-cc -O2 -o hello hello.c + +An autoconf package needs only the compiler and a host triple that +``config.sub`` knows: + +.. code-block:: console + + $ ./configure --host=aarch64-none-elf CC=$E/bin/nuttx-cc + $ make + +Copy the result to the target, for example to the directory that a hostfs or +v9fs mount shows, and run it from NSH. + +What the wrapper does +===================== + +- It compiles with the exported headers only (``-nostdinc``), plus the + compiler's own headers (``stddef.h``, ``arm_neon.h``, ...). A header that + NuttX does not have is reported missing. It is not taken from the C + library of the toolchain. +- It uses the CPU, ABI and define options of the configuration. It does not + use the warning options (for example ``-Werror``) of the NuttX build. +- It expands response files (``@file``) before it looks at the options. +- It accepts ``-pthread`` and drops it: libc has the POSIX threads. +- When it links, it adds ``startup/crt0.o``, ``scripts/gnu-elf.ld`` and, in a + kernel build (``CONFIG_BUILD_KERNEL``), the user-space libraries of the + package. +- In a kernel build, a link with an undefined symbol fails. A kernel-build + program is a relocatable (``-r``) ELF, and a relocatable link does not + report undefined symbols by itself. Without this check, every configure + test for a function would pass. + +Limits +====== + +- Only the GNU toolchain and kernel builds (``qemu-armv8a:knsh``) are + tested. +- In other builds the wrapper adds no libraries and does not check for + undefined symbols. diff --git a/tools/mkexport.sh b/tools/mkexport.sh index 3171330dbe0..9eb436c6670 100755 --- a/tools/mkexport.sh +++ b/tools/mkexport.sh @@ -539,6 +539,98 @@ cp -f "${TOPDIR}/tools/link.sh" "${EXPORTDIR}/tools/" cp -f "${TOPDIR}/tools/unlink.bat" "${EXPORTDIR}/tools/" cp -f "${TOPDIR}/tools/unlink.sh" "${EXPORTDIR}/tools/" +# Install the compiler wrapper for programs built outside the tree, and the +# flags it needs. Warning options are left out: they are this tree's policy +# (-Werror), not part of the ABI. -Wa, -Wl and -Wp pass options on and stay. +# Paths into this tree (some boards put crt0.o in LDELFFLAGS) are left out: +# they do not exist where the package is used, and the wrapper adds the +# exported crt0.o itself. + +noWarnings() { + for flag in $1; do + case ${flag} in + -Wa,* | -Wl,* | -Wp,*) printf '%s ' "${flag}" ;; + -W*) ;; + *) printf '%s ' "${flag}" ;; + esac + done +} + +noTreePaths() { + for flag in $1; do + case ${flag} in + "${TOPDIR}"/*) ;; + *) printf '%s ' "${flag}" ;; + esac + done +} + +isSet() { + grep -q "^$1=y" "${TOPDIR}/.config" +} + +CCDEFINES="-D__NuttX__" +if isSet CONFIG_NDEBUG; then + CCDEFINES="${CCDEFINES} -DNDEBUG" +fi + +# The C++ headers, as ARCHXXINCLUDES in tools/Config.mk has them. + +CCXXINCDIRS= +if isSet CONFIG_LIBCXX; then + CCXXINCDIRS="libcxx" +elif isSet CONFIG_UCLIBCXX; then + CCXXINCDIRS="uClibc++" +elif isSet CONFIG_LIBMINIABI; then + CCXXINCDIRS="${CCXXINCDIRS} cxx" + if isSet CONFIG_ETL; then + CCXXINCDIRS="${CCXXINCDIRS} etl" + fi +fi +if isSet CONFIG_LIBCXXABI; then + CCXXINCDIRS="${CCXXINCDIRS} libcxxabi" +fi + +CCLIBS= +CCNOUNDEF=n +if [ "${NUTTX_BUILD}" = "kernel" ]; then + CCNOUNDEF=y + if isSet CONFIG_NX; then + CCLIBS="nx" + fi + CCLIBS="${CCLIBS} mm c proxies" + if isSet CONFIG_HAVE_CXX; then + CCLIBS="${CCLIBS} xx" + fi + if [ -f "${EXPORTDIR}/libs/libm${LIBEXT}" ]; then + CCLIBS="${CCLIBS} m" + fi + CCLIBS="${CCLIBS} gcc" +fi + +CCLDSCRIPT= +if [ -f "${EXPORTDIR}/scripts/${APPLD}" ]; then + CCLDSCRIPT=${APPLD} +fi + +{ + echo "NUTTX_CC=\${NUTTX_CC:-'${CC}'}" + echo "NUTTX_CXX=\${NUTTX_CXX:-'${CXX}'}" + echo "NUTTX_CPUFLAGS='${ARCHCPUFLAGS}'" + echo "NUTTX_CFLAGS='$(noWarnings "${ARCHCFLAGS}")${ARCHPICFLAGS} ${CCDEFINES}'" + echo "NUTTX_CXXFLAGS='$(noWarnings "${ARCHCXXFLAGS}")${ARCHPICFLAGS} ${CCDEFINES}'" + echo "NUTTX_CXXINCDIRS='${CCXXINCDIRS}'" + echo "NUTTX_LDFLAGS='$(noTreePaths "${LDELFFLAGS}")-Bstatic'" + echo "NUTTX_LDSCRIPT='${CCLDSCRIPT}'" + echo "NUTTX_LIBS='${CCLIBS}'" + echo "NUTTX_NOUNDEF='${CCNOUNDEF}'" +} >"${EXPORTDIR}/scripts/nuttx-cc.conf" + +mkdir -p "${EXPORTDIR}/bin" +cp -f "${TOPDIR}/tools/nuttx-cc.sh" "${EXPORTDIR}/bin/nuttx-cc" +cp -f "${TOPDIR}/tools/nuttx-cc.sh" "${EXPORTDIR}/bin/nuttx-c++" +chmod 755 "${EXPORTDIR}/bin/nuttx-cc" "${EXPORTDIR}/bin/nuttx-c++" + # Now tar up the whole export directory cd "${TOPDIR}" || \ diff --git a/tools/nuttx-cc.sh b/tools/nuttx-cc.sh new file mode 100755 index 00000000000..900bc7cb3fa --- /dev/null +++ b/tools/nuttx-cc.sh @@ -0,0 +1,199 @@ +#!/bin/sh +# tools/nuttx-cc.sh +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +# Compiler wrapper for programs built outside the NuttX tree. +# +# tools/mkexport.sh installs this script as bin/nuttx-cc and bin/nuttx-c++ +# in the export package, next to scripts/nuttx-cc.conf, which holds the +# flags of the exported configuration. Use it like a normal compiler: +# +# nuttx-cc -O2 -c hello.c +# nuttx-cc -o hello hello.o +# ./configure --host=aarch64-none-elf CC=/path/to/export/bin/nuttx-cc +# +# It compiles against the exported headers only (-nostdinc), so a header +# that NuttX does not have is reported missing instead of being taken from +# the toolchain's own C library. When it links, it adds the startup +# object, the exported linker script and the exported libraries. +# +# NUTTX_CC and NUTTX_CXX override the compiler named in the package. + +# Find the export package from the location of this script + +case $0 in + */*) bindir=${0%/*} ;; + *) bindir=$(dirname "$(command -v "$0")") ;; +esac + +export_dir=$(cd "${bindir}/.." && pwd) || exit 1 +conf=${export_dir}/scripts/nuttx-cc.conf + +if [ ! -r "${conf}" ]; then + echo "nuttx-cc: ${conf} not found" >&2 + exit 1 +fi + +. "${conf}" + +# C or C++, from the name we were called by + +case ${0##*/} in + *++*) + cc=${NUTTX_CXX} + cflags="${NUTTX_CXXFLAGS}" + for dir in ${NUTTX_CXXINCDIRS}; do + cflags="${cflags} -isystem ${export_dir}/include/${dir}" + done + ;; + *) + cc=${NUTTX_CC} + cflags="${NUTTX_CFLAGS}" + ;; +esac + +# Use only the exported headers plus the compiler's own (stddef.h, +# arm_neon.h, ...). + +builtin=$(${cc} ${NUTTX_CPUFLAGS} -print-file-name=include) +cflags="${cflags} -isystem ${export_dir}/include" +if [ -d "${builtin}" ]; then + cflags="-nostdinc ${cflags} -isystem ${builtin}" +fi + +# Expand the response files (@file) here, so that the checks below see the +# options in them. +# +# libc has the POSIX threads, so -pthread needs no library. Accept it and +# drop it: build systems pass it to any POSIX compiler. + +for arg in "$@"; do + shift + case ${arg} in + -pthread) + continue + ;; + @*) + if [ -r "${arg#@}" ]; then + while IFS= read -r opt; do + [ -z "${opt}" ] || [ "${opt}" = "-pthread" ] || + set -- "$@" "${opt}" + done <<EOF +$(xargs printf '%s\n' < "${arg#@}") +EOF + continue + fi + ;; + esac + set -- "$@" "${arg}" +done + +# Do we link? Not with -c, -S, -E or -M/-MM, and not when there is no +# input file at all (--version, -print-file-name=..., -dumpmachine). + +link=n +skip=n +out=a.out +for arg in "$@"; do + if [ ${skip} = o ]; then + out=${arg} + skip=n + continue + elif [ ${skip} = y ]; then + skip=n + continue + fi + + case ${arg} in + -c | -S | -E | -M | -MM) + link=never + ;; + -o) + skip=o + ;; + -o*) + out=${arg#-o} + ;; + -x | -MF | -MT | -MQ | -I | -L | -D | -U | -T | -l | -u | -e | \ + -include | -imacros | -isystem | -idirafter | -iquote | -iprefix | \ + -Xlinker | -Xassembler | -Xpreprocessor | --param) + skip=y + ;; + -*) + ;; + *) + [ ${link} = n ] && link=y + ;; + esac +done + +if [ ${link} != y ]; then + exec ${cc} ${NUTTX_CPUFLAGS} ${cflags} "$@" +fi + +# Link: crt0, the user's objects and libraries, then the NuttX libraries. +# The ld options in the package are passed one by one through -Xlinker. + +ldflags= +for opt in ${NUTTX_LDFLAGS}; do + ldflags="${ldflags} -Xlinker ${opt}" +done + +if [ -n "${NUTTX_LDSCRIPT}" ]; then + ldflags="${ldflags} -T ${export_dir}/scripts/${NUTTX_LDSCRIPT}" +fi + +libs= +for lib in ${NUTTX_LIBS}; do + libs="${libs} -l${lib}" +done + +${cc} ${NUTTX_CPUFLAGS} ${cflags} -nostdlib -nostartfiles ${ldflags} \ + "${export_dir}/startup/crt0.o" "$@" -L "${export_dir}/libs" \ + -Wl,--start-group ${libs} -Wl,--end-group || exit + +# A relocatable (-r) link never fails on an undefined symbol. In a kernel +# build nothing is left to resolve it at load time, so fail here, as a +# normal link would. Configure scripts depend on this to find out which +# functions exist. + +if [ "${NUTTX_NOUNDEF}" = y ]; then + syms=$("$(${cc} -print-prog-name=nm)" -u "${out}") || exit + + # Only a strong reference (U) must be resolved. A weak one (w, v) may + # stay undefined and then has the value 0, as in a normal link. + + undef= + while read -r type sym; do + if [ "${type}" = U ]; then + undef="${undef} ${sym} +" + fi + done <<EOF +${syms} +EOF + + if [ -n "${undef}" ]; then + echo "nuttx-cc: ${out}: undefined references:" >&2 + printf '%s' "${undef}" >&2 + rm -f "${out}" + exit 1 + fi +fi
