Add 'xo-indentlog/' from commit '624178f193'
git-subtree-dir: xo-indentlog git-subtree-mainline:624178f193git-subtree-split:624178f193
This commit is contained in:
parent
624178f193
commit
0df3e730a0
23 changed files with 6231 additions and 0 deletions
37
xo-indentlog/.github/workflows/main-stock-ubuntu.yml
vendored
Normal file
37
xo-indentlog/.github/workflows/main-stock-ubuntu.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
name: build on ubuntu base platform
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize CMake build type here
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# This build won't be entirely reproducible, given ubuntu changes on github runners may
|
||||
# introduce regressions.
|
||||
#
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: bootstrap xo-cmake
|
||||
run: |
|
||||
PREFIX=${{github.workspace}}/local
|
||||
mkdir -p ${PREFIX}
|
||||
echo "::group::xo-cmake configure"
|
||||
cmake -B .build0 -S xo-cmake -DCMAKE_INSTALL_PREFIX=${PREFIX}
|
||||
echo "::group::xo-cmake build"
|
||||
cmake --build .build0
|
||||
echo "::group::xo-cmake install"
|
||||
cmake --install .build0
|
||||
|
||||
- name: print install path
|
||||
run: |
|
||||
tree ${{github.workspace}}/local
|
||||
2
xo-indentlog/.gitignore
vendored
Normal file
2
xo-indentlog/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
result
|
||||
.build*
|
||||
104
xo-indentlog/CMakeLists.txt
Normal file
104
xo-indentlog/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# xo/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(xo-submodule3 VERSION 1.0)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# global build settings
|
||||
|
||||
# Adopting submodule builds directly into this cmake.
|
||||
# Submodule builds will pickup dependent xo artifacts directly
|
||||
# from sibling build dirs.
|
||||
# (Contrast with a build that relies on install step).
|
||||
# In particular, configure step in satellite projects
|
||||
# needs to avoid using cmake find_package() on sibling xo projects:
|
||||
# 1. .cmake support files
|
||||
# fooConfig.cmake
|
||||
# fooConfigVersion.cmake
|
||||
# fooTargets.cmake
|
||||
# won't have been installed
|
||||
# 2. In any case, they point to final install location;
|
||||
# we need build location
|
||||
#
|
||||
set(XO_SUBMODULE_BUILD True)
|
||||
|
||||
# toplevel source directory; used only with XO_SUBMODULE_BUILD
|
||||
set(XO_UMBRELLA_SOURCE_DIR ${CMAKE_SOURCE_DIR})
|
||||
set(XO_UMBRELLA_REPO_SUBDIR .)
|
||||
# toplevel binary directory; used only with XO_SUBMODULE_BUILD
|
||||
set(XO_UMBRELLA_BINARY_DIR ${CMAKE_BINARY_DIR})
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/xo-cmake/cmake)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# unit test setup
|
||||
|
||||
enable_testing()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# global c++ settings.
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
# temporary compiler flags here
|
||||
set(PROJECT_CXX_FLAGS "")
|
||||
add_definitions(${PROJECT_CXX_FLAGS})
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||
|
||||
if(NOT CMAKE_INSTALL_RPATH)
|
||||
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib CACHE STRING "runpath for installed libraries/executables")
|
||||
endif()
|
||||
|
||||
message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}")
|
||||
message("-- CMAKE_INSTALL_RPATH=${CMAKE_INSTALL_RPATH}")
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# xo satellite projects
|
||||
|
||||
add_subdirectory(xo-cmake)
|
||||
add_subdirectory(xo-indentlog)
|
||||
#add_subdirectory(xo-refcnt)
|
||||
#add_subdirectory(xo-subsys)
|
||||
#add_subdirectory(xo-randomgen)
|
||||
#add_subdirectory(xo-ordinaltree)
|
||||
#add_subdirectory(xo-pyutil)
|
||||
#add_subdirectory(xo-flatstring)
|
||||
#add_subdirectory(xo-reflectutil)
|
||||
#add_subdirectory(xo-reflect)
|
||||
#add_subdirectory(xo-pyreflect)
|
||||
#add_subdirectory(xo-ratio)
|
||||
#add_subdirectory(xo-unit)
|
||||
#add_subdirectory(xo-pyunit)
|
||||
#add_subdirectory(xo-expression)
|
||||
#add_subdirectory(xo-pyexpression)
|
||||
#add_subdirectory(xo-tokenizer)
|
||||
#add_subdirectory(xo-reader)
|
||||
#add_subdirectory(xo-jit)
|
||||
#add_subdirectory(xo-pyjit)
|
||||
#add_subdirectory(xo-callback)
|
||||
#add_subdirectory(xo-webutil)
|
||||
#add_subdirectory(xo-pywebutil)
|
||||
#add_subdirectory(xo-printjson)
|
||||
#add_subdirectory(xo-pyprintjson)
|
||||
#add_subdirectory(xo-reactor)
|
||||
#add_subdirectory(xo-pyreactor)
|
||||
#add_subdirectory(xo-websock)
|
||||
#add_subdirectory(xo-pywebsock)
|
||||
#add_subdirectory(xo-statistics)
|
||||
#add_subdirectory(xo-distribution)
|
||||
#add_subdirectory(xo-pydistribution)
|
||||
#add_subdirectory(xo-simulator)
|
||||
#add_subdirectory(xo-pysimulator)
|
||||
#add_subdirectory(xo-process)
|
||||
#add_subdirectory(xo-pyprocess)
|
||||
#add_subdirectory(xo-kalmanfilter)
|
||||
#add_subdirectory(xo-pykalmanfilter)
|
||||
85
xo-indentlog/README.md
Normal file
85
xo-indentlog/README.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Introduction
|
||||
|
||||
Local nix build for xo libraries.
|
||||
Intended for local development work, with source in immediate subdirectories.
|
||||
|
||||
## Features
|
||||
|
||||
- native c++
|
||||
- deterministic simulation
|
||||
- reflection
|
||||
- python bindings
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Cmake build
|
||||
|
||||
If `nix` is available, you probably prefer the nix build.
|
||||
Otherwise continue reading..
|
||||
|
||||
The cmake build has two phases, because it needs to bootstrap
|
||||
generated `xo-cmake-config`, `xo-build` helpers.
|
||||
|
||||
```
|
||||
$ cd xo
|
||||
$ PREFIX=/path/to/say/usr/local
|
||||
# phase 1
|
||||
$ cmake -B .build0 -S xo-cmake -DCMAKE_INSTALL_PREFIX=${PREFIX}
|
||||
$ cmake --build .build0
|
||||
$ cmake --install .build0
|
||||
# phase 2
|
||||
$ cmake -B .build -S . -DCMAKE_INSTALL_PREFIX=${PREFIX}
|
||||
$ cmake --build .build
|
||||
$ cmake --install .build
|
||||
```
|
||||
|
||||
### Nix Build
|
||||
|
||||
Nix build uses toplevel `default.nix`,
|
||||
along with top-level `pkgs/xo-foo.nix` for each subproject `foo`.
|
||||
It doesn't interact with toplevel `CMakeLists.txt`.
|
||||
|
||||
```
|
||||
$ nix-build -A xo-userenv
|
||||
```
|
||||
|
||||
This builds all xo subprojects, assembles sandbox under `./result`.
|
||||
|
||||
```
|
||||
$ tree -L 1 ./result
|
||||
./result
|
||||
├── bin
|
||||
│ ├── xo-build
|
||||
│ ├── xo-cmake-config
|
||||
│ └── xo-cmake-lcov-harness
|
||||
└── share
|
||||
├── cmake
|
||||
│ └── xo_macros
|
||||
│ ├── code-coverage.cmake
|
||||
│ ├── xo-project-macros.cmake
|
||||
│ └── xo_cxx.cmake
|
||||
├── etc
|
||||
│ └── xo
|
||||
│ └── subsystem-list
|
||||
└── xo-macros
|
||||
├── Doxyfile.in
|
||||
├── gen-ccov.in
|
||||
└── xo-bootstrap-macros.cmake
|
||||
```
|
||||
|
||||
## To add a new satellite repo
|
||||
|
||||
1. check clone in clean state (all local changes committed or unwound)
|
||||
|
||||
2. add satellite as remote
|
||||
|
||||
```
|
||||
$ git remote add xo-foo git@github.com:Rconybea/xo-foo.git
|
||||
$ git fetch xo-foo
|
||||
```
|
||||
|
||||
3. checkout satellite repo
|
||||
|
||||
```
|
||||
$ git subtree add --prefix=xo-foo main
|
||||
```
|
||||
94
xo-indentlog/default.nix
Normal file
94
xo-indentlog/default.nix
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#{ pkgs ? import <nixpkgs> { overlays = [ (final: prev: { llvmPackages = prev.llvmPackages_17; }) ]; } }:
|
||||
#pkgs.mkShell {
|
||||
# buildInputs = [ pkgs.coreutils ];
|
||||
#}
|
||||
|
||||
|
||||
{
|
||||
nixpkgs-path ? ../nixpkgs,
|
||||
|
||||
# pkgs ? import (fetchTarball {
|
||||
# # 24.05-darwin works on macos, clang17, llvm 18 (copying from xo-nix2)
|
||||
# url = "https://github.com/NixOS/nixpkgs/archive/dd868b7bd4d1407d607da0d1d9c5eca89132e2f7.tar.gz";
|
||||
# }),
|
||||
} :
|
||||
|
||||
let
|
||||
# this approach (overlays) is effective, but has super wide cross-section,
|
||||
# since absolutely everything has to be rebuilt from source
|
||||
#
|
||||
|
||||
llvm-overlay = self: super: {
|
||||
# use 'super' when you want to override the terms of a package.
|
||||
# use 'self' when pointing to an existing package
|
||||
|
||||
llvmPackages = super.llvmPackages_18;
|
||||
};
|
||||
|
||||
xo-overlay = self: super:
|
||||
let
|
||||
# Choose the LLVM version you want
|
||||
llvmPackages = self.llvmPackages_18;
|
||||
in
|
||||
|
||||
let
|
||||
# Rebuild stdenv to use that LLVM version
|
||||
customStdenv = super.overrideCC super.stdenv llvmPackages.clang;
|
||||
in
|
||||
|
||||
{
|
||||
xo-cmake = self.callPackage pkgs/xo-cmake.nix {};
|
||||
xo-indentlog = self.callPackage pkgs/xo-indentlog.nix {};
|
||||
# xo-refcnt = self.callPackage pkgs/xo-refcnt.nix {};
|
||||
# xo-subsys = self.callPackage pkgs/xo-subsys.nix {};
|
||||
# xo-randomgen = self.callPackage pkgs/xo-randomgen.nix {};
|
||||
# xo-ordinaltree = self.callPackage pkgs/xo-ordinaltree.nix {};
|
||||
# xo-pyutil = self.callPackage pkgs/xo-pyutil.nix {};
|
||||
# xo-flatstring = self.callPackage pkgs/xo-flatstring.nix {};
|
||||
# xo-reflectutil = self.callPackage pkgs/xo-reflectutil.nix {};
|
||||
# xo-reflect = self.callPackage pkgs/xo-reflect.nix {};
|
||||
# xo-pyreflect = self.callPackage pkgs/xo-pyreflect.nix {};
|
||||
# xo-ratio = self.callPackage pkgs/xo-ratio.nix {};
|
||||
# xo-unit = self.callPackage pkgs/xo-unit.nix {};
|
||||
# xo-pyunit = self.callPackage pkgs/xo-pyunit.nix {};
|
||||
# xo-expression = self.callPackage pkgs/xo-expression.nix {};
|
||||
# xo-pyexpression = self.callPackage pkgs/xo-pyexpression.nix {};
|
||||
# xo-tokenizer = self.callPackage pkgs/xo-tokenizer.nix {};
|
||||
# xo-reader = self.callPackage pkgs/xo-reader.nix {};
|
||||
# xo-jit = self.callPackage pkgs/xo-jit.nix { stdenv = customStdenv;
|
||||
# clang = llvmPackages.clang;
|
||||
# llvm = llvmPackages.llvm; };
|
||||
# xo-pyjit = self.callPackage pkgs/xo-pyjit.nix {};
|
||||
# xo-callback = self.callPackage pkgs/xo-callback.nix {};
|
||||
# xo-webutil = self.callPackage pkgs/xo-webutil.nix {};
|
||||
# xo-pywebutil = self.callPackage pkgs/xo-pywebutil.nix {};
|
||||
# xo-printjson = self.callPackage pkgs/xo-printjson.nix {};
|
||||
# xo-pyprintjson = self.callPackage pkgs/xo-pyprintjson.nix {};
|
||||
# xo-reactor = self.callPackage pkgs/xo-reactor.nix {};
|
||||
# xo-pyreactor = self.callPackage pkgs/xo-pyreactor.nix {};
|
||||
# xo-websock = self.callPackage pkgs/xo-websock.nix {};
|
||||
# xo-pywebsock = self.callPackage pkgs/xo-pywebsock.nix {};
|
||||
# xo-statistics = self.callPackage pkgs/xo-statistics.nix {};
|
||||
# xo-distribution = self.callPackage pkgs/xo-distribution.nix {};
|
||||
# xo-pydistribution = self.callPackage pkgs/xo-pydistribution.nix {};
|
||||
# xo-simulator = self.callPackage pkgs/xo-simulator.nix {};
|
||||
# xo-pysimulator = self.callPackage pkgs/xo-pysimulator.nix {};
|
||||
# xo-process = self.callPackage pkgs/xo-process.nix {};
|
||||
# xo-pyprocess = self.callPackage pkgs/xo-pyprocess.nix {};
|
||||
# xo-kalmanfilter = self.callPackage pkgs/xo-kalmanfilter.nix {};
|
||||
# xo-pykalmanfilter = self.callPackage pkgs/xo-pykalmanfilter.nix {};
|
||||
#
|
||||
xo-userenv = self.callPackage pkgs/xo-userenv.nix {};
|
||||
# xo-userenv-slow = self.callPackage pkgs/xo-userenv-slow.nix {};
|
||||
};
|
||||
|
||||
in
|
||||
let
|
||||
pkgs = import nixpkgs-path {
|
||||
overlays = [
|
||||
# llvm-overlay
|
||||
xo-overlay
|
||||
];
|
||||
};
|
||||
in
|
||||
pkgs
|
||||
15
xo-indentlog/pkgs/xo-cmake.nix
Normal file
15
xo-indentlog/pkgs/xo-cmake.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
# dependencies
|
||||
|
||||
stdenv,
|
||||
cmake
|
||||
} :
|
||||
|
||||
stdenv.mkDerivation (finalattrs:
|
||||
{
|
||||
name = "xo-cmake";
|
||||
|
||||
src = ../xo-cmake;
|
||||
|
||||
nativeBuildInputs = [ cmake ];
|
||||
})
|
||||
18
xo-indentlog/pkgs/xo-indentlog.nix
Normal file
18
xo-indentlog/pkgs/xo-indentlog.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
# dependencies
|
||||
stdenv, cmake, catch2,
|
||||
|
||||
xo-cmake,
|
||||
} :
|
||||
|
||||
stdenv.mkDerivation (finalattrs:
|
||||
{
|
||||
name = "xo-indentlog";
|
||||
version = "1.0";
|
||||
|
||||
src = ../xo-indentlog;
|
||||
|
||||
cmakeFlags = ["-DCMAKE_MODULE_PATH=${xo-cmake}/share/cmake"];
|
||||
doCheck = true;
|
||||
nativeBuildInputs = [ cmake catch2 xo-cmake ];
|
||||
})
|
||||
99
xo-indentlog/pkgs/xo-userenv.nix
Normal file
99
xo-indentlog/pkgs/xo-userenv.nix
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# builds environment with all xo packages,
|
||||
# using combined output directory for each.
|
||||
#
|
||||
# parallels github actions build on stock ubuntu,
|
||||
# except that we use nixpkgs for toolchain
|
||||
#
|
||||
# For xo development, probably prefer xo-userenv.nix instead of this xo-userenv-slow.nix:
|
||||
# 1. xo-userenv.nix allows parallel build
|
||||
# 2. xo-userenv.nix only rebuilds xo packages that have changed
|
||||
|
||||
{
|
||||
# nixpkgs dependencies
|
||||
buildEnv,
|
||||
stdenv,
|
||||
cmake,
|
||||
catch2,
|
||||
eigen,
|
||||
libwebsockets,
|
||||
jsoncpp,
|
||||
doxygen,
|
||||
sphinx,
|
||||
python3Packages,
|
||||
|
||||
# xo dependencies
|
||||
xo-cmake,
|
||||
xo-indentlog,
|
||||
# xo-subsys,
|
||||
# xo-refcnt,
|
||||
# xo-randomgen,
|
||||
# xo-ordinaltree,
|
||||
# xo-flatstring,
|
||||
# xo-reflectutil,
|
||||
# xo-ratio,
|
||||
# xo-unit,
|
||||
# xo-pyunit,
|
||||
# xo-pyutil,
|
||||
# xo-reflect,
|
||||
# xo-pyreflect,
|
||||
# xo-printjson,
|
||||
# xo-pyprintjson,
|
||||
# xo-callback,
|
||||
# xo-webutil,
|
||||
# xo-pywebutil,
|
||||
# xo-reactor,
|
||||
# xo-pyreactor,
|
||||
# xo-simulator,
|
||||
# xo-pysimulator, xo-distribution, xo-pydistribution, xo-process, xo-pyprocess, xo-statistics, xo-kalmanfilter,
|
||||
# xo-pykalmanfilter, xo-websock, xo-pywebsock, xo-tokenizer,
|
||||
# xo-expression, xo-pyexpression, xo-reader,
|
||||
# xo-jit,
|
||||
# xo-pyjit
|
||||
|
||||
# other args
|
||||
|
||||
# someconfigurationoption ? false
|
||||
} :
|
||||
|
||||
buildEnv {
|
||||
name = "xo-userenv";
|
||||
paths = [ xo-cmake
|
||||
xo-indentlog
|
||||
# xo-refcnt
|
||||
# xo-subsys
|
||||
# xo-randomgen
|
||||
# xo-ordinaltree
|
||||
# xo-pyutil
|
||||
# xo-flatstring
|
||||
# xo-reflectutil
|
||||
# xo-reflect
|
||||
# xo-pyreflect
|
||||
# xo-ratio
|
||||
# xo-unit
|
||||
# xo-pyunit
|
||||
# xo-expression
|
||||
# xo-pyexpression
|
||||
# xo-tokenizer
|
||||
# xo-reader
|
||||
# xo-jit
|
||||
# xo-pyjit
|
||||
# xo-callback
|
||||
# xo-webutil
|
||||
# xo-pywebutil
|
||||
# xo-printjson
|
||||
# xo-pyprintjson
|
||||
# xo-reactor
|
||||
# xo-pyreactor
|
||||
# xo-websock
|
||||
# xo-pywebsock
|
||||
# xo-statistics
|
||||
# xo-distribution
|
||||
# xo-pydistribution
|
||||
# xo-simulator
|
||||
# xo-pysimulator
|
||||
# xo-process
|
||||
# xo-pyprocess
|
||||
# xo-kalmanfilter
|
||||
# xo-pykalmanfilter
|
||||
];
|
||||
}
|
||||
74
xo-indentlog/xo-cmake/.github/workflows/xo-cpp-main.yml
vendored
Normal file
74
xo-indentlog/xo-cmake/.github/workflows/xo-cpp-main.yml
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
name: XO cmake xo-cpp-builder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
# custom docker image. see github.com:rconybea/docker-xo-builder for definition
|
||||
image: ghcr.io/rconybea/docker-xo-builder:v1
|
||||
|
||||
steps:
|
||||
- name: xo-cmake
|
||||
run: |
|
||||
# treat github.com as known host to prevent shtoopid SSL errors
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
|
||||
cat ~/.ssh/known_hosts
|
||||
|
||||
git config --global http.sslVerify false
|
||||
|
||||
XO_NAME=xo-cmake
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/xo-cmake.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::verify"
|
||||
ls -l ${PREFIX}/bin/xo-cmake-config
|
||||
${PREFIX}/bin/xo-cmake-config --help
|
||||
echo "$(${PREFIX}/bin/xo-cmake-config --lcov-exe)"
|
||||
echo "$(${PREFIX}/bin/xo-cmake-config --genhtml-exe)"
|
||||
echo "$(${PREFIX}/bin/xo-cmake-config --lcov-harness-exe)"
|
||||
echo "$(${PREFIX}/bin/xo-cmake-config --gen-ccov-template)"
|
||||
echo "$(${PREFIX}/bin/xo-cmake-config --cmake-module-path)"
|
||||
echo "::endgroup"
|
||||
3
xo-indentlog/xo-cmake/.gitignore
vendored
Normal file
3
xo-indentlog/xo-cmake/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.projectile
|
||||
.build
|
||||
compile_commands.json
|
||||
67
xo-indentlog/xo-cmake/CMakeLists.txt
Normal file
67
xo-indentlog/xo-cmake/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(xo_macros VERSION 1.0)
|
||||
|
||||
# if any are useful for this project..
|
||||
include (GNUInstallDirs)
|
||||
|
||||
set(XO_PROJECT_NAME xo_macros)
|
||||
|
||||
# LCOV_EXECUTABLE,GENHTML_EXECUTABLE: needed by xo-cmake-lcov-harness.in
|
||||
find_program(LCOV_EXECUTABLE NAMES lcov)
|
||||
find_program(GENHTML_EXECUTABLE NAMES genhtml)
|
||||
|
||||
configure_file(
|
||||
${PROJECT_SOURCE_DIR}/bin/xo-cmake-lcov-harness.in
|
||||
${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness
|
||||
@ONLY
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${PROJECT_SOURCE_DIR}/bin/xo-cmake-config.in
|
||||
${PROJECT_BINARY_DIR}/xo-cmake-config
|
||||
@ONLY
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${PROJECT_SOURCE_DIR}/bin/xo-build.in
|
||||
${PROJECT_BINARY_DIR}/xo-build
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
"cmake/xo_macros/xo-project-macros.cmake"
|
||||
"cmake/xo_macros/xo_cxx.cmake"
|
||||
"cmake/xo_macros/code-coverage.cmake"
|
||||
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/xo_macros
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
"${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness"
|
||||
"${PROJECT_BINARY_DIR}/xo-cmake-config"
|
||||
"${PROJECT_BINARY_DIR}/xo-build"
|
||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
"etc/xo/subsystem-list"
|
||||
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/etc/xo
|
||||
)
|
||||
|
||||
# The cmake template gen-ccov.in should be expanded in downstream project;
|
||||
# to pickup downstream project's PROJECT_SOURCE_DIR / PROJECT_BINARY_DIR
|
||||
#
|
||||
install(
|
||||
FILES
|
||||
"share/xo-macros/gen-ccov.in"
|
||||
"share/xo-macros/Doxyfile.in"
|
||||
"share/xo-macros/xo-bootstrap-macros.cmake"
|
||||
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/xo-macros
|
||||
)
|
||||
120
xo-indentlog/xo-cmake/FAQ
Normal file
120
xo-indentlog/xo-cmake/FAQ
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
# Unit test build can't find library:
|
||||
|
||||
Missing dependency at link time
|
||||
|
||||
```
|
||||
set(SELF_EXE mumble)
|
||||
set(SELF_SRCS mumble.cpp)
|
||||
|
||||
xo_add_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
xo_self_dependency(${SELF_EXE} xo_foo)
|
||||
```
|
||||
|
||||
and build fails with `cannot find -lxo_bar`.
|
||||
|
||||
Possible causes:
|
||||
|
||||
1. missing cmake export for a dependency of `xo_foo`.
|
||||
Check `xo_foo/cmake/xo_fooConfig.cmake.in`:
|
||||
|
||||
If `xo_foo` depends on `xo_bar`, then cmake export
|
||||
needs to have `find_dependency(xo_bar)`
|
||||
|
||||
```
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(xo_bar)
|
||||
find_dependency(xo_maybemoar)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
```
|
||||
|
||||
# Howto introduce sphinx documentation to an XO project
|
||||
|
||||
REMINDER: must re-run `cmake` after introducing skeleton, since build copies files from docs/ to .build directory
|
||||
|
||||
Minimal skeleton for docs/ directory:
|
||||
|
||||
```
|
||||
xo-foo
|
||||
+- docs
|
||||
+- CMakeLists.txt
|
||||
+- conf.py
|
||||
+- _static
|
||||
+- index.rst
|
||||
```
|
||||
|
||||
CMakeLists.txt:
|
||||
```
|
||||
# xo-foo/docs/CMakeLists.txt
|
||||
|
||||
xo_doxygen_collect_deps()
|
||||
xo_docdir_doxygen_config()
|
||||
xo_docdir_sphinx_config(
|
||||
index.rst
|
||||
)
|
||||
```
|
||||
|
||||
conf.py:
|
||||
```
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'xo jit documentation'
|
||||
copyright = '2024, Roland Conybeare'
|
||||
author = 'Roland Conybeare'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
#extensions = []
|
||||
extensions = [ "breathe",
|
||||
"sphinx.ext.mathjax", # inline math
|
||||
"sphinx.ext.autodoc", # generate info from docstrings
|
||||
"sphinxcontrib.ditaa", # diagrams-through-ascii-art
|
||||
"sphinxcontrib.plantuml" # text -> uml diagrams
|
||||
]
|
||||
|
||||
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
|
||||
# match project name in Doxyfile.in
|
||||
breathe_default_project = "xodoxxml"
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
#html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_static_path = ['_static']
|
||||
html_favicon = '_static/img/favicon.ico'
|
||||
```
|
||||
|
||||
index.rst:
|
||||
```
|
||||
xo-foo documentation
|
||||
====================
|
||||
|
||||
text here
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: xo-foo contents
|
||||
|
||||
genindex
|
||||
search
|
||||
```
|
||||
|
||||
_static:
|
||||
|
||||
copy from xo-unit/docs/_static. Just need .ico
|
||||
61
xo-indentlog/xo-cmake/README.md
Normal file
61
xo-indentlog/xo-cmake/README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# XO cmake modules
|
||||
|
||||
Collects cmake macros to be shared across XO projects (e.g. indentlog, reflect, kalman, ..)
|
||||
|
||||
## Features
|
||||
|
||||
- support for both manyrepo and monorepo projects
|
||||
- support for generating cmake `xxxConfig.cmake` files, so cmake `find_package()` works reliably
|
||||
- support for header-only libraries
|
||||
- support for pybind11 libraries
|
||||
- documentation generation using doxygen + breathe + sphinx
|
||||
- code coverage using ccov + lcov
|
||||
|
||||
## Getting Started
|
||||
|
||||
### copy repo
|
||||
|
||||
```
|
||||
$ git clone https://github.com:rconybea/xo-cmake.git
|
||||
```
|
||||
|
||||
### configure + install
|
||||
```
|
||||
$ cd xo-cmake
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local -B .build -S . # ..or desired prefix
|
||||
$ cmake --install .build
|
||||
```
|
||||
|
||||
## use from a cmake project
|
||||
|
||||
In some project `foo`:
|
||||
```
|
||||
$ cd foo
|
||||
$ mkdir cmake
|
||||
$ cp $PREFIX/share/xo-macros/xo-bootstrap-macros.cmake cmake/
|
||||
```
|
||||
|
||||
`xo-bootstrap-macros-cmake` has two vital jobs:
|
||||
1. set `XO_CMAKE_CONFIG_EXECUTABLE` (locate `xo-cmake-config`)
|
||||
2. set `CMAKE_MODULE_PATH` (obtained from `xo-cmake-config --cmake-module-path`)
|
||||
|
||||
then in `foo/CMakeLists.txt`:
|
||||
```
|
||||
include(cmake/xo-bootstrap-macros.cmake)
|
||||
|
||||
xo_cxx_toplevel_options3()
|
||||
```
|
||||
|
||||
Now as long as `$PREFIX/bin` is in `PATH`:
|
||||
```
|
||||
$ cd mybuild
|
||||
$ cmake path/to/foo/source
|
||||
```
|
||||
|
||||
### or set `XO_CMAKE_CONFIG_EXECUTABLE` and `CMAKE_MODULE_PATH`
|
||||
|
||||
In some project `foo`:
|
||||
```
|
||||
$ cd mybuild
|
||||
$ cmake -DXO_CMAKE_CONFIG_EXECUTABLE=xo-cmake-config -DCMAKE_MODULE_PATH=$(xo-cmake-config --cmake-module-path) path/to/foo/source
|
||||
```
|
||||
220
xo-indentlog/xo-cmake/bin/xo-build.in
Normal file
220
xo-indentlog/xo-cmake/bin/xo-build.in
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
$0 [-u|--usage|-h|--help]
|
||||
[--list] [-n] [--xoname=NAME]
|
||||
[--repo|--clone|--configure|--build|--install]
|
||||
[-S=SOURCEDIR] [-B=BUILDDIR]
|
||||
EOF
|
||||
}
|
||||
|
||||
help() {
|
||||
usage
|
||||
|
||||
cat <<EOF
|
||||
|
||||
fetch and/or build xo component libraries
|
||||
|
||||
Options:
|
||||
-u | --usage brief help message
|
||||
-h | --help this help message
|
||||
--list list known xo libraries
|
||||
-n dry-run: don't actually invoke state-changing commands
|
||||
--xoname=NAME operate on xo subsystem NAME
|
||||
--repo report github repo for NAME
|
||||
--clone git clone xo library NAME in current directory
|
||||
--configure run cmake for xo library NAME in immediate subdir
|
||||
Will use NAME/.build as build directory
|
||||
--build run cmake build for xo library NAME in immediate subdir
|
||||
--install run cmake install for xo library NAME in immediate subdir
|
||||
-S=SOURCEDIR override path/to/source
|
||||
-B=BUILDDIR override path/to/build
|
||||
EOF
|
||||
}
|
||||
|
||||
noop_flag=0
|
||||
xoname=
|
||||
repo_flag=0
|
||||
clone_flag=0
|
||||
configure_flag=0
|
||||
build_flag=0
|
||||
install_flag=0
|
||||
pathtosource=
|
||||
pathtobuild=
|
||||
|
||||
while [[ $# > 0 ]]; do
|
||||
case "$1" in
|
||||
-u | --usage)
|
||||
cmd='usage'
|
||||
;;
|
||||
-h | --help)
|
||||
cmd='help'
|
||||
;;
|
||||
-n)
|
||||
noop_flag=1
|
||||
;;
|
||||
-S)
|
||||
shift
|
||||
pathtosource=$1
|
||||
;;
|
||||
-S=*)
|
||||
pathtosource="${1#*=}"
|
||||
;;
|
||||
-B)
|
||||
shift
|
||||
pathtobuild=$1
|
||||
;;
|
||||
-B=*)
|
||||
pathtobuild="${1#*=}"
|
||||
;;
|
||||
--list)
|
||||
cmd='list'
|
||||
;;
|
||||
--xoname=*)
|
||||
xoname="${1#*=}"
|
||||
;;
|
||||
--repo)
|
||||
repo_flag=1
|
||||
;;
|
||||
--clone)
|
||||
clone_flag=1
|
||||
;;
|
||||
--configure|--config)
|
||||
configure_flag=1
|
||||
;;
|
||||
--build)
|
||||
build_flag=1
|
||||
;;
|
||||
--install)
|
||||
install_flag=1
|
||||
;;
|
||||
xo-*)
|
||||
xoname="$1"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
echo xoname=$xoname pathtosource=$pathtosource pathtobuild=$pathtobuild
|
||||
|
||||
if [[ -z "$pathtosource" ]]; then
|
||||
pathtosource=$xoname
|
||||
fi
|
||||
|
||||
if [[ -z "$pathtobuild" ]]; then
|
||||
pathtobuild=$xoname/.build
|
||||
fi
|
||||
|
||||
SUBSYSTEMLIST_FILE=@CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-list
|
||||
|
||||
subsystem_list() {
|
||||
cat ${SUBSYSTEMLIST_FILE}
|
||||
}
|
||||
|
||||
XO_REPO_STEM="https://github.com/Rconybea"
|
||||
|
||||
repo() {
|
||||
xoname=$1
|
||||
|
||||
case "$xoname" in
|
||||
xo-indentlog)
|
||||
echo "${XO_REPO_STEM}/indentlog.git"
|
||||
;;
|
||||
xo-refcnt)
|
||||
echo "${XO_REPO_STEM}/refcnt.git"
|
||||
;;
|
||||
xo-subsys)
|
||||
echo "${XO_REPO_STEM}/subsys.git"
|
||||
;;
|
||||
xo-reflect)
|
||||
echo "${XO_REPO_STEM}/reflect.git"
|
||||
;;
|
||||
*)
|
||||
if grep -q $1 ${SUBSYSTEMLIST_FILE}; then
|
||||
echo "${XO_REPO_STEM}/${1}.git"
|
||||
else
|
||||
>&2 echo "$0: unknown xo component [${xoname}]"
|
||||
return 1
|
||||
fi
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ $cmd == 'usage' ]]; then
|
||||
echo -n "usage: "
|
||||
usage
|
||||
exit 0
|
||||
elif [[ $cmd == 'help' ]]; then
|
||||
echo -n "help; "
|
||||
help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
if [[ $cmd == 'list' ]]; then
|
||||
subsystem_list
|
||||
fi
|
||||
|
||||
if [[ $repo_flag -eq 1 ]]; then
|
||||
if [[ -n "$xoname" ]]; then
|
||||
repo $xoname
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $clone_flag -eq 1 ]]; then
|
||||
if [[ -n "$xoname" ]]; then
|
||||
url=$(repo $xoname)
|
||||
|
||||
cmd="git clone $url"
|
||||
|
||||
if [[ $noop_flag -eq 1 ]]; then
|
||||
echo $cmd
|
||||
else
|
||||
$cmd
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $configure_flag -eq 1 ]]; then
|
||||
if [[ -n "$xoname" ]]; then
|
||||
cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -S $pathtosource -B $pathtobuild"
|
||||
|
||||
if [[ $noop_flag -eq 1 ]]; then
|
||||
echo $cmd
|
||||
else
|
||||
$cmd
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $build_flag -eq 1 ]]; then
|
||||
if [[ -n "$xoname" ]]; then
|
||||
cmd="cmake --build $pathtobuild -j"
|
||||
|
||||
if [[ $noop_flag -eq 1 ]]; then
|
||||
echo $cmd
|
||||
else
|
||||
$cmd
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $install_flag -eq 1 ]]; then
|
||||
if [[ -n "$xoname" ]]; then
|
||||
cmd="cmake --install $pathtobuild"
|
||||
|
||||
if [[ $noop_flag -eq 1 ]]; then
|
||||
echo $cmd
|
||||
else
|
||||
$cmd
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
86
xo-indentlog/xo-cmake/bin/xo-cmake-config.in
Executable file
86
xo-indentlog/xo-cmake/bin/xo-cmake-config.in
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
usage() {
|
||||
echo "$0 [-u|--usage|-h|--help|--lcov-exe|--genhtml-exe|--lcov-harness-exe|--gen-ccov-template|--cmake-module-path|--subsystem-list]" 1>&2
|
||||
}
|
||||
|
||||
help() {
|
||||
usage
|
||||
|
||||
cat <<EOF
|
||||
|
||||
display xo-cmake configuration variables
|
||||
|
||||
Options:
|
||||
-u | --usage brief help message
|
||||
-h | --help this help message
|
||||
--cmake-module-path report directory providing xo-cmake macros (will use/appear-in CMAKE_MODULE_PATH)
|
||||
--lcov-exe report path to 'lcov' executable
|
||||
--genhtml-exe report path to 'genhtml' executable
|
||||
--lcov-harness-exe report path to 'xo-cmake-lcov-harness' executable
|
||||
--gen-ccov-template report path to 'gen-ccov.in' template
|
||||
--doxygen-template report path to 'Doxyfile.in' template
|
||||
--subsystem-list report path to 'subsystem-ilst'
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# > 0 ]]; do
|
||||
case "$1" in
|
||||
-u | --usage)
|
||||
cmd='usage'
|
||||
;;
|
||||
-h | --help)
|
||||
cmd='help'
|
||||
;;
|
||||
--cmake-module-path)
|
||||
cmd='cmake_module_path'
|
||||
;;
|
||||
--lcov-exe)
|
||||
cmd='lcov_exe'
|
||||
;;
|
||||
--genhtml-exe)
|
||||
cmd='genhtml_exe'
|
||||
;;
|
||||
--lcov-harness-exe)
|
||||
cmd='lcov_harness_exe'
|
||||
;;
|
||||
--gen-ccov-template)
|
||||
cmd='gen_ccov_template'
|
||||
;;
|
||||
--doxygen-template)
|
||||
cmd='doxygen_template'
|
||||
;;
|
||||
--subsystem-list)
|
||||
cmd='subsystem_list'
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ $cmd == 'usage' ]]; then
|
||||
echo -n "usage: "
|
||||
usage
|
||||
elif [[ $cmd == 'help' ]]; then
|
||||
echo -n "help: "
|
||||
help
|
||||
elif [[ $cmd == 'cmake_module_path' ]]; then
|
||||
echo -n @CMAKE_INSTALL_FULL_DATADIR@/cmake
|
||||
elif [[ $cmd == 'lcov_exe' ]]; then
|
||||
echo -n @LCOV_EXECUTABLE@
|
||||
elif [[ $cmd == 'genhtml_exe' ]]; then
|
||||
echo -n @GENHTML_EXECUTABLE@
|
||||
elif [[ $cmd == 'lcov_harness_exe' ]]; then
|
||||
echo -n @CMAKE_INSTALL_FULL_BINDIR@/xo-cmake-lcov-harness
|
||||
elif [[ $cmd == 'gen_ccov_template' ]]; then
|
||||
echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/gen-ccov.in
|
||||
elif [[ $cmd == 'doxygen_template' ]]; then
|
||||
echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/Doxyfile.in
|
||||
elif [[ $cmd == 'subsystem_list' ]]; then
|
||||
echo -n @CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-list
|
||||
fi
|
||||
121
xo-indentlog/xo-cmake/bin/xo-cmake-lcov-harness.in
Executable file
121
xo-indentlog/xo-cmake/bin/xo-cmake-lcov-harness.in
Executable file
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
srcdir=$1
|
||||
builddir=$2
|
||||
outputstem=$3 # optional
|
||||
lcov=$4 # optional
|
||||
genhtml=$5 # optional
|
||||
|
||||
if [[ -z "${srcdir}" ]]; then
|
||||
echo "xo-cmake-lcov-harness: expected non-empty srcdir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${builddir}" ]]; then
|
||||
echo "xo-cmake-lcov-harness: expected non-empty builddir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z ${outputstem} ]]; then
|
||||
outputstem=$builddir/ccov/out
|
||||
fi
|
||||
|
||||
if [[ -z ${lcov} ]]; then
|
||||
lcov=@LCOV_EXECUTABLE@
|
||||
if [[ $lcov == "LCOV_EXECUTABLE-NOTFOUND" ]]; then
|
||||
echo "xo-cmake-lcov-harness: lcov executable not found during xo-cmake build/install"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z ${genhtml} ]]; then
|
||||
genhtml=@GENHTML_EXECUTABLE@
|
||||
if [[ $genhtml == "GENHTML_EXECUTABLE-NOTFOUND" ]]; then
|
||||
echo "xo-cmake-lcov-harness: genhtml executable not found during xo-cmake build/install"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p $builddir/ccov
|
||||
|
||||
# directory stems for location of {.gcda, gcno} coverage information,
|
||||
#
|
||||
# if we have source tree:
|
||||
#
|
||||
# ${srcdir}
|
||||
# +- foo
|
||||
# | \- foo.cpp
|
||||
# \- bar
|
||||
# \- quux
|
||||
# +- quux.cpp
|
||||
# \- quux_main.cpp
|
||||
#
|
||||
# then we expect build tree:
|
||||
#
|
||||
# ${builddir}
|
||||
# +- foo
|
||||
# | \- CMakeFiles
|
||||
# | \- foo_target.dir
|
||||
# | +- foo.cpp.gcda
|
||||
# | \- foo.cpp.gcno
|
||||
# +- bar
|
||||
# \- quux
|
||||
# \- CMakeFiles
|
||||
# \- target4quux.dir
|
||||
# +- quux.cpp.gcda
|
||||
# +- quux.cpp.gcno
|
||||
# +- quux_main.cpp.gcda
|
||||
# \- quux_main.cpp.gcno
|
||||
#
|
||||
# in which case will have cmd_body:
|
||||
#
|
||||
# ${primarydirs}
|
||||
# ./foo/CMakeFiles/foo_target.dir
|
||||
# ./bar/quux/CMakeFiles/target4quux.dir
|
||||
#
|
||||
# here foo_target, quux_target are whatever build is using for corresponding cmake target names.
|
||||
#
|
||||
# We want to invoke lcov like:
|
||||
#
|
||||
# lcov --capture \
|
||||
# --output ${builddir}/ccov \
|
||||
# --exclude /utest/ \
|
||||
# --base-directory ${srcdir}/foo --directory ${builddir}/foo/CMakeFiles/foo_target.dir \
|
||||
# --base-directory ${srcdir}/bar/quux --directory ${builddir}/bar/quux/CMakeFiles/target4quux.dir
|
||||
#
|
||||
primarydirs=$(cd ${builddir} && find -name '*.gcno' \
|
||||
| xargs --replace=xx dirname xx \
|
||||
| uniq \
|
||||
| sed -e 's:^\./::')
|
||||
|
||||
#echo "primarydirs=${primarydirs}"
|
||||
|
||||
cmd="${lcov} --output ${outputstem}.info --capture --ignore-errors source"
|
||||
|
||||
for bdir in ${primarydirs}; do
|
||||
sdir=$(dirname $(dirname ${bdir}))
|
||||
|
||||
cmd="${cmd} --base-directory ${srcdir}/${sdir} --directory ${builddir}/${bdir}"
|
||||
done
|
||||
|
||||
#echo cmd=${cmd}
|
||||
|
||||
set -x
|
||||
|
||||
# capture
|
||||
${cmd}
|
||||
|
||||
# keep only files with paths under source tree
|
||||
# (don't want coverage for external libraries such as libstdc++ etc)
|
||||
${lcov} --extract ${outputstem}.info "${srcdir}/*" --output ${outputstem}2.info
|
||||
|
||||
# remove unit test dirs
|
||||
# (we're interested in coverage of our installed code, not of the unit tests that exercise it)
|
||||
${lcov} --remove ${outputstem}2.info '*/utest/*' --output ${outputstem}3.info
|
||||
|
||||
# generate .html tree
|
||||
mkdir -p ${builddir}/ccov/html
|
||||
${genhtml} --ignore-errors source --show-details --prefix ${srcdir} --output-directory ${builddir}/ccov/html ${outputstem}3.info
|
||||
|
||||
# also send report to stdout
|
||||
${lcov} --list ${outputstem}3.info
|
||||
678
xo-indentlog/xo-cmake/cmake/xo_macros/code-coverage.cmake
Normal file
678
xo-indentlog/xo-cmake/cmake/xo_macros/code-coverage.cmake
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
#
|
||||
# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
|
||||
# USAGE: To enable any code coverage instrumentation/targets, the single CMake
|
||||
# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or
|
||||
# on the command line.
|
||||
#
|
||||
# From this point, there are two primary methods for adding instrumentation to
|
||||
# targets:
|
||||
#
|
||||
# 1 - A blanket instrumentation by calling `add_code_coverage()`, where
|
||||
# all targets in that directory and all subdirectories are automatically
|
||||
# instrumented.
|
||||
#
|
||||
# 2 - Per-target instrumentation by calling
|
||||
# `target_code_coverage(<TARGET_NAME>)`, where the target is given and thus only
|
||||
# that target is instrumented. This applies to both libraries and executables.
|
||||
#
|
||||
# To add coverage targets, such as calling `make ccov` to generate the actual
|
||||
# coverage information for perusal or consumption, call
|
||||
# `target_code_coverage(<TARGET_NAME>)` on an *executable* target.
|
||||
#
|
||||
# Example 1: All targets instrumented
|
||||
#
|
||||
# In this case, the coverage information reported will will be that of the
|
||||
# `theLib` library target and `theExe` executable.
|
||||
#
|
||||
# 1a: Via global command
|
||||
#
|
||||
# ~~~
|
||||
# add_code_coverage() # Adds instrumentation to all targets
|
||||
#
|
||||
# add_library(theLib lib.cpp)
|
||||
#
|
||||
# add_executable(theExe main.cpp)
|
||||
# target_link_libraries(theExe PRIVATE theLib)
|
||||
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target
|
||||
# # (instrumentation already added via global anyways)
|
||||
# # for generating code coverage reports.
|
||||
# ~~~
|
||||
#
|
||||
# 1b: Via target commands
|
||||
#
|
||||
# ~~~
|
||||
# add_library(theLib lib.cpp)
|
||||
# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets.
|
||||
#
|
||||
# add_executable(theExe main.cpp)
|
||||
# target_link_libraries(theExe PRIVATE theLib)
|
||||
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports.
|
||||
# ~~~
|
||||
#
|
||||
# Example 2: Target instrumented, but with regex pattern of files to be excluded
|
||||
# from report
|
||||
#
|
||||
# ~~~
|
||||
# add_executable(theExe main.cpp non_covered.cpp)
|
||||
# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
|
||||
# ~~~
|
||||
#
|
||||
# Example 3: Target added to the 'ccov' and 'ccov-all' targets
|
||||
#
|
||||
# ~~~
|
||||
# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders.
|
||||
#
|
||||
# add_executable(theExe main.cpp non_covered.cpp)
|
||||
# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
|
||||
# ~~~
|
||||
|
||||
# Options
|
||||
option(
|
||||
CODE_COVERAGE
|
||||
"Builds targets with code coverage instrumentation. (Requires GCC or Clang)"
|
||||
OFF)
|
||||
|
||||
# Programs
|
||||
find_program(LLVM_COV_PATH llvm-cov)
|
||||
find_program(LLVM_PROFDATA_PATH llvm-profdata)
|
||||
find_program(LCOV_PATH lcov)
|
||||
find_program(GENHTML_PATH genhtml)
|
||||
# Hide behind the 'advanced' mode flag for GUI/ccmake
|
||||
mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH)
|
||||
|
||||
# Variables
|
||||
set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov)
|
||||
set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1)
|
||||
|
||||
# Common initialization/checks
|
||||
if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
|
||||
set(CODE_COVERAGE_ADDED ON)
|
||||
|
||||
# Common Targets
|
||||
add_custom_target(
|
||||
ccov-preprocessing
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}
|
||||
DEPENDS ccov-clean)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
|
||||
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
|
||||
# Messages
|
||||
message(STATUS "Building with llvm Code Coverage Tools")
|
||||
|
||||
if(NOT LLVM_COV_PATH)
|
||||
message(FATAL_ERROR "llvm-cov not found! Aborting.")
|
||||
else()
|
||||
# Version number checking for 'EXCLUDE' compatibility
|
||||
execute_process(COMMAND ${LLVM_COV_PATH} --version
|
||||
OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT)
|
||||
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION
|
||||
${LLVM_COV_VERSION_CALL_OUTPUT})
|
||||
|
||||
if(LLVM_COV_VERSION VERSION_LESS "7.0.0")
|
||||
message(
|
||||
WARNING
|
||||
"target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Targets
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
|
||||
add_custom_target(
|
||||
ccov-clean
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
|
||||
else()
|
||||
add_custom_target(
|
||||
ccov-clean
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -f
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -f
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
|
||||
endif()
|
||||
|
||||
# Used to get the shared object file list before doing the main all-
|
||||
# processing
|
||||
add_custom_target(
|
||||
ccov-libs
|
||||
COMMAND ;
|
||||
COMMENT "libs ready for coverage report.")
|
||||
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
|
||||
"GNU")
|
||||
# Messages
|
||||
message(STATUS "Building with lcov Code Coverage Tools")
|
||||
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
|
||||
if(NOT ${upper_build_type} STREQUAL "DEBUG")
|
||||
message(
|
||||
WARNING
|
||||
"Code coverage results with an optimized (non-Debug) build may be misleading"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(
|
||||
WARNING
|
||||
"Code coverage results with an optimized (non-Debug) build may be misleading"
|
||||
)
|
||||
endif()
|
||||
if(NOT LCOV_PATH)
|
||||
message(FATAL_ERROR "lcov not found! Aborting...")
|
||||
endif()
|
||||
if(NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif()
|
||||
|
||||
# Targets
|
||||
add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory
|
||||
${CMAKE_BINARY_DIR} --zerocounters)
|
||||
|
||||
else()
|
||||
message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Adds code coverage instrumentation to a library, or instrumentation/targets
|
||||
# for an executable target.
|
||||
# ~~~
|
||||
# EXECUTABLE ADDED TARGETS:
|
||||
# GCOV/LCOV:
|
||||
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
|
||||
# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target.
|
||||
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
|
||||
#
|
||||
# LLVM-COV:
|
||||
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
|
||||
# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter.
|
||||
# ccov-${TARGET_NAME} : Generates HTML code coverage report.
|
||||
# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information.
|
||||
# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file.
|
||||
# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information.
|
||||
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
|
||||
# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line.
|
||||
# ccov-all-export : Exports the coverage report to a JSON file.
|
||||
#
|
||||
# Required:
|
||||
# TARGET_NAME - Name of the target to generate code coverage for.
|
||||
# Optional:
|
||||
# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE.
|
||||
# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE.
|
||||
# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects)
|
||||
# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets.
|
||||
# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets.
|
||||
# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory
|
||||
# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`.
|
||||
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.**
|
||||
# OBJECTS <TARGETS> - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output
|
||||
# ARGS <ARGUMENTS> - For executables ONLY, appends the given arguments to the associated ccov-* executable call
|
||||
# ~~~
|
||||
function(target_code_coverage TARGET_NAME)
|
||||
# Argument parsing
|
||||
set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN)
|
||||
set(single_value_keywords COVERAGE_TARGET_NAME)
|
||||
set(multi_value_keywords EXCLUDE OBJECTS ARGS)
|
||||
cmake_parse_arguments(
|
||||
target_code_coverage "${options}" "${single_value_keywords}"
|
||||
"${multi_value_keywords}" ${ARGN})
|
||||
|
||||
# Set the visibility of target functions to PUBLIC, INTERFACE or default to
|
||||
# PRIVATE.
|
||||
if(target_code_coverage_PUBLIC)
|
||||
set(TARGET_VISIBILITY PUBLIC)
|
||||
set(TARGET_LINK_VISIBILITY PUBLIC)
|
||||
elseif(target_code_coverage_INTERFACE)
|
||||
set(TARGET_VISIBILITY INTERFACE)
|
||||
set(TARGET_LINK_VISIBILITY INTERFACE)
|
||||
elseif(target_code_coverage_PLAIN)
|
||||
set(TARGET_VISIBILITY PUBLIC)
|
||||
set(TARGET_LINK_VISIBILITY)
|
||||
else()
|
||||
set(TARGET_VISIBILITY PRIVATE)
|
||||
set(TARGET_LINK_VISIBILITY PRIVATE)
|
||||
endif()
|
||||
|
||||
if(NOT target_code_coverage_COVERAGE_TARGET_NAME)
|
||||
# If a specific name was given, use that instead.
|
||||
set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME})
|
||||
endif()
|
||||
|
||||
if(CODE_COVERAGE)
|
||||
|
||||
# Add code coverage instrumentation to the target's linker command
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
|
||||
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
|
||||
target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY}
|
||||
-fprofile-instr-generate -fcoverage-mapping)
|
||||
target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY}
|
||||
-fprofile-instr-generate -fcoverage-mapping)
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
|
||||
"GNU")
|
||||
target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs
|
||||
-ftest-coverage)
|
||||
target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov)
|
||||
endif()
|
||||
|
||||
# Targets
|
||||
get_target_property(target_type ${TARGET_NAME} TYPE)
|
||||
|
||||
# Add shared library to processing for 'all' targets
|
||||
if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
|
||||
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
|
||||
add_custom_target(
|
||||
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>" >>
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
|
||||
DEPENDS ccov-preprocessing ${TARGET_NAME})
|
||||
|
||||
if(NOT TARGET ccov-libs)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
|
||||
)
|
||||
endif()
|
||||
|
||||
add_dependencies(ccov-libs
|
||||
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# For executables add targets to run and produce output
|
||||
if(target_type STREQUAL "EXECUTABLE")
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
|
||||
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
|
||||
|
||||
# If there are shared objects to also work with, generate the string to
|
||||
# add them here
|
||||
foreach(SO_TARGET ${target_code_coverage_OBJECTS})
|
||||
# Check to see if the target is a shared object
|
||||
if(TARGET ${SO_TARGET})
|
||||
get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE)
|
||||
if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY")
|
||||
set(SO_OBJECTS ${SO_OBJECTS} -object=$<TARGET_FILE:${SO_TARGET}>)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Run the executable, generating raw profile data Make the run data
|
||||
# available for further processing. Separated to allow Windows to run
|
||||
# this target serially.
|
||||
add_custom_target(
|
||||
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw
|
||||
$<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>"
|
||||
${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E echo
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw"
|
||||
>> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list
|
||||
JOB_POOL ccov_serial_pool
|
||||
DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME})
|
||||
|
||||
# Merge the generated profile data so llvm-cov can process it
|
||||
add_custom_target(
|
||||
ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${LLVM_PROFDATA_PATH} merge -sparse
|
||||
${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o
|
||||
${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
|
||||
DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
|
||||
# Ignore regex only works on LLVM >= 7
|
||||
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
|
||||
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
|
||||
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
|
||||
-ignore-filename-regex='${EXCLUDE_ITEM}')
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Print out details of the coverage information to the command line
|
||||
add_custom_target(
|
||||
ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
|
||||
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
|
||||
-show-line-counts-or-regions ${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
|
||||
# Print out a summary of the coverage information to the command line
|
||||
add_custom_target(
|
||||
ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} report $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
|
||||
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
|
||||
${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
|
||||
# Export coverage information so continuous integration tools (e.g.
|
||||
# Jenkins) can consume it
|
||||
add_custom_target(
|
||||
ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} export $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
|
||||
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
|
||||
-format="text" ${EXCLUDE_REGEX} >
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json
|
||||
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
|
||||
# Generates HTML output of the coverage information for perusal
|
||||
add_custom_target(
|
||||
ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
|
||||
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
|
||||
-show-line-counts-or-regions
|
||||
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
-format="html" ${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
|
||||
"GNU")
|
||||
set(COVERAGE_INFO
|
||||
"${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info"
|
||||
)
|
||||
|
||||
# Run the executable, generating coverage information
|
||||
add_custom_target(
|
||||
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
|
||||
DEPENDS ccov-preprocessing ${TARGET_NAME})
|
||||
|
||||
# Generate exclusion string for use
|
||||
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
|
||||
set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
|
||||
'${EXCLUDE_ITEM}')
|
||||
endforeach()
|
||||
|
||||
if(EXCLUDE_REGEX)
|
||||
set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
|
||||
${COVERAGE_INFO})
|
||||
else()
|
||||
set(EXCLUDE_COMMAND ;)
|
||||
endif()
|
||||
|
||||
if(NOT ${target_code_coverage_EXTERNAL})
|
||||
set(EXTERNAL_OPTION --no-external)
|
||||
endif()
|
||||
|
||||
# Capture coverage data
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
|
||||
add_custom_target(
|
||||
ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
|
||||
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
|
||||
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
|
||||
COMMAND
|
||||
${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
|
||||
${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
|
||||
${COVERAGE_INFO}
|
||||
COMMAND ${EXCLUDE_COMMAND}
|
||||
DEPENDS ccov-preprocessing ${TARGET_NAME})
|
||||
else()
|
||||
add_custom_target(
|
||||
ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
|
||||
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
|
||||
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
|
||||
COMMAND
|
||||
${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
|
||||
${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
|
||||
${COVERAGE_INFO}
|
||||
COMMAND ${EXCLUDE_COMMAND}
|
||||
DEPENDS ccov-preprocessing ${TARGET_NAME})
|
||||
endif()
|
||||
|
||||
# Generates HTML output of the coverage information for perusal
|
||||
add_custom_target(
|
||||
ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
COMMAND
|
||||
${GENHTML_PATH} -o
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
${COVERAGE_INFO}
|
||||
DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT
|
||||
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
# AUTO
|
||||
if(target_code_coverage_AUTO)
|
||||
if(NOT TARGET ccov)
|
||||
add_custom_target(ccov)
|
||||
endif()
|
||||
add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
|
||||
if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID
|
||||
MATCHES "GNU")
|
||||
if(NOT TARGET ccov-report)
|
||||
add_custom_target(ccov-report)
|
||||
endif()
|
||||
add_dependencies(
|
||||
ccov-report
|
||||
ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ALL
|
||||
if(target_code_coverage_ALL)
|
||||
if(NOT TARGET ccov-all-processing)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
|
||||
)
|
||||
endif()
|
||||
|
||||
add_dependencies(ccov-all-processing
|
||||
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Adds code coverage instrumentation to all targets in the current directory and
|
||||
# any subdirectories. To add coverage instrumentation to only specific targets,
|
||||
# use `target_code_coverage`.
|
||||
function(add_code_coverage)
|
||||
if(CODE_COVERAGE)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
|
||||
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
|
||||
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
|
||||
add_link_options(-fprofile-instr-generate -fcoverage-mapping)
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
|
||||
"GNU")
|
||||
add_compile_options(-fprofile-arcs -ftest-coverage)
|
||||
link_libraries(gcov)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Adds the 'ccov-all' type targets that calls all targets added via
|
||||
# `target_code_coverage` with the `ALL` parameter, but merges all the coverage
|
||||
# data from them into a single large report instead of the numerous smaller
|
||||
# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for
|
||||
# use with coverage dashboards (e.g. codecov.io, coveralls).
|
||||
# ~~~
|
||||
# Optional:
|
||||
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex!
|
||||
# ~~~
|
||||
function(add_code_coverage_all_targets)
|
||||
# Argument parsing
|
||||
set(multi_value_keywords EXCLUDE)
|
||||
cmake_parse_arguments(add_code_coverage_all_targets "" ""
|
||||
"${multi_value_keywords}" ${ARGN})
|
||||
|
||||
if(CODE_COVERAGE)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
|
||||
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
|
||||
|
||||
# Merge the profile data for all of the run executables
|
||||
if(WIN32)
|
||||
add_custom_target(
|
||||
ccov-all-processing
|
||||
COMMAND
|
||||
powershell -Command $$FILELIST = Get-Content
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe
|
||||
merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
|
||||
-sparse $$FILELIST)
|
||||
else()
|
||||
add_custom_target(
|
||||
ccov-all-processing
|
||||
COMMAND
|
||||
${LLVM_PROFDATA_PATH} merge -o
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`)
|
||||
endif()
|
||||
|
||||
# Regex exclude only available for LLVM >= 7
|
||||
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
|
||||
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
|
||||
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
|
||||
-ignore-filename-regex='${EXCLUDE_ITEM}')
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Print summary of the code coverage information to the command line
|
||||
if(WIN32)
|
||||
add_custom_target(
|
||||
ccov-all-report
|
||||
COMMAND
|
||||
powershell -Command $$FILELIST = Get-Content
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe
|
||||
report $$FILELIST
|
||||
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
|
||||
${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-all-processing)
|
||||
else()
|
||||
add_custom_target(
|
||||
ccov-all-report
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} report `cat
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
|
||||
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
|
||||
${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-all-processing)
|
||||
endif()
|
||||
|
||||
# Export coverage information so continuous integration tools (e.g.
|
||||
# Jenkins) can consume it
|
||||
add_custom_target(
|
||||
ccov-all-export
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} export `cat
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
|
||||
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
|
||||
-format="text" ${EXCLUDE_REGEX} >
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
|
||||
DEPENDS ccov-all-processing)
|
||||
|
||||
# Generate HTML output of all added targets for perusal
|
||||
if(WIN32)
|
||||
add_custom_target(
|
||||
ccov-all
|
||||
COMMAND
|
||||
powershell -Command $$FILELIST = Get-Content
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show
|
||||
$$FILELIST
|
||||
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
|
||||
-show-line-counts-or-regions
|
||||
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
|
||||
-format="html" ${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-all-processing)
|
||||
else()
|
||||
add_custom_target(
|
||||
ccov-all
|
||||
COMMAND
|
||||
${LLVM_COV_PATH} show `cat
|
||||
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
|
||||
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
|
||||
-show-line-counts-or-regions
|
||||
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
|
||||
-format="html" ${EXCLUDE_REGEX}
|
||||
DEPENDS ccov-all-processing)
|
||||
endif()
|
||||
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
|
||||
"GNU")
|
||||
set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info")
|
||||
|
||||
# Nothing required for gcov
|
||||
add_custom_target(ccov-all-processing COMMAND ;)
|
||||
|
||||
# Exclusion regex string creation
|
||||
set(EXCLUDE_REGEX)
|
||||
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
|
||||
set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
|
||||
'${EXCLUDE_ITEM}')
|
||||
endforeach()
|
||||
|
||||
if(EXCLUDE_REGEX)
|
||||
set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
|
||||
${COVERAGE_INFO})
|
||||
else()
|
||||
set(EXCLUDE_COMMAND ;)
|
||||
endif()
|
||||
|
||||
# Capture coverage data
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
|
||||
add_custom_target(
|
||||
ccov-all-capture
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
|
||||
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
|
||||
--output-file ${COVERAGE_INFO}
|
||||
COMMAND ${EXCLUDE_COMMAND}
|
||||
DEPENDS ccov-preprocessing ccov-all-processing)
|
||||
else()
|
||||
add_custom_target(
|
||||
ccov-all-capture
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
|
||||
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
|
||||
--output-file ${COVERAGE_INFO}
|
||||
COMMAND ${EXCLUDE_COMMAND}
|
||||
DEPENDS ccov-preprocessing ccov-all-processing)
|
||||
endif()
|
||||
|
||||
# Generates HTML output of all targets for perusal
|
||||
add_custom_target(
|
||||
ccov-all
|
||||
COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
|
||||
${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR}
|
||||
DEPENDS ccov-all-capture)
|
||||
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET ccov-all
|
||||
POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT
|
||||
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report."
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include(xo_macros/xo_cxx)
|
||||
include(xo_macros/code-coverage)
|
||||
1426
xo-indentlog/xo-cmake/cmake/xo_macros/xo_cxx.cmake
Normal file
1426
xo-indentlog/xo-cmake/cmake/xo_macros/xo_cxx.cmake
Normal file
File diff suppressed because it is too large
Load diff
38
xo-indentlog/xo-cmake/etc/xo/subsystem-list
Normal file
38
xo-indentlog/xo-cmake/etc/xo/subsystem-list
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
xo-cmake
|
||||
xo-indentlog
|
||||
xo-refcnt
|
||||
xo-subsys
|
||||
xo-randomgen
|
||||
xo-ordinaltree
|
||||
xo-pyutil
|
||||
xo-flatstring
|
||||
xo-reflectutil
|
||||
xo-reflect
|
||||
xo-pyreflect
|
||||
xo-ratio
|
||||
xo-unit
|
||||
xo-pyunit
|
||||
xo-expression
|
||||
xo-pyexpression
|
||||
xo-tokenizer
|
||||
xo-reader
|
||||
xo-jit
|
||||
xo-pyjit
|
||||
xo-callback
|
||||
xo-webutil
|
||||
xo-pywebutil
|
||||
xo-printjson
|
||||
xo-pyprintjson
|
||||
xo-reactor
|
||||
xo-pyreactor
|
||||
xo-websock
|
||||
xo-pywebsock
|
||||
xo-statistics
|
||||
xo-distribution
|
||||
xo-pydistribution
|
||||
xo-simulator
|
||||
xo-pysimulator
|
||||
xo-process
|
||||
xo-pyprocess
|
||||
xo-kalmanfilter
|
||||
xo-pykalmanfilter
|
||||
2816
xo-indentlog/xo-cmake/share/xo-macros/Doxyfile.in
Normal file
2816
xo-indentlog/xo-cmake/share/xo-macros/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load diff
30
xo-indentlog/xo-cmake/share/xo-macros/gen-ccov.in
Normal file
30
xo-indentlog/xo-cmake/share/xo-macros/gen-ccov.in
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
srcdir=@PROJECT_SOURCE_DIR@
|
||||
builddir=@PROJECT_BINARY_DIR@
|
||||
|
||||
#lcov=@LCOV_EXECUTABLE@
|
||||
#genhtml=@GENHTML_EXECUTABLE@
|
||||
#
|
||||
#if [[ $lcov == "LCOV_EXECUTABLE-NOTFOUND" ]]; then
|
||||
# echo "gen-ccov: lcov executable not found"
|
||||
# exit 1
|
||||
#fi
|
||||
#
|
||||
#if [[ $genhtml == "GENHTML_EXECUTABLE-NOTFOUND" ]]; then
|
||||
# echo "gen-ccov: genhtml executable not found"
|
||||
# exit 1
|
||||
#fi
|
||||
|
||||
lcovharness=@XO_CMAKE_LCOV_HARNESS_EXECUTABLE@
|
||||
|
||||
if [[ -z $lcovharness ]]; then
|
||||
echo "gen-ccov: lcov-harness executable (XO_CMAKE_LCOV_HARNESS_EXECUTABLE) not configured"
|
||||
echo "gen-ccov: expect value of path/to/xo-cmake-config --lcov-harness-exe"
|
||||
echo "gen-ccov: stored in XO_CMAKE_LCOV_HARNESS_EXECUTABLE by xo_toplevel_testing_options2()"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO: allow providing LCOV_EXECUTABLE GENHTML_EXECUTABLE here
|
||||
|
||||
$lcovharness $srcdir $builddir
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# ----------------------------------------------------------------
|
||||
# for example:
|
||||
# $ PREFIX=/usr/local # for example
|
||||
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||
#
|
||||
# will get
|
||||
# CMAKE_MODULE_PATH
|
||||
# from xo-cmake-config --cmake-module-path
|
||||
#
|
||||
# and expect .cmake macros in
|
||||
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||
|
||||
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
|
||||
message(FATAL "could not find xo-cmake-config executable")
|
||||
endif()
|
||||
|
||||
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
|
||||
|
||||
if (NOT XO_SUBMODULE_BUILD)
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||
# default to typical install location for xo-project-macros
|
||||
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||
#
|
||||
include(xo_macros/xo_cxx)
|
||||
|
||||
xo_cxx_bootstrap_message()
|
||||
Loading…
Add table
Add a link
Reference in a new issue