ct-magicflags

Show the magic flags / magic comments that a file exports

Author:

drgeoffathome@gmail.com

Date:

2018-02-23

Copyright:

Copyright (C) 2011-2018 Zomojo Pty Ltd

Version:

13.1.4

Manual section:

1

Manual group:

developers

SYNOPSIS

ct-magicflags [-h] [-c CONFIG_FILE] [–headerdeps {direct,cpp}]

[–variant VARIANT] [-v] [-q] [–version] [-?] [–ID ID] [–CPP CPP] [–CC CC] [–CXX CXX] [–CPPFLAGS CPPFLAGS] [–CXXFLAGS CXXFLAGS] [–CFLAGS CFLAGS] [–git-root | –no-git-root] [–include [INCLUDE [INCLUDE …]]] [–shorten | –no-shorten] [–magic {cpp,direct}] [–style {null,pretty}] filename [filename …]

DESCRIPTION

ct-magicflags extracts the magic flags/magic comments from a given file. It is mostly used for debugging purposes so that you can see what the other compiletools will be using as the magic flags. A magic flag / magic comment is simply a C++ style comment that provides information required to complete the build process.

compiletools works very differently to other build systems, because compiletools expects that the compiler/link flags will be directly in the source code. For example, if you have written your own “compress.hpp” that requires linking against libzip you would normally specify “-lzip” in your Makefile (or build system) on the link line. However, compiletools based applications add the following comment in the file that includes:

//#LDFLAGS=-lzip

For easy maintainence, it is convenient to put the magic flag directly after the include:

#include <zip.h>
//#LDFLAGS=-lzip

Whenever “compress.hpp” is included (either directly or indirectly), the “-lzip” will be automatically added to the link step. If you stop using the header, for a particular executable, compiletools will figure that out and stop linking against libzip.

If you want to compile a cpp file with a particular optimization enabled you would add something like:

//#CXXFLAGS=-fstrict-aliasing

Because the code and build flags are defined so close to each other, it is much easier to tweak the compilation locally and allow for easier maintainence.

Using PKG-CONFIG

Instead of manually specifying compiler and linker flags, you can use pkg-config to automatically extract the correct flags for a library. For example, with zlib:

//#PKG-CONFIG=zlib
#include <zlib.h>

This single line automatically adds both the compilation flags (from pkg-config --cflags zlib) and link flags (from pkg-config --libs zlib) to your build. This approach is preferred over manual LDFLAGS because:

  • It’s more portable across different systems and distributions

  • It automatically includes the correct include paths

  • It handles library dependencies correctly

  • It adapts to different installation locations

The PKG-CONFIG magic flag works with any library that provides a .pc file, including common libraries like gtk+-3.0, libpng, libcurl, openssl, and many more.

Version Constraints

A package may carry a version constraint (=, ==, !=, <, <=, >, >=). The operator and version stay attached to the package they qualify, so a constrained package is still one package and is passed to pkg-config intact:

//#PKG-CONFIG=zlib >= 1.2

Spaces are required on both sides of the operator. Package, operator and version must be three whitespace-separated words. Omitting either space changes what pkg-config resolves, and in one case does so without saying anything, so those spellings are rejected as a malformed package specification before pkg-config is ever invoked. “Rejected” means a diagnostic naming the specification and zero contributed flags, gated by --pkg-config-errors (default warn, build continues; error raises and stops the build on first failure — see “Strict pkg-config failures” in ct-config(1)):

//#PKG-CONFIG=zlib >= 1.2   // the only spelling that enforces what you wrote
//#PKG-CONFIG=zlib >=1.2    // rejected: pkg-config would eat the "1" into
                            // the operator and enforce ">= .2"
//#PKG-CONFIG=zlib>= 1.2    // rejected: pkg-config would read two packages,
                            // "zlib>=" and "1.2"
//#PKG-CONFIG=>= 1.2        // rejected: operator with no package
//#PKG-CONFIG=zlib >=       // rejected: operator with no version

The fully attached form is not a constraint and is not malformed: //#PKG-CONFIG=zlib>=1.2 asks for one package literally named zlib>=1.2, which pkg-config reports as missing.

