/noa

A set of re-usable utilities for Sourcemeta projects

Primary LanguageCMakeGNU Affero General Public License v3.0AGPL-3.0

Noa

A set of re-usable and opinionated utilities for Sourcemeta projects.

Installation

We recommend using vendorpull to include Noa in your vendor directory.

Live at head

Like projects such as GoogleTest, Noa follows the Abseil Live at Head philosophy. We recommend always following the latest commit in the main branch.

CMake

To make use of Noa in a CMake project (after installing it into your project using vendorpull), add the following statements after the first call to project:

include(vendor/noa/cmake/noa.cmake)

Defaults

Noa will automatically set sane defaults for your project. You can check the list of applied defaults here. Note that these are only defaults. You can always override them after including Noa.

Functions

noa_library

Instantiate a C++ library with an opinionated structure and configuration.

noa_library(
  [NAMESPACE [namespace]]
  PROJECT [project]
  NAME [name]
  VARIANT [variant]
  FOLDER [folder]
  [PRIVATE_HEADERS [headers...]]
  [SOURCES [globs...]])

If NAMESPACE is declared, the files in PRIVATE_HEADERS are resolved relatively to include/<namespace>/<name>/<name>_. Otherwise, they are resolved relatively to include/<name>/<name>_.

If NAMESPACE is declared, the expected structure is as follows:

include/
  <namespace>/
    <project>/
      <name>.h
      <name>_<private_header>.h
<sources...>.cc
<sources...>.h

If NAMESPACE is not declared, the expected structure is as follows:

include/
  <project>/
    <name>.h
    <name>_<private_header>.h
<sources...>.cc
<sources...>.h

If VARIANT is declared, it allows for creating sub-libraries or variants of a main library. The expected structure changes as follows:

<name>/
  include/
    <namespace>/
      <project>/
        <name>.h
        <name>_<private_header>.h
  <variant>/
    <sources...>.cc
    <sources...>.h

If NAMESPACE is declared, calling this function will result in the following:

  • A target called <namespace>_<project>_<name>
  • An alias target called <namespace>::<project>::<name>

If NAMESPACE is not declared, calling this function will result in the following:

  • A target called <project>_<name>
  • An alias target called <project>::<name>

In all cases:

  • An export file on the library include path called <name>_export.h, if the library is not header-only
  • The version of the library corresponds to the project version, if the library is not header-only

noa_library_install

Declare installation of opinionated Noa libraries created with noa_library.

noa_library_install([NAMESPACE [namespace]] PROJECT [project] NAME [name])

If NAMESPACE is declared, calling this function will result in the following:

  • A <namespace>_<project>_<name> installation component
  • A <namespace>_<project>_<name>_dev installation component

If NAMESPACE is not declared, calling this function will result in the following:

  • A <project>_<name> installation component
  • A <project>_<name>_dev installation component

In both cases:

  • An export file at LIBDIR/cmake/<project>

noa_target_clang_format

Setup ClangFormat using an opinionated configuration file based on the LLVM coding standards.

noa_target_clang_format(SOURCES [globs...] [REQUIRED])

If the REQUIRED option is set and ClangFormat is not found, configuration will abort.

After running this function, you will have two targets at your disposal:

  • clang_format: Run the formatter on the files declared in the SOURCES option and modify them in place
  • clang_format_test: Run the formatter on the files declared in the SOURCES option in dry-mode, reporting if there is any deviation. This option is meant to be used in a continuous-integration environment

For example:

