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

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

Preprocess C and C++ Safely with x86_64-linux-gnu-cpp-13

A macro expands to the wrong value and the compiler error points nowhere useful, so run it through x86_64-linux-gnu-cpp-13 first. You can see exactly what the compiler sees, then control macros and include paths, and produce a Make dependency file. Allow about 10 minutes if the source file already exists.

Before you start

You need a shell, the installed command, and a C or C++ source file you are allowed to read. The examples target Ubuntu's x86_64-linux-gnu-cpp-13, provided by GCC 13.3.0 packages. Check the version and executable location first:

x86_64-linux-gnu-cpp-13 --version
command -v x86_64-linux-gnu-cpp-13

On the target system this reports Ubuntu's GCC 13.3.0 build and normally resolves to /usr/bin/x86_64-linux-gnu-cpp-13. The unversioned x86_64-linux-gnu-cpp alias reports the same installed GCC build here.

Checkpoint

Continue only when the command exists and its version is the one you intend to document or reproduce.

1. Expand a source file to standard output

Create a small input in a disposable directory, or substitute a real source path. This example uses a temporary file and sends the result to standard output, so it changes no project files:

work=/tmp/cpp-guide
mkdir -p "$work"
printf '%s\n' '#define LIMIT 3' 'int values[LIMIT];' > "$work/sample.c"
x86_64-linux-gnu-cpp-13 -P "$work/sample.c"

The output contains the declaration with LIMIT expanded to 3. The -P option suppresses preprocessor linemarkers, which makes output easier to read when another tool expects source-like text.

Without -P, CPP normally emits linemarkers so a compiler can retain the original file and line locations. Do not treat preprocessed output as a replacement source file unless the next tool expects it.

Tip

Rerun the command without -P if you need to diagnose where a generated line came from.

2. Select behaviour with macros and headers

Use -D to define a macro before processing and -U to cancel a previous definition. Options are applied in the order given, so a later -U can remove a definition made by an earlier -D. Keep shell quoting around definitions that contain spaces or shell metacharacters.

printf '%s\n' '#if FEATURE_LEVEL >= 2' 'const char *mode = "new";' '#else' 'const char *mode = "old";' '#endif' > "$work/feature.c"
x86_64-linux-gnu-cpp-13 -P -DFEATURE_LEVEL=2 "$work/feature.c"

Expected output is:

const char *mode = "new";

For project headers, -I adds a directory to the include search path for both quoted and angle-bracket includes. -iquote applies only to quoted includes. The quoted form searches the current file's directory first, then quote directories, then -I directories and system directories. This order is a common source of accidental header shadowing.

mkdir -p "$work/include"
printf '%s\n' '#define MESSAGE "from local header"' > "$work/include/settings.h"
printf '%s\n' '#include "settings.h"' 'const char *message = MESSAGE;' > "$work/header.c"
x86_64-linux-gnu-cpp-13 -P -I "$work/include" "$work/header.c"

Expected output is:

const char *message = "from local header";

Safety boundary

Do not use -I casually to replace vendor or system headers. The manpage recommends -isystem for vendor-supplied system headers, because it preserves system-header treatment and avoids surprising include ordering.

3. Save output without overwriting the input

Use -o to write preprocessed output to a separate file. The command accepts standard input or standard output as -, and omitted input or output is treated similarly. Make the destination explicit when scripting so a misplaced argument cannot silently replace an important file.

x86_64-linux-gnu-cpp-13 -P -I "$work/include" "$work/header.c" -o "$work/header.i"
test -s "$work/header.i" && sed -n '1,5p' "$work/header.i"

There is no elevated-privilege step here. If the destination is owned by another user, fix the path or permissions rather than reaching for sudo.

Warning

The cleanup command below permanently removes files under the named temporary directory. Check the path before running it: rm -rf /tmp/cpp-guide. Use the actual disposable directory if you used the commands above.

4. Generate Make dependencies

For build automation, -MMD produces dependencies for user headers while excluding system headers. -MF chooses the dependency file. Use -MT when the Make target is not the default object name.

x86_64-linux-gnu-cpp-13 -MMD -MF "$work/header.d" -MT 'build/header.o' -I "$work/include" "$work/header.c"
sed -n '1,5p' "$work/header.d"

The dependency rule names build/header.o, the source file and settings.h. If you need system headers included too, use -MD instead. If a generated header is not present yet, -MG can add it to dependency output without raising an error, but use that only when the build has a real generation step.

Do not confuse these modes: -M and -MM emit dependency rules instead of normal preprocessed output. -MF keeps those rules out of the normal output stream. A dependency file is build metadata, not compilable C.

5. Investigate a failing include or macro

Use -H to print each header as it is read, with indentation showing nesting. Use -dM to list the macros defined during processing, including predefined macros. These are diagnostic views, so capture them separately when another tool consumes standard output.

x86_64-linux-gnu-cpp-13 -H -P -I "$work/include" "$work/header.c" > "$work/expanded.txt" 2> "$work/headers.txt"
sed -n '1,5p' "$work/headers.txt"
x86_64-linux-gnu-cpp-13 -dM - < /dev/null | grep -E '^#define (__GNUC__|__x86_64__)'

If an undefined identifier is evaluated in an #if, CPP treats it as zero. Add -Wundef when that default could hide a typo. For strict standard diagnostics, select the relevant -std=c90, -std=c99, -std=c11 or -std=c17 mode and add -pedantic when required. The exact language standard belongs to the project, not to a generic troubleshooting command.

Common traps

  • CPP is a lexical tool, not a general text processor. It is for C, C++ and Objective-C input. It can damage formats such as Makefiles by removing significant tabs.
  • Do not group multi-letter options. -dM is not equivalent to -d -M.
  • -C is not harmless. Preserving comments can make comments act as tokens and change how directives are recognised.
  • Do not edit system headers to repair an include problem. Inspect the search order with -H, then correct -I, -iquote or the build configuration.

Done means

  • Version confirmed. The installed GCC 13.3.0 preprocessor version is confirmed.
  • Basic expansion works. A disposable source file expands to the expected output.
  • Macros and includes verified. Macro and include-path choices are explicit and verified.
  • Output separated. Preprocessed output and Make dependencies are written to separate files.
  • Diagnostics captured cleanly. Header and macro diagnostics can be captured without contaminating build output.