Commas separate packages here just as whitespace does, so //#PKG-CONFIG=zlib >= 1.2, libpng is two packages. A specification never reaches across a comma to find a missing operand: //#PKG-CONFIG=zlib >=, libpng is a malformed zlib >= plus an ordinary libpng.

This is the same tokenizer the conf-file pkg-config = ... key uses, so the two surfaces agree on what counts as one package. See ct-config(1) for the conf-file side.

Project-Level PKG-CONFIG Overrides

If your project needs custom or patched .pc files (e.g., to pin a library version, override flags, or provide a package that isn’t installed system-wide), place them in ct.conf.d/pkgconfig/ at the root of your git repository:

myproject/
    ct.conf.d/
        pkgconfig/
            zlib.pc        # overrides the system zlib.pc
            mylib.pc       # provides a package not installed system-wide
    src/
        main.cpp

compiletools prepends this directory to PKG_CONFIG_PATH before any pkg-config invocation, so project-level .pc files take priority over system-installed ones. This follows the same layering principle as the rest of the configuration system (project config overrides system config).

In monorepos, a subdirectory can have its own ct.conf.d/pkgconfig/ that takes priority over the repo-wide one. When ct-cake is invoked from a subdirectory that contains ct.conf.d/pkgconfig/, the resulting PKG_CONFIG_PATH priority is:

cwd/ct.conf.d/pkgconfig/  >  gitroot/ct.conf.d/pkgconfig/  >  system

This lets subprojects override repo-wide .pc files without shell-script workarounds.

For one-off overrides that cannot be committed (e.g., CI pipelines, temporary debugging, or machine-specific paths), use the --prepend-PKG-CONFIG-PATH and --append-PKG-CONFIG-PATH command-line flags. The complete priority order is:

--prepend-PKG-CONFIG-PATH  >  cwd/ct.conf.d/pkgconfig/  >  gitroot/ct.conf.d/pkgconfig/  >  $PKG_CONFIG_PATH  >  --append-PKG-CONFIG-PATH

One process-lifetime caveat: a package’s existence verdict and flag output are memoised per (package, query) — not per PKG_CONFIG_PATH — so a long-lived embedder that changes the path between builds in the same process keeps the first verdict for packages already probed, until something clears the pkg-config caches (apptools.clear_cache). Ordinary CLI invocations are unaffected — each run is a fresh process.

Use --prepend-PKG-CONFIG-PATH to override all project and system paths (e.g., ct-cake --prepend-PKG-CONFIG-PATH=/opt/custom/pkgconfig to test a patched version). Use --append-PKG-CONFIG-PATH as a fallback for system-wide or user-local package paths not in the environment.

prepend-PKG-CONFIG-PATH set in any conf file accumulates across the conf hierarchy. When multiple confs contribute entries (e.g. a project ct.conf plus axis confs composed by --variant), the higher-priority conf-file entry wins over the lower-priority one — so an axis conf that pins a build-flavor-specific .pc directory overrides a global ct.conf’s prepend. CLI --prepend-PKG-CONFIG-PATH wins over all conf-file entries. append-PKG-CONFIG-PATH follows the symmetric ordering at the rear.

CPPFLAGS and CXXFLAGS Unification

By default, CPPFLAGS and CXXFLAGS magic flags are merged into a single deduplicated set. A flag set via //#CPPFLAGS= is also applied as a CXXFLAGS and vice versa. Use --separate-flags-CPP-CXX on the command line to keep them separate.

VALID MAGIC FLAGS

A magic flag follows the pattern //#key=value. Whitespace around the equal sign is acceptable.

The known magic flags are:

===========  ==============================================================
Key          Description
===========  ==============================================================
CPPFLAGS     C Pre Processor flags
CFLAGS       C compiler flags
CXXFLAGS     C++ flags (do not confuse these with the C PreProcessor flags)
INCLUDE      Specify include paths without "-I".
             Adds the path to CPPFLAGS, CFLAGS and CXXFLAGS.