noa_target_clang_format(SOURCES src/*.h src/*.cc REQUIRED)

To run the targets:

cmake --build <dir> [<options>] --target clang_format
cmake --build <dir> [<options>] --target clang_format_test

noa_target_clang_tidy

Setup ClangTidy using an opinionated built-in configuration file.

noa_target_clang_tidy(SOURCES [globs...] [REQUIRED])

If the REQUIRED option is set and ClangTidy is not found, configuration will abort.

After running this function, you will have a new targets at your disposal:

  • clang_tidy: Run the analyzer on the files declared in the SOURCES option

For example:

noa_target_clang_tidy(SOURCES src/*.h src/*.cc REQUIRED)

To run the targets:

cmake --build <dir> [<options>] --target clang_tidy

noa_target_shellcheck

Setup ShellCheck.

noa_target_shellcheck(SOURCES [globs...] [REQUIRED])

If the REQUIRED option is set and ShellCheck is not found, configuration will abort.

After running this function, you will have a new targets at your disposal:

  • shellcheck: Run the linter on the files declared in the SOURCES option

For example:

noa_target_shellcheck(SOURCES scripts/*.sh REQUIRED)

To run the targets:

cmake --build <dir> [<options>] --target shellcheck

noa_target_doxygen

Setup Doxygen with a templated configuration file.

noa_target_doxygen(CONFIG [config] OUTPUT [output])

On your configuration file, make sure to set OUTPUT_DIRECTORY as follows:

OUTPUT_DIRECTORY       = @NOA_TARGET_DOXYGEN_OUTPUT@

After running this function, you will have a new target at your disposal:

  • doxygen: Run Doxygen and store the output in the OUTPUT directory

For example:

noa_target_doxygen(CONFIG "${PROJECT_SOURCE_DIR}/Doxyfile.in"
  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/docs")

To run the targets:

cmake --build <dir> [<options>] --target doxygen

noa_add_default_options

Configure a target with an opinionated set of strict compiler options. This function is used by default when making use of noa_library.

noa_add_default_options([visibility] [target])

For example:

noa_add_default_options(PUBLIC my_lib)

noa_add_vectorization_diagnostics

If possible, configure the given compiler to emit loop vectorization diagnostics during compilation.

noa_add_vectorization_diagnostics([target])

For example:

noa_add_vectorization_diagnostics(my_lib)

noa_sanitizer

Provides a unified interface for setting up a set of compiler sanitizers project-wide.

noa_sanitizer(NAME [sanitizer])

Supported sanitizers and their respective compilers are as follows:

Sanitizer Compiler Description
address LLVM Clang AddressSanitizer
memory LLVM Clang MemorySanitizer
undefined LLVM Clang UndefinedBehaviorSanitizer

For example:

noa_sanitizer(NAME address)

Tips & Tricks

  • To better debug issues raised by the undefined sanitizer on LLVM, run with LLDB along with the -fsanitize-trap=all compiler option and set the UBSAN_OPTIONS=print_stacktrace=1 environment variable

Commands

noa_command_copy_file

The built-in file command can be used to copy a file during the configure phase. Instead, this command copies a file at the build step to deal with generated files or as an optimization.

noa_command_copy_file(FROM [input] TO [output])

For example, you can declare a file to be copied at built-time, and then reference such output in a target for the copying to actually take place:

noa_command_copy_file(FROM input.txt TO "${CMAKE_CURRENT_BINARY_DIR}/output.txt")
add_custom_target(do_copy DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/output.txt")

It is highly recommended to always copy files into the binary directory.

Variables

Programming language

  • NOA_LANGUAGES: A list of the programming languages declared in the last project invocation

Compiler detection

  • NOA_COMPILER_LLVM: Set to ON if using the Clang or AppleClang compilers
  • NOA_COMPILER_GCC: Set to ON if using the GNU GCC compiler
  • NOA_COMPILER_MSVC: Set to ON if using the MSVC compiler

For example:

if(NOA_COMPILER_LLVM)
  add_compile_options([...])
endif()

Options

noa_option_enum

A shortcut for declaring CMake options that correspond to string enumerations.

noa_option_enum(NAME [name] DEFAULT [value] DESCRIPTION [description] CHOICES [choices...])

This function will validate that user provided values (and your own default value) matches the provided choices. It will also make sure to provide a nice selection interface in cmake-gui(1).

For example:

noa_option_enum(
  NAME MY_OPTION
  DEFAULT "foo"
  DESCRIPTION "A test enum"
  CHOICES foo bar baz)

Shims

CMake functionality shimmed to work on older versions: