The Cppm Book (BETA)

Cppm is BETA Project Please Give Feedback

Cppm is CMake based Cross Platform C++ package manager
Manage and Downloads C++ dependencies, easy project build, install, upload to cppkg

Features

  • Easy to use CMake
  • Easy Config file than CMakeList.txt
  • Generate Cross Platform CMake base C++ Project
  • Dependencies auto install in build step
  • can use cmake uninstall target
  • CMake base project can build with cppm (cppm build) (Use CMake's toolchain feature)
  • Manage Depdendency with cppkg and Hunter Package Manage
  • Dependency install only use CMake

Getting Started

To get started with Cppm, Install this Dependency

  • C++ Compiler (c++17) # can use filesystem.h and optional.h
  • CMake (3.12)
  • ccache (optional) # Build cache

Installation

Install Cppm

cppm install command install defualt prefix is $HOME/.cppm/bin

Require

  • C++17 Compiler (AppleClang can't build)
  • CMake (Minumum 3.12)
  • git
  • ccache (optional)

Linux and MacOS

git clone --recurse-submodules https://github.com/injae/cppm.git
cd cppm
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DUSE_CPPM_PATH=ON .
cd build
cmake --build . --config Release
cd Release
./cppm build install
# Add to cppm path
export PATH=$PATH:$HOME/.cppm/bin

Windows

# need visual studio , git , cmake
# scoop install git cmake
git clone --recurse-submodules https://github.com/injae/cppm.git
cd cppm
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DUSE_CPPM_PATH=ON .
cd build
cmake --build . --config Release --target install --target cppm_link
# Add System Path %USERPROFILE%\.cppm\bin

First Steps with Cppm

To start a new package with Cppm, use cppm init

$ cppm init -h  
Usage:
   cppm init [--verbose]

Option:
   --bin [-b] {name}               :initialize new c++ binary project
   --help [-h]                     :show cppm commands and options
   --lib [-l] {name}               :initialize new c++ library project

New Binary Project

$ cppm init --bin hello_world

Generated project

$ cd hello_world
$ tree .  
.
|-- build  # Build directory
|   |-- Debug   # Debug mode target directory 
|   `-- Release # Release mode target directory
|-- cmake    # cmake module path
|   |-- Modules  # cmake Find*.cmake path
|   `-- cppm_tool.cmake # cppm_tool load tool
|-- cppm.toml
|-- include   # public header
|-- src       # private header and sources
|   `-- main.cpp
`-- thirdparty # cppkg config and file path
    `-- ${cppkg_name}/${cppkg_version}/cppkg.toml  # cppkg config file

Cppm project config file

# cppm.toml
[package]
   name = "hello_world"
   version = "0.0.1"
   description = ""

[[bin]]
   name = "hello_world"
   source = ["src/.*"]

Generated defualt cpp file

// src/main.cpp
#include <iostream>
int main(int argc, char* argv[]) {
    std::cout<<"hello world"<<std::endl;
    return 0; 
} 

Compile "hello_world" project with cppm build, this command generate CMakeLists.txt

$ cppm build
[cppm] Generate CMakeLists.txt
From https://github.com/injae/cppm_tools
   1c79dca..00fb374  0.0.9      -> origin/0.0.9
-- [cppm] cppm_tools-0.0.9 download to /home-path/.cppm/src/cppm_tools/0.0.9
-- The C compiler identification is Clang 9.0.0
-- The CXX compiler identification is Clang 9.0.0
-- Check for working C compiler: /usr/local/opt/llvm/bin/clang
-- Check for working C compiler: /usr/local/opt/llvm/bin/clang -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/local/opt/llvm/bin/clang++
-- Check for working CXX compiler: /usr/local/opt/llvm/bin/clang++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- [cppm] Target: hello_world [Type:debug, Cppm:0.0.9, CMake:3.16.2]
-- [cppm] System: x86_64-Darwin-19.3.0
-- [cppm] Compiler: Clang-9.0.0
-- [cppm] Generator: Unix Makefiles
-- [cppm] Build Cache: ccache
-- [cppm] cppm_root: /path/to/.cppm
-- [cppm] c++ version: 17
-- [cppm] Compiler Option: -std=c++17 -Wall -fPIC -O0 -g
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/hello_world/build
Scanning dependencies of target hello_world
[ 50%] Building CXX object CMakeFiles/hello_world.dir/src/main.cpp.o
[100%] Linking CXX executable hello_world
[100%] Built target hello_world

And the run it.

$ cd build/Debug
$ ./hello_world
hello world

Easy run command cppm run, default binary name ${[package.name]}

$ cppm run
hello world

Generated CMakeLists.txt

cmake_minimum_required(VERSION 3.12)

include(cmake/cppm_tool.cmake)
cppm_project()
project(hello_world VERSION 0.0.1 LANGUAGES C CXX)
cppm_setting()

cppm_cxx_standard(17)
cppm_compiler_option(DEFAULT)

cppm_target_define(hello_world BINARY 
SOURCES 
        src/main.cpp
)

cppm_target_dependencies(hello_world)

cppm_target_install(hello_world)

Changed project Directory

$ tree .
.
├── CMakeLists.txt
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.17.1
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   ├── a.out
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       ├── a.out
│   │   │       └── tmp
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── Makefile.cmake
│   │   ├── Makefile2
│   │   ├── TargetDirectories.txt
│   │   ├── cmake.check_cache
│   │   ├── progress.marks
│   │   ├── hello_world.dir
│   │   │   ├── CXX.includecache
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── depend.internal
│   │   │   ├── depend.make
│   │   │   ├── flags.make
│   │   │   ├── link.txt
│   │   │   ├── progress.make
│   │   │   └── src
│   │   │       └── main.cpp.o
│   │   └── hello_world_info.dir
│   │       ├── DependInfo.cmake
│   │       ├── build.make
│   │       ├── cmake_clean.cmake
│   │       └── progress.make
│   ├── Debug
│   │   └── hello_world  # Binary or Library export Directory
│   ├── Makefile
│   ├── cmake_install.cmake
│   └── compile_commands.json
├── cmake
│   ├── Modules
│   └── cppm_tool.cmake
├── cppm.toml
├── include
├── src
│   └── main.cpp
└── thirdparty 

Cppm Guide

Cppm Base Project Layout

.
|-- cppm.toml # cppm config file
|-- CMakeLists.txt # <-- generated from cppm.toml 
|-- build  # Build directory
|   |-- Debug   # Debug mode target directory 
|   `-- Release # Release mode target directory
|-- cmake    # cmake module path
|   |-- Modules  # cmake Find*.cmake path
|   `-- cppm_tool.cmake # cppm_tool load script (require)
|-- include   # public header (export header)
|-- src       # private header and sources
|   `-- main.cpp
|-- test      # test src directory 
|   `-- test01.cpp 
|-- examples  # example src directory
|   `-- example01.cpp 
`-- thirdparty # cppkg config and file path
    `-- ${cppkg_name}/${cppkg_version}/cppkg.toml  # cppkg config file

Cppm Structure

cppm use cmake toolchain feature cppm build command use CMakeCache.txt

cppm build command search cppm.toml or CMakeLists.txt

$ cppm build --detail 
cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/.cppm/cppkg/cppm-tools-0.0.11/toolchain.cmake -DUSE_CPPM_PATH=ON -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . --config Debug --  -j{your cpu core}
~/.cppm/
├── bin  # cppm installed binary path (symbolic links)
│   └── cppm # -> ../share/cppm-0.0.10/bin/cppm
├── cache ... # cmake build cache
├── cmake  # cppm module path
│   ├── HunterGate.cmake # if you use hunter package this file copy your package cmake/HunterGate.cmake
│   └── cppm_tool.cmake 
├── repo # cppkg regiested package installer path 
│   └── cppkg
│       ├── Catch2
│       │   ├── 2.9.1
│       │   │   ├── Catch2.cmake.in
│       │   │   └── cppkg.toml
│       │   └── git
│       │       ├── Catch2.cmake.in
│       │       └── cppkg.toml
│       ├── cppm
│       │   └── git
│       .       ├── cppkg.toml
│       .       └── cppm.cmake.in 
└── cppkg  # cppkg install path
    ├── cppm-0.0.11
    .   ├── bin ...
    .   ├── include ...
    .   └── lib ...
 

cppkg.toml

[package]
    name = "..." # (require)
    version = "..." # (require)
    desciprtion = "..." # (require)
    standard = "{11|14|17(default)|20}"
    git = "..." # this option use to cppm build --export
    vcpkg= "false(default)|true" # cppm auto detect vcpkg option
[hunter] # (optional)  hunter package manager setting option
    use = true|false(default) # if you use hunter package this option is default is true
    url = "..." # custom hunter version url setting option, [ url, sha1 ] is pair
    sha1 = "..." # custom hunter version sha1 setting option, [ url, sha1 ] is pair
[cmake] # (optional) cmake setting option 
    include = ["xxx.cmake", ...] # this option is include cmake file
    version = "..." # minimum cmake version setting option default is 3.12
    option = "..." # cmake build option use to command 'cppm build'
    toolchains = "..." # (incomplete) cmake toolchains option

Profiles Prfiles provide a way alter the compiler settings, Profile settings can be overridden for specific packages

[profile.dev] # this is only debug or test or example mode compiler option
[profile.release] # this is only release mode compiler option
[profile.test] # (incomplete) this is only test mode compiler option
[profile.release.compiler] # this is only release mode compiler option
    clang = "..." # compiler option
    gcc = "..." # compiler option
    msvc = "..." # compiler option
[profile.release.package.{package.name}] # (incomplete) override for specific packages
[profile.release.package.{package_name}.compiler] # (incomplete) this is only release mode compiler option
[[bin]]
    name = "..." # target name (require)
    sources = ["src/ss/.*", src/xx.cpp, ...] # (require), source files, you can use regex
    install = true(default)|false  # disable install setting
[lib]
    name = "..." # target name, export cmake package name 
    type = "static(default)|shared|hearder-only" 
    namespace = "... " (default: [package.name]) # cmake export namespace  
    sources = ["src/xxx.cpp", ...] # type = "header_only" this option not working
    install = true(default)|false  # disable install setting
[[test]]
    name = "..." # target name, export cmake package name 
    type = "binary(default)|static|shared|hearder-only" 
    sources = ["src/xxx.cpp", ...] # use source files, type = "header_only" no nessasery this optio
    install = true|false(default)  # disable install setting
[[example]]
    name = "..." # target name, export cmake package name 
    type = "binary(default)|static|shared|hearder-only" 
    sources = ["src/xxx.cpp", ...] # use source files, type = "header_only" no nessasery this option
    install = true|false(default)  # disable install setting
# sub project setting
[workspace]
    member = ["path/", "path"]
[dev-dependency] # this dependency only work debug mode
[dependencies] 
    # cppkg package add dependency
    # name   version(require)
      ... = "x.x.x|git|latest" # this config find cppkg.toml in ${package root}/thirdparty/${name}/${version}/cppkg.toml
    # inline dependency setting
    # name        (require)                   (optional)           (optional) default is public            (optional) package type  
      ... = {module = "..." , version ="...", components="... ...", link = "public|private|interface", type="lib(default)|bin|cmake(incomplete)", repo="cppkg(default)|hunter" }
    # hunter package add dependency
    # name        (require)                   (optional)           (optional) default is public            (require) load hunter package
      ... = {module = "..." , version ="latest", components="... ...", link = "public|private|interface" ,    repo="hunter" }
    # none cmake package add dependency
    # name        (require)            (optional)           (optional) default is public (require) none cmake package
      ... = {module = "...", components="... ...", lnk_type = "public|private|interface", no_module= true }
    # module is name that cmake find_package 
    # matched cmake scirpt
    # find_package(${name} ${version} COMPONENTS ${components})
    # target_link_libraries(${target} ${lnk_type} ${module})
    #link.public    => this library use header and source , dependency foward 
    #link.private   => this library use source only, dependency not forward
    #link.interface => this library use header only, dependency forward
[target.{triplet}.dependencies] 
[target.{triplet}.dev-dependencies] 

# triplet types
# {arch}-{platform}-{compiler}
# arch
# x86 or x64 or arm or arm64 or arm64s or arm7s or all(default)
# platform
# (       unix                      )    (   windows  )
# linux or osx or freebsd or android or uwp or windows
# compiler
# (        clang               )   
# clang or clang_cl apple_clang or gnu(gcc) or msvc(cl) or intel or cuda(nvidia)

## example
[target.x64-windows-msvc.dependencies]
[target.all-windows-msvc.dependencies]
[target.windows.dependencies]
[target.unix.dependencies]
[target.x64-macos-apple_clang.dependencies]
[target.x64-macos-clang.dependencies]
[target.macos.dependencies]
[target.linux.dependencies]
# (incomplete)
[exclude]

# (incomplete)
[ci.github_action]
[ci.appveyer]
[ci.azure]
[ci.travis]

Dependencies

cppkg is cppm central package repository
cppm use it by default to find requested packages
if you want to use Hunter Package Manager manager set repo="hunter"
cppm auto detect vcpkg (with package.vcpkg config) you can find package with cppm cppkg search

$ cppm cppkg search -h
Usage:
   cppm cppkg search [--verbose]

Option:
   --all [-a]                      :show all package 
   --help [-h]                     :show cppm commands and options
   --repo [-r] {repo name}         :show {repo_name}'s packages  # cppkg or hunter

Adding a Dependency

add [dependencies] in your cppm.tomlfile

[dependencies]
fmt = "6.2.0"

Re-run cppm build, and Cppm Search yout package thirdparty directory thirdparty/fmt/6.2.0/cppkg.toml
if can't find it cppm search from cppkg repository and copy to thirdparty/fmt/6.2.0/cppkg.toml

You can easily add unregistered packages

If you want to add unregistered package need to make cppkg.toml file cmake base unregistered package is only need 3 config version git or url *** module *** no cmake base package need to custom downloader cppm provides commands to create cppkg.toml

cppkg file generate command

$ cppm run cppkg init -h                                                                                                             
Usage:
   cppm cppkg init [--verbose]

Option:
   --des [-d] {description}        :add description    
   --flags [-f] {arg}              :add cmake build flags
   --git [-g] {repo}               :add git repo       
   --git_tag [-t] {tag}            :add git tag        
   --help [-h]                     :show cppm commands and options
   --module [-m] {module}          :add module name    
   --type [-t] {arg}               :add type default is lib
   --uri [-U] {arg}                :auto detect uri or git and version
   --url [-u] {url}                :add url repo Require --version
   --version [-v] {version}        :add library version Require --version

Example fmt library

6.2.0 version config

# thirdparty/fmt/6.2.0/cppkg.toml
[fmt]
version = "6.2.0"
description = "A modern formatting library"
module = "fmt::fmt"
url="https://github.com/fmtlib/fmt/releases/download/6.2.0/fmt-6.2.0.zip"
flags="-DFMT_DOC=OFF -DFMT_TEST=OFF -DFMT_FUZZ=OFF" # (optional) for cmake build fast flags

git repository version
git version dependency always version check in CMake build step

# thirdparty/fmt/git/cppkg.toml
[fmt]
version = "git"
description = "A modern formatting library"
module = "fmt::fmt"
git="https://github.com/fmtlib/fmt.git"
flags="-DFMT_DOC=OFF -DFMT_TEST=OFF -DFMT_FUZZ=OFF" # (optional) for cmake build fast flags

Easy Way to use cppkg init command

# fmt 6.2.0
$ cppm cppkg init -u "https://github.com/fmtlib/fmt/releases/download/6.2.0/fmt-6.2.0.zip" -v "6.2.0" -m "fmt::fmt" fmt
# or (auto detect version)
$ cppm cppkg init -U "https://github.com/fmtlib/fmt/releases/download/6.2.0/fmt-6.2.0.zip" fmt
# fmt git
$ cppm cppkg init -U "https://github.com/fmtlib/fmt.git" fmt
# or (auto detect version)
$ cppm cppkg init -g "https://github.com/fmtlib/fmt.git" -m "fmt::fmt" fmt
# fmt 6.2.0 with cmake flags
$ cppm cppkg init -U "https://github.com/fmtlib/fmt/releases/download/6.2.0/fmt-6.2.0.zip" --flags "-DFMT_DOC=OFF -DFMT_TEST=OFF -DFMT_FUZZ=OFF" fmt

Hunter package manager version

# cppm.toml
[dependencies]
fmt = {version="latest", module="fmt::fmt", repo="hunter"}

Custom Downloader

#thirdparty/fmt/6.2.0/cppkg.toml
[fmt]
version = "6.2.0"
description = "A modern formatting library"
module = "fmt::fmt"
url="https://github.com/fmtlib/fmt/releases/download/6.2.0/fmt-6.2.0.zip"
custom=true 
#helper=""(incomplete)

ExternalProject_add

# Cppkg Base Dependency Downloader
# Almost same cmake ExternalProject_Add
# Other Options:
# - Linux Configures:
#    L_CONFIGURE {...}, L_BUILD {...}, L_INSTALL {...}
# - Windows Configures:
#    W_CONFIGURE {...}, W_BUILD {...}, W_INSTALL {...}
# - Install Path Options:
#    LOCAL(default) GLOBAL 
cmake_minimum_required(VERSION 3.6)
project(fmt-6.2.0-install C CXX)

include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppm_tool.cmake)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH})
download_package(fmt 6.2.0  URL https://github.com/fmtlib/fmt/releases/download/6.2.0/fmt-6.2.0.zip CMAKE_ARGS ${CMAKE_ARGS} -DFMT_DOC=OFF -DFMT_TEST=OFF -DFMT_FUZZ=OFF)

Dependency Format

[${name}] 
    version = "x.x.x|git|latest(hunter only)"
    type = "lib(default)|bin|cmake(incomplete)"
    git = "..." # if git version 
    url = "..." # if x.x.x version
    module = "..." # (require)
    link = "public(default)|private|interface"
    custom = "false(default)|true" # cppm build time cppkg.toml translate ${name}.cmake.in in 
                                   # if you want to custom installer(${name}.cmake.in) set true
    repo = "cppkg(default)|hunter"
    flags="${cmake build flags}"
    helper="Find...cmake" (optional) cmake/Modules/Find${package_name}.cmake 
    components = "..."

Cppm Commands

  • ***cppm build ***
  • ***cppm cppkg ***
  • ***cppm init ***
  • ***cppm test ***
  • ***cppm update ***