Home / Alt manpages / aarch64-linux-gnu-cpp-13(1)

  • aarch64-linux-gnu-cpp-13(1)
  • User command
  • linux

Preprocess Cross-Compiled C with aarch64-linux-gnu-cpp-13

You will finish with a repeatable way to expand an AArch64 C or C++ source file, inspect the headers and macros selected by the cross toolchain, and write Make-compatible dependency data. The examples use the installed aarch64-linux-gnu-cpp-13 from GCC 13.3.0 on Ubuntu.

Allow about fifteen minutes. You need a shell and the cpp-aarch64-linux-gnu and cpp-13-aarch64-linux-gnu packages already installed. No command below needs root. This guide preprocesses source only: it does not compile, link, install headers or change a build.

1. Confirm the binary and version

Start with a read-only version check. The suffixed command makes the GCC major version explicit; the unsuffixed aarch64-linux-gnu-cpp alias is also installed on this machine.

$ command -v aarch64-linux-gnu-cpp-13
/usr/bin/aarch64-linux-gnu-cpp-13
$ aarch64-linux-gnu-cpp-13 --version
aarch64-linux-gnu-cpp-13 (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
$ aarch64-linux-gnu-cpp-13 -dumpmachine
aarch64-linux-gnu

The version matters when you are comparing generated output between build hosts. Do not assume that the host compiler and this target preprocessor share the same system include directories or predefined macros.

Checkpoint

If the command is missing, stop here and install the relevant cross-compiler package through your normal system administration process. Do not work around it by invoking the native cpp; that would select the host target.

2. Expand a small source file

cpp reads an input file and writes preprocessed text. -E is accepted by this driver and makes the intention clear. Use standard input for a quick smoke test, so the example changes no files:

$ printf '#define MESSAGE "AArch64"\nMESSAGE\n' |
  aarch64-linux-gnu-cpp-13 -E -P -
"AArch64"

The -P option removes the #line linemarkers that are normally useful to a later compiler. It makes a short inspection easier to read. Leave -P out when another compiler or tool needs accurate source locations.

For a real source file, put the output in a deliberately named temporary file or pipe it into the next tool:

$ aarch64-linux-gnu-cpp-13 -E -I ./include src/main.c > /tmp/main.i
$ test -s /tmp/main.i && echo "preprocessed output exists"
preprocessed output exists

That command does not overwrite src/main.c. Treat preprocessed output as an intermediate: it can be much larger than the input and can contain implementation details from system headers.

3. Select macros without editing source

Use -D for a macro supplied by the build and -U to cancel a definition. Options are processed from left to right, so a later option wins:

$ printf '#if FEATURE_LEVEL >= 2\nselected\n#else\nold\n#endif\n' |
  aarch64-linux-gnu-cpp-13 -E -P -DFEATURE_LEVEL=2 -
selected
$ printf '#if defined(TRACE)\ntrace\n#endif\n' |
  aarch64-linux-gnu-cpp-13 -E -P -DTRACE -UTRACE -

Quote function-like definitions because parentheses and shell metacharacters have meaning to the shell. For example, -D'PAIR(a,b)=((a)+(b))' is one argument. Keep configuration in the build command or an included header, rather than relying on a developer's ambient shell variables.

Checkpoint

If a conditional branch surprises you, print the active macro set with an empty input stream:

$ aarch64-linux-gnu-cpp-13 -dM - < /dev/null | grep -E '^#define __aarch64__|^#define __GNUC__ '
#define __GNUC__ 13
#define __aarch64__ 1

The list is target and version specific. Do not hard-code the order or assume every GCC installation defines the same implementation macros.

4. Make header lookup visible

Header failures are often search-path failures. Add -H to print each header used, with indentation showing nesting:

$ printf '#include <stdint.h>\n' |
  aarch64-linux-gnu-cpp-13 -E -H - 2>&1 | sed -n '1,2p'
. /usr/lib/gcc-cross/aarch64-linux-gnu/13/include/stdint.h
.. /usr/aarch64-linux-gnu/include/stdint.h

Exact paths and indentation depend on the installed package set, so use your output as the authority. For project headers, -I DIR is searched before standard system directories. -iquote DIR applies only to #include "file.h", while -isystem DIR marks a directory as a system header location. Prefer -isystem for vendor headers rather than pretending they are project headers.

Do not add a broad current-directory include path just to hide a missing header. It can make an unintended local file win over the target's intended header. Fix the include path or source reference, then rerun the -H check.

5. Generate dependencies for Make

Use -MMD when the build should track project headers but not system headers. Pair it with -MF for the dependency file and -MT for the target name. Replace the two source paths with paths from your build:

$ aarch64-linux-gnu-cpp-13 -MMD -MF /tmp/main.d -MT build/main.o src/main.c > /tmp/main.i
$ sed -n '1,3p' /tmp/main.d
build/main.o: src/main.c include/project-config.h

The displayed rule assumes that src/main.c includes include/project-config.h; your prerequisite list will differ. The output is a Make rule, not preprocessed C, so keep it in a separate .d file. -MM is the corresponding dependency mode that excludes system headers while using the normal preprocessor output mode; choose the spelling that matches the rest of your build and verify the generated rule.

This example writes only under /tmp. In a real build, use a private build directory and arrange for failed commands not to replace a known-good dependency file. Dependency files influence what gets rebuilt, so review a new path before feeding it into an automated build.

6. Diagnose the common failures

A missing header is a non-zero preprocessor result. Reproduce it with a deliberately absent name:

$ printf '#include "does-not-exist.h"\n' |
  aarch64-linux-gnu-cpp-13 -E - > /dev/null
<stdin>:1:10: fatal error: does-not-exist.h: No such file or directory
compilation terminated.

The diagnostic names the include and the source location. Add the specific project directory with -I, or correct the include spelling. Do not use -nostdinc as a generic repair: it disables standard system include directories and requires you to provide every required directory explicitly.

Remember that cpp is a C-family preprocessor, not a general text substitution tool. It interprets comments, character constants, directives and macro tokens. Running it over a Makefile can remove significant hard tabs, and apostrophes in unrelated text can be parsed as C syntax. Use a language-specific preprocessor or a proper text processor for non-C input.

If you must inspect the command without running a later compiler stage, the cross preprocessor is already the boundary: it reads the input and included files and emits text. No elevated privilege, service restart or persistent configuration change is involved, so the recovery is simply to remove temporary outputs such as /tmp/main.i and /tmp/main.d when they are no longer needed.

Done means

  • You confirmed aarch64-linux-gnu-cpp-13 is GCC 13.3.0 for the aarch64-linux-gnu target.
  • You can expand source with -E and choose whether -P linemarkers are useful.
  • You can verify command-line macro selection with -D, -U and -dM.
  • You can investigate header order with -H without guessing which file was selected.
  • You generate dependency rules separately from preprocessed output.
  • You can distinguish a missing include path from an unsuitable use of cpp.