#competitive-programming #source-file #cpp #programming #competitive #single-file #amalgamation

app cpp-amalgamate

cpp-amalgamate recursively combines C++ source files and the headers they include into a single output file

2 stable releases

1.0.1 Jun 7, 2022
1.0.0 Nov 17, 2021

#1207 in Development tools

MIT license

38KB
793 lines

cpp-amalgamate

Build status License Crates.io

cpp-amalgamate recursively combines C++ source files and the headers they include into a single output file. It tracks which headers have been included and skips any further references to them. Which includes are inlined and which are left as is can be precisely controlled.

It originated as an automated way to inline pre-written snippets when submitting to competitive programming sites such as Codeforces or AtCoder. Since then, it has been generalized and might be useful in other contexts as well.

Features & limitations

When provided with one or more source files and accompanying search directories for includes, cpp-amalgamate will concatenate the source files and recursively inline all include statements it can resolve using the given search directories (or from the current directory, for #include "..."). While, by default, all resolvable includes are inlined, this can be controlled using the --filter* family of options. It can also insert corresponding #line num "file" directives which allows compilers or debuggers to resolve lines in the combined file back to their origin.

However, cpp-amalgamate does not interpret preprocessor instructions beyond #include. This notably means that it cannot understand traditional header guards using #if instructions. Instead, cpp-amalgamate assumes that every header should be included at most once, as if it was guarded by a header guard or #pragma once. It does detect #pragma once instructions and removes them, as these cause warnings or errors when compiling the combined file with some compilers.

This simplified behavior also might cause problems if #include statements themselves are inside #if blocks. If the same header is referenced inside two separate #if blocks, it will only be expanded in the former while the latter #include will be removed.

Usage

The basic invocation for cpp-amalgamate is

cpp-amalgamate [options] source-files...

To specify search directories, use -d/--dir. You can also use --dir-quote or --dir-system for search directories that should only be used for quote (i.e., #include "...") or system includes (i.e., #include <...>). Note that cpp-amalgamate does not use any search directories by default!

Filtering

Using -f/--filter, you can specify globs for includes that should not be inlined. As with search directories, --filter-quote and --filter-system are versions only applicable to one type of include. Globs can be inverted with a leading !, causing matching headers to be inlined even if a previous glob excluded them. Globs are evaluated in order, with the last matching glob determining whether a header is included or not. By default (i.e., if no glob matches), all headers are inlined.

Note that these globs are applied to the absolute path of the header with all symbolic links resolved. This means that often a ** fragment will be necessary, which matches any number of path entries. That is,

  • ** matches any file,
  • **/*.hpp all files with the extension .hpp,
  • and /usr/local/include/** all files in /usr/local/include.

For the full details on the supported syntax, check the globset documentation.

Miscellaneous

Other flags supported by cpp-amalgamate are:

  • -o/--output: Write the combined source file to a file rather than the standard output.
  • --line-directives: Add #line num "file" directives to the output, allowing compilers and debuggers to resolve lines to their original files.
  • -v/--verbose and -q/--quiet: Increase or decrease the level of log messages shown. By default, only warnings and errors are shown.
  • --unresolvable-include: Specifies what is done when an include cannot be resolved. Possible values are error, warn, and ignore, with the latter being the default. This can be useful to assert that all includes end up inlined Also available as --unresolvable-quote-include and --unresolvable-system-include.
  • --cyclic-include: Specifies how a cyclic include is handled. Supports the same values as --unresolvable-include except with error as the default.

Installation

Each GitHub release contains precompiled binaries for most common operating systems/architectures. Alternatively, cpp-amalgamate can be installed using cargo which is bundled with Rust:

cargo install cpp-amalgamate

Dependencies

~9–22MB
~283K SLoC