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

Reply via email to