The C++ submodule manager is fundamentally a set of conventions for structuring projects with Git and CMake rather than just a new tool. The goal is to make it economical to compose projects from independently developed packages. You can basically spin off a library with CI integration and with dependencies to other libraries and have it usable as a manageable dependency in other projects in a matter of minutes or less.
Basic features:
Additional features:
See repositories with the #cppsm topic .
cppsm commandcppsm commandThe cppsm command is basically a set of fairly simple
Bash scripts that automate
various operations on projects adhering to the C++ submodule manager
conventions. All the hard work is already done by
Git , CMake , and other tools and
services used. Any cppsm project can be used and developed without the cppsm
command itself.
The cppsm command has been developed
with Bash and used at least
under macOS , Linux
(Ubuntu ), Cygwin , and
Git BASH .
There currently is no "native" Windows support. I personally have no need for such support as I'm comfortable using Cygwin and Git BASH . If you'd like to work on native Windows support, or support for some other platform, then you are welcome to contribute to the project!
At the moment the cppsm scripts have only been tested under
Bash (compatible) shell
implementations. I would like to support other shells in the future.
Contributions in that regard are also welcome!
To install the cppsm command you need to clone its
repository and add its bin directory to
your PATH.
Automated installation
using the install
script is as easy as running the pipe
curl -s https://raw.githubusercontent.com/cppsm/cppsm-cli/master/install | bash
using curl or the pipe
wget -qO- https://raw.githubusercontent.com/cppsm/cppsm-cli/master/install | bash
using wget .
Manual installation is
not hard either. Just clone the
cppsm-cli repository somewhere, e.g.
$HOME/.cppsm:
git clone --single-branch https://github.com/cppsm/cppsm-cli.git "$HOME/.cppsm"
And add the following lines to your
$HOME/.bashrc or $HOME/.bash_profile :
CPPSM="$HOME/.cppsm"
export PATH="$CPPSM/bin:$PATH"
. "$CPPSM/bash_completion" # NOTE: This installs optional Bash completion
Optional dependencies:
For optional code formatting
you need to have both
clang-format and
prettier commands in path.
For optional auto completion
of GitHub urls you must have either
curl or
wget command and
jq command in path.
For optional watch commands
you must have either fswatch or
watchexec command in path.
Here is a quick tour of basic cppsm functionality.
Create a new empty project:
mkdir PROJECT && cd "$_"
cppsm init
At this point you could try adding dependencies or writing code, but let's actually try the hello world example:
cppsm init-hello
cppsm test
.build*/internals/hello
Start hacking:
emacs internals/program/hello.cpp &
cppsm test-watch
Format project files inplace:
cppsm format
Clone an existing project:
cppsm clone URL BRANCH
Or clone an existing project using plain git:
git clone -b BRANCH URL/NAME.git
cd NAME
git submodule update --init # NOTE: non-recursive
Add a required library:
cppsm add requires URL/NAME.git BRANCH
Remove a previously required library:
cppsm remove requires/NAME/BRANCH
Upgrade all required libraries:
cppsm upgrade
Below is reference documentation for each cppsm subcommand. Each subcommand
also prints out short instructions to the console in case the invocation is
incorrect.
cppsmWhen invoked without specifying a subcommand, cppsm displays a brief usage
instruction and cppsm version information.
cppsm add (requires|equipment) <url> <branch>Adds a new cppsm compatible submodule and recursively the submodules it requires to the project. This command is idempotent and can be run to e.g. add new transitive dependencies after updating submodules.
cppsm buildSets up a build directory and builds the project. See
cppsm setup for the configuration variables.
cppsm build-watchSets up a build directory, builds the project, and starts a file system watch to
build the project on any changes to project files. See
cppsm setup for the configuration variables.
cppsm clone <url> <branch>Clones the specified cppsm compatible repository and its dependencies.
cppsm formatFormats project files inplace using clang-format and prettier .
cppsm initInitializes a new project with cppsm configuration files when run in an empty
directory or updates an existing project to use the latest configuration files.
See also cppsm init-hello and
cppsm init-library.
Configuration variables:
NAME='...' specifies the base name for
the project and defaults to the name of the current directory.
VERSION='v1'|'...' specifies the
branch and version suffix for the project.
cppsm init-helloInitializes a new project with an example "Hello, world!" program. This is only
intended for educational purposes. See also cppsm init and
cppsm init-library.
CMakeLists.txt
equipment/
testing.cpp/
v1/
provides/
CMakeLists.txt
include/
testing_v1/
[...]
library/
[...]
internals/
CMakeLists.txt
testing/
message_test.cpp
program/
hello.cpp
provides/
CMakeLists.txt
include/
message_v1/
hello.hpp
library/
hello.cpp
cppsm init-libraryInitializes a new project with boilerplate for a simple library project in an
empty directory. See also cppsm init and
cppsm init-hello.
CMakeLists.txt
internals/
CMakeLists.txt
testing/
compile_synopsis_test.cpp
provides/
CMakeLists.txt
include/
${LIBRARY_NAME}_${LIBRARY_VERSION}/
synopsis.hpp
cppsm listPrints out a dependency tree of submodules. This command exits with an error code in case any problems are found in the dependency tree.
cppsm remove <path>Removes a previously required submodule. Note that this command does not remove submodules transitively.
cppsm setupSets up a build directory. The build directory name is determined based on the configuration variables.
Configuration variables:
CMAKE_BUILD_TYPE=Debug|Release
specifies which configuration to use .
CMAKE_TOOLCHAIN_FILE=''|'...'
specifies toolchain file to use .
CC=cc|gcc|clang|emcc|...
specifies which C compiler to use .
CXX=c++|g++|clang++|emcc|...
specifies which C++ compiler to use .
CLEAN=0|1 specifies whether the build directory
should be recreated from scratch.
COVERAGE=0|1 specifies whether the build
should be configured to generate coverage information. Currently code coverage
is only supported on GCC and
Clang .
cppsm testSets up a build directory, builds the project, and runs the tests. See
cppsm setup for the configuration variables.
cppsm test-watchSets up a build directory, builds the project, runs the tests, and starts a file
system watch to build and test the project on any changes to project files. See
cppsm setup for the configuration variables.
cppsm updatePulls the current git branch and updates all cppsm managed submodules to the versions in the branch.
cppsm upgradeUpgrades all cppsm managed submodules to latest remote versions and runs
cppsm init to update configuration files.
CMake boilerplate is provided for projects and simple libraries, tests, and executables.
conventional-project.cmakeconventional-project.cmake
is a CMake script that (only) defines a number of CMake functions for projects
that adhere to the project structure.
Typically, when using the C++ submodule manager, one does not directly call these functions as they are called by the automatically generated boilerplate code.
add_conventional_targets_under(directory)Recursively descends into the specified directory tree stopping at directories
containing aCMakeLists.txt script and adds those
target directories to the project with
add_subdirectory .
add_conventional_targets_provided_under(directory)Recursively descends into the specified directory tree stopping at project submodule directories and adds those submodules to the project.
.cppsm directory, then the provides
directory of the submodule is added with the
add_conventional_targets_under function.CMakeLists.txt script, then
the directory is added to the project with
add_subdirectory .add_conventional_targets()Adds conventional targets into a project.
Specifically, calls
and, when called from the top-level of a CMake source tree, also calls
conventional-targets.cmakeconventional-targets.cmake
is a CMake script that (only) defines a number of CMake functions for defining
targets that adhere to the
conventional target structure.
add_conventional_executable(name)Adds an executable target with the given name. Assumes that the target directory
has implementation files matching the pattern program/*.{cpp,hpp}.
CMakeLists.txt
program/
*.{cpp,hpp}
Add dependencies using
target_link_libraries
separately.
add_conventional_executable_tests(...)Adds an executable test target per file matching pattern testing/*.cpp. The
arguments given to add_conventional_executable_tests are passed to
target_link_libraries
for each added test target.
CMakeLists.txt
testing/
*.cpp
add_conventional_library(${LIBRARY_NAME}_${LIBRARY_VERSION})Adds a library target with the given name. Assumes that the target directory has
public header files matching the pattern
include/${LIBRARY_NAME}_${LIBRARY_VERSION}/*.hpp
and implementation files matching the pattern library/*.{cpp,hpp}.
CMakeLists.txt
include/
${LIBRARY_NAME}_${LIBRARY_VERSION}/
*.hpp
library/
*.(cpp|hpp)
Note that inside include there is a directory whose name
${LIBRARY_NAME}_${LIBRARY_VERSION}
suggests that it should include both the library name and its major version. The
intention of the directory is to differentiate between the header files of
different targets (and their major versions).
A Travis CI
configuration file
and CI script is
provided to build and test both Debug and Release builds on various OS
(Linux, OS X, Windows) and compiler configurations
(Clang , GCC ,
Visual C++ ,
Emscripten ). Just add your project to Travis CI.
Configuration variables:
CLANG=1|0 specifies whether to build with
Clang . Set CLANG=0 explicitly to skip building
with Clang .
CODECOV=0|1 specifies whether a code
coverage test is executed and results are pushed to
Codecov . Set CODECOV=1 explicitly to enable code
coverage.
EMSCRIPTEN=0|1 specifies whether to
also build with Emscripten . Set EMSCRIPTEN=1
explicitly to build with Emscripten .
FORMAT_CHECK=1|0 specifies whether
to check that source files are formatted as with
cppsm format. Set FORMAT_CHECK=0 explicitly to disable
the format check.
GCC=1|0 specifies whether to build with
GCC . Set GCC=0 explicitly to skip building with
GCC .
INSTALL_WAIT=0|1 specifies whether
installation of additional packages is performed concurrently with builds when
possible. Set INSTALL_WAIT=1 explicitly to wait for installations to
complete before starting any builds.
UPGRADE_CHECK=1|0 specifies
whether, after running tests, a cppsm upgrade is performed
and, if something is upgraded, tests are rerun. Set UPGRADE_CHECK=0
explicitly to skip the upgrade and conditional rerun of tests.
VS2017=1|0 specifies whether to build with
Visual C++ 2017. Set VS2017=0
explicitly to skip building with
Visual C++ 2017.
VS2019=1|0 specifies whether to build with
Visual C++ 2019. Set VS2019=0
explicitly to skip building with
Visual C++ 2019.
Several environment variables can be set to change the default behavior of one
or more cppsm commands. These variables can be used both on the CI
and also when using cppsm commands locally.
CTEST_OUTPUT_ON_FAILURE=1|0By default CTest is configured to
log output in case a test fails .
Set CTEST_OUTPUT_ON_FAILURE=0 explicitly to override the default.
QUIET=1|0By default various commands are invoked with quiet settings to reduce noise. Set
QUIET=0 explicitly to see more output from various commands.
GIT_QUIET=QUIET controls the --quiet
switch for Git commands.
MSBUILD_VERBOSITY=QUIET|MINIMAL|NORMAL|DETAILED|DIAGNOSTIC
controls the
/VERBOSITY
switch for
MSBuild . If
MSBUILD_VERBOSITY is not explicitly set and QUIET=1 then
MSBUILD_VERBOSITY will be set to QUIET.
XCODE_VERBOSITY=quiet|verbose
optionally passes either -quiet or -verbose flag to the xcodebuild
command. If XCODE_VERBOSITY is not explicitly set and QUIET=1 then
XCODE_VERBOSITY will be set to quiet.
NUMBER_OF_PROCESSORS=1|2|...By default the number of processors is auto detected and parallelism is set
based on the number of processors. Set NUMBER_OF_PROCESSORS explicitly to
desired number to override the default.
N_PARALLEL_BUILD=NUMBER_OF_PROCESSORS
controls the
--parallel option of cmake --build .
N_PARALLEL_TEST=NUMBER_OF_PROCESSORS
controls the
--parallel option of ctest .
N_PARALLEL_UPDATE=NUMBER_OF_PROCESSORS
controls the
--jobs option of git submodule update .
TRACE=0|1By default scripts do not output trace information to reduce noise. Set
TRACE=1 explicitly to see trace output.
XTRACE=TRACE controls whether to
set -x
to enable Bash xtrace.C++ submodule manager projects adhere to conventions to make it simple to operate on projects and targets programmatically and also to make room for both independently developed projects and different versions of a single project to coexist.
In order to understand how these conventions translate into practice, it can be
helpful to play with an example. The cppsm init-hello
script is written for this purpose to generate a simple example project.
Every cppsm project must conform to the project structure, whose rules are
codified into functions defined in the
conventional-project.cmake script and many of
the subcommands of the cppsm command.
A project contains a .cppsm subdirectory
(containing the boilerplate files)
and four optional directories as follows:
equipment directory
may contain any number of project submodules that the
project internally depends upon.internals directory
may contain any number of target directories that are
internal to the project.provides directory
may contain any number of target directories that are
provided for dependant projects.requires directory
may contain any number of project submodules that the
provided targets depend upon.A project submodule either
contains a project or just a CMakeLists.txt script. In the former
case the submodule is treated as a cppsm project whose targets under
provides will be added to the build and in the latter
case the submodule is treated as a foreign CMake project to be added to the
build.
A target directory simply
contains a CMakeLists.txt script that defines targets.
Note that in the common case when only a single target directory is needed under
internals or provides, there
is no need to create a nested directory for it and the CMakeLists.txt script
can go directly under the internals or
provides directory.
In a cppsm project one may optionally structure targets according to conventions
codified into functions defined in the
conventional-targets.cmake script. In that case
a target directory may simultaneously contain:
include/${LIBRARY_NAME}_${LIBRARY_VERSION}
and library directories.testing directory.program directory.C++ submodule manager projects and project submodules are versioned such that the branches of a project are named after their (major) version numbers and the version numbers are part of the paths of submodules containing C++ submodule manager projects.
The ${PROJECT_NAME} of a C++
submodule manager project is taken to be the last (/ separated) part of the
URL of the Git project sans the .git suffix.
The ${PROJECT_VERSION} of a
cppsm project is the name of a branch.
When added as a submodule dependency, a cppsm project whose name is
${PROJECT_NAME} and whose version is
${PROJECT_VERSION} goes either to
equipment/${PROJECT_NAME}/${PROJECT_VERSION} or to
requires/${PROJECT_NAME}/${PROJECT_VERSION} depending
on the nature of the dependency.
In a cppsm project one may optionally version libraries such that their header
files start with a directory name of the form ${LIBRARY_NAME}_${VERSION},
which is also the name of the CMake library target
and of the namespace inside of which all the public code of the library resides
in.
Note that when combined with the project versioning conventions this allows a single project to potentially use multiple (major or incompatible) versions of a single project or library. This can be very important in large projects composed of separately develop subprojects or libraries.
Note that often LIBRARY_NAME may be the same as
PROJECT_NAME and LIBRARY_VERSION may be the same as
PROJECT_VERSION, but this is not required.