A reusable template for building a C++ commandline tool (using HTSlib) with a Python CLI wrapper, complete with GitHub Actions for CI, Docker release, and Conda release. This template lets you jump straight into implementing a C++ tool without setting up all the build, test, container, and packaging boilerplate code from scratch.
Template Repo Combines:
- A C++ executable (
aligncount_cpp- compiled with CMake) - A Python wrapper (
aligncount- installed via pip) - Built-in unit tests (C++ and Python)
- A multi-stage Dockerfile (builds, tests, and packages a wheel)
- A Conda recipe (creates a cross-platform packages for Linux/macOS)
- GitHub Actions for CI, Docker/Conda release, styling, and documentation
- Use this repo as a template.
- Search for and replace every case‑sensitive instance of the following strings throughout the repository:
- Aligncount -> Mytool
- aligncount -> mytool
- ALIGNCOUNT_VERSION -> MYTOOL_VERSION
- Rename the following files and directories:
- cpp/include/aligncount/aligncount.h -> cpp/include/mytool/mytool.h
- cpp/source/aligncount.cpp -> cpp/source/mytool.cpp
- cpp/test/source/aligncount.cpp -> cpp/test/source/mytool.cpp
- Test your changes by installing, building the docker image, and running unit tests (see below)
- Replace the source files with your own
- Set GitHub action secrets for:
- ANACONDA_TOKEN
- ANACONDA_USER
- DOCKERHUB_TOKEN
- DOCKERHUB_USERNAME
- CODECOV_TOKEN (if making code coverage reports for the C++ tool)
- Source: cli/entrypoint.py
- Entry point: aligncount console script (configured in setup.py)
- Functionality:
- Parses subcommands (count-mapped or count-unmapped)
- Checks that the input SAM file exists and is non-empty
- Calls
aligncount_cpp -a <path>and writes to count stdout
- Source: cpp/standalone/source/main.cpp
- Statically links HTSlib
- Build system: CMake (CMakeLists.txt)
- Functionality: Counts the number of SAM records
To install in a virtualenv while at repo root:
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip setuptools scikit-build
pip install .
Now you have:
- aligncount_cpp (C++ binary, in venv/bin)
- aligncount (Python wrapper, in venv/bin)
Run the wrapper (counts the number of SAM records in a file):
aligncount count-mapped -a sample.sam
aligncount count-unmapped -a sample.sam
Note, the installation command implicitly builds the C++ tool via scikit-build/cmake. If you want to install the Python Wrapper by itself do (does not install aligncount_cpp):
python3 -m venv venv
pip install --upgrade pip setuptools
cd cli
pip install .
To build manually while at repo root:
mkdir build
cd build
cmake ../cpp/standalone -DCMAKE_BUILD_TYPE=Release
cmake --build .
Now you have aligncount_cpp available in the build folder. Run the tool to counts SAM records:
./aligncount_cpp -a sample.sam
A multi-stage Dockerfile builds and tests everything, then produces a minimal runtime image:
- Builder stage:
- Installs build-time dependencies (CMake, Git, Python dev headers, pip)
- Compiles C++ (aligncount_cpp) and runs CTest
- Builds a Python wheel (bundles aligncount_cpp + aligncount)
- Final stage:
- Installs runtime dependencies (Python, pip)
- Installs the wheel (placing aligncount_cpp and aligncount into /usr/local/bin)
- Runs the Python wrapper unit tests
- Sets ENTRYPOINT ["aligncount"]
Build and test in one command:
docker build -t aligncount:latest .
Run:
docker run --rm aligncount:latest --help
To run the C++ tool tests at repo root do:
cmake -S cpp/test -B build/test
cmake --build build/test
CTEST_OUTPUT_ON_FAILURE=1 cmake --build build/test --target test
To run the python wrapper tests at repo root do:
export PYTHONPATH="$PWD/cli:${PYTHONPATH}"
pytest tests
The export command is required if the wrapper is not installed.
.github/workflows/conda_release.ymlCreates and publishes a conda package for various platforms..github/workflows/release_release.ymlCompiles and publishes the Dockerfile to docker hub..github/workflows/cpp_documentation.ymlBuilds and publishes documentation for the C++ tool.
.github/workflows/cpp_wrapper_ci.ymlBuilds, installs, and tests the C++ tool and wrapper on ubuntu..github/workflows/cpp_install.ymlBuilds, installs, and tests the C++ tool on ubuntu..github/workflows/cpp_macos.ymlBuilds and tests the C++ tool on MacOS..github/workflows/cpp_standalone.ymlBuilds the C++ tool on ubuntu..github/workflows/cpp_ubuntu.ymlTests and creates codecov reports for the C++ tool on ubuntu..github/workflows/cpp_style.ymlChecks C++ and CMake source style.
The conda-recipe/meta.yaml contains instructions for the conda-build command to create the conda package that
contains the C++ tool and the python wrapper. The recipe expects certain environment variables to be set, which
are set by the GitHub action conda_release.yml. The expected environment variables are:
REPO_NAME # Name of the repo, e.g. cpp-python-tool-template
VERSION # Tool version, e.g. 0.0.1
TAR_URL # URL to the repo archive, e.g. https://github.com/user/foo/archive/refs/tags/v0.0.1.tar.gz
SHA256 # SHA256 checksum of the repo archive
REPO_HOME # URL to the repo home, e.g. https://github.com/user/foo
- The development files for the C++ executable (under
cpp/) were adapted from ModernCppStarter after adding HTSlib support in a slim fork htslib-cpp-starter. The contents of htslib-cpp-starter were added to this repo undercpp/viagit subtreeand further adapted. - The implementation pattern for HTSlib support was adapted from Yang Li’s blog post: Building Cpp Development in Bioinformatics
The conda release workflow publishes to your personal anacanda account. The workflow uses the repository name to name
the conda package, which would equate to cpp-python-tool-template for this repo. Additionally, the workflow
creates a conda package for the following platforms: linux-64, linux-aarch64, osx-64, and osx-arm64.
- See github issues for this repo
With this template, you have a fully tested building, packaging, and distribution pipeline out of the box. Happy coding!