LDFLAGS      Linker flags
LINKFLAGS    Linker flags (deprecated, use LDFLAGS)
SOURCE       Inject an extra source file into the list of files to be built.
             This is most commonly used in cross platform work.
PKG-CONFIG   Extract the cflags and libs using pkg-config.
             Multiple packages on one line (``PKG-CONFIG=a b``)
             create hard link-order constraints between them.
             Whitespace and commas both separate packages; a
             package may carry a version constraint written as
             three spaced words (``zlib >= 1.2``).
PCH          Precompiled header. Specifies a header to precompile into a
             .gch file. The path is resolved relative to the source file.
READMACROS   Read macro definitions from specified file before evaluating
             conditional compilation. Useful for system headers.
GIT          Declare an external git repository to fold into the build
             (``//#GIT=<url>[@<ref>]``). compiletools clones it and adds
             its root (and ``include/``) to the include path. Consumed by
             the fetch step, not the flag slots. See ct-fetch(1).
===========  ==============================================================

Note: Magic flags with arbitrary keys (not listed above) are also accepted and will be passed through to the output. This will allow for project-specific extensions in the future.

Multi-Package PKG-CONFIG

When a source file depends on multiple packages and their relative link order matters, list them in a single annotation:

//#PKG-CONFIG=libssh2 numa

This tells compiletools that libssh2 must be linked before numa. The ordering is treated as a hard constraint — it will never be silently discarded, and contradictory hard constraints between files are reported as a cyclic-dependency error.

“Multiple packages” is counted after version constraints are parsed, not by counting whitespace-separated words. //#PKG-CONFIG=zlib >= 1.2 numa is two packages and yields the single hard edge zlib → numa; the >= and the 1.2 are part of the zlib specification and never become orderable packages of their own.

Compare this with two separate single-package annotations:

//#PKG-CONFIG=libssh2
//#PKG-CONFIG=numa

Here each package’s pkg-config --libs output is collected independently. The ordering between libssh2 and numa flags is a soft constraint — if another file’s pkg-config output implies the opposite order (common with shared transitive dependencies like -lssl/-lcrypto), compiletools cancels both directions and picks a deterministic order instead of raising an error.

Rule of thumb: use a single multi-package annotation when you know the link order matters; use separate annotations when you just need both packages and don’t care about their relative order.

IMPORTANT: Library Linking

compiletools does not automatically detect library requirements from includes. For example, #include <pthread.h> does NOT automatically add -lpthread. All library linking must be explicitly specified using either:

  • //#LDFLAGS=-lpthread for direct library specification

  • //#PKG-CONFIG=libname for pkg-config managed libraries

Using READMACROS

The READMACROS magic flag allows extracting macro definitions from a file before evaluating conditional compilation. This is useful when magic flags depend on macros defined in system headers that aren’t in the include path.

#include <fake_system_include/system/version.h>
//#READMACROS=fake_system_include/system/version.h

#if SYSTEM_VERSION_MAJOR >= 2
//#CPPFLAGS=-DSYSTEM_ENABLE_V2
#else
//#CPPFLAGS=-DUSE_LEGACY_API
#endif

An absolute path is used as-is. A relative path is searched for in this order:

  1. The global include directories (-I / -isystem from the command line and config files).

  2. The include directories declared by magic flags anywhere in the same translation unit (//#CPPFLAGS=-isystem ..., //#INCLUDE=, //#PKG-CONFIG= cflags), the declaring file’s own first. This mirrors the real compile line, where magic flags from every file of the translation unit are aggregated onto one command — so the -isystem that locates the file may live in a different header than the READMACROS declaration itself.

  3. The directory of the source file containing the READMACROS flag.

Using PCH (Precompiled Headers)

The PCH magic flag marks a header for precompilation. The build system compiles the header into a .gch file and ensures it is built before any source that references it.

//#PCH=stdafx.h
#include "stdafx.h"

int main() { return 0; }

The header path is resolved relative to the source file containing the annotation, matching SOURCE semantics. Absolute paths are also accepted.

EXAMPLES

  • ct-magicflags main.cpp

  • ct-magicflags –variant=release main.cpp

SEE ALSO

compiletools (1), ct-cake (1)