A (Simplified) C++ Package Manager based on CMake and Git.
Forked from https://github.com/iauns/cpm
CPM is designed to promote small, well-tested, composable C++ modules.
SCPM is a relaxed CPM that doesn't manage versioning and namespaces, reduced to be simply a downloader of projects in a complex hierarchy of subprojects.
Basic usage is to add CPM to each non-leaf project that you have in your hierarchy and list its dependancies.
Table of Contents
- Brief Example
- Using CPM
- Building CPM Modules
- CPM Function Reference
- Miscellaneous Issues and Questions
Below is a sample of a CMakeLists.txt file that uses 3 modules. These modules are: platform specific OpenGL headers, an axis aligned bounding box implementation, and G-truc's GLSL vector math library. See the next section for a full explanation of how to use CPM. The following CMakeLists.txt is self-contained and will build as-is:
cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(foo)
#------------------------------------------------------------------------------
# Required CPM Setup - no need to modify - See: https://github.com/iauns/cpm
#------------------------------------------------------------------------------
set(CPM_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpm_packages" CACHE TYPE STRING)
find_package(Git)
if(NOT GIT_FOUND)
message(FATAL_ERROR "CPM requires Git.")
endif()
if (NOT EXISTS ${CPM_DIR}/CPM.cmake)
message(STATUS "Cloning repo (https://github.com/morpheby/scpm)")
execute_process(
COMMAND "${GIT_EXECUTABLE}" clone https://github.com/morpheby/scpm ${CPM_DIR}
RESULT_VARIABLE error_code
OUTPUT_QUIET ERROR_QUIET)
if(error_code)
message(FATAL_ERROR "CPM failed to get the hash for HEAD")
endif()
endif()
include(${CPM_DIR}/CPM.cmake)
#------------------------------------------------------------------------------
# CPM Modules
#------------------------------------------------------------------------------
# ++ MODULE: OpenGL platform
CPM_AddModule(
GIT_REPOSITORY "https://github.com/iauns/cpm-gl-platform"
GIT_TAG "1.3.5")
# ++ MODULE: aabb
CPM_AddModule(
GIT_REPOSITORY "https://github.com/iauns/cpm-glm-aabb"
GIT_TAG "1.0.3")
# ++ EXTERNAL-MODULE: GLM
CPM_AddModule(
GIT_REPOSITORY "https://github.com/iauns/cpm-glm"
GIT_TAG "1.0.2"
USE_EXISTING_VER TRUE)
# If the project is the root project:
CPM_Finish()
# OR
# If the project is not the root project:
CPM_InitModule(my-project-name)
#-----------------------------------------------------------------------
# Setup source
#-----------------------------------------------------------------------
# NOTE: Feel free to ignore this section. It simply creates a main.cpp source
# file from scratch instead of relying on the source being present. This is
# done only to keep this CMakeLists.txt self-contained.
file(WRITE src/main.cpp "#include <iostream>\n")
file(APPEND src/main.cpp "#include <glm-aabb/AABB.hpp>\n")
file(APPEND src/main.cpp "#include <glm/glm.hpp>\n\n")
file(APPEND src/main.cpp "namespace glm_aabb = CPM_AABB_NS;\n\n")
file(APPEND src/main.cpp "int main(int argc, char** av)\n")
file(APPEND src/main.cpp "{\n")
file(APPEND src/main.cpp " glm_aabb::AABB aabb(glm::vec3(-1.0), glm::vec3(1.0));\n")
file(APPEND src/main.cpp " aabb.extend(glm::vec3(-2.0, 3.0, -0.5));\n")
file(APPEND src/main.cpp " glm_aabb::AABB aabb2(glm::vec3(1.0), 1.0);\n")
file(APPEND src/main.cpp " std::cout << \"AABB Interesction: \" << aabb.intersect(aabb2) << std::endl;\n")
file(APPEND src/main.cpp " return 0;\n")
file(APPEND src/main.cpp "}\n")
set(Sources src/main.cpp)
#-----------------------------------------------------------------------
# Setup executable
#-----------------------------------------------------------------------
set(EXE_NAME cpm-test)
add_executable(${EXE_NAME} ${Sources})
target_link_libraries(${EXE_NAME} ${CPM_LIBRARIES})
To use CPM in your C++ project, include the following at the top of your CMakeLists.txt:
#------------------------------------------------------------------------------
# Required CPM Setup - no need to modify - See: https://github.com/iauns/cpm
#------------------------------------------------------------------------------
set(CPM_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpm-packages" CACHE TYPE STRING)
find_package(Git)
if(NOT GIT_FOUND)
message(FATAL_ERROR "CPM requires Git.")
endif()
if (NOT EXISTS ${CPM_DIR}/CPM.cmake)
message(STATUS "Cloning repo (https://github.com/morpheby/scpm)")
execute_process(
COMMAND "${GIT_EXECUTABLE}" clone https://github.com/morpheby/scpm ${CPM_DIR}
RESULT_VARIABLE error_code
OUTPUT_QUIET ERROR_QUIET)
if(error_code)
message(FATAL_ERROR "CPM failed to get the hash for HEAD")
endif()
endif()
include(${CPM_DIR}/CPM.cmake)
#------------------------------------------------------------------------------
# CPM Modules
#------------------------------------------------------------------------------
# TODO: Include any modules here...
CPM_Finish()
Then use the targets provided by your libraries' CMake files like that:
target_link_libraries(my_project anotherProject::anotherProject)
And you're done. You will be able to start using CPM modules right away by adding the following snippet to the CPM Modules section of your CMakeLists.txt:
CPM_AddModule("aabb"
GIT_REPOSITORY "https://github.com/iauns/cpm-glm-aabb"
GIT_TAG "1.0.2")
This snippet will automatically download, build, and add to CMake scope
version 1.0.2 of a simple axis aligned bounding box implementation named aabb
.
Be sure to place all calls to CPM_AddModule
before your call to
CPM_Finish
. The # TODO: Include any modules here...
section mentioned in the first snippet indicates where you should place calls
to CPM_AddModule
.
While it may be tempting to use the origin/master
tag to track the most
recent changes to a module, it is not recommended. Using version tags for a
module (such as 1.0.2
) and upgrading modules when necessary will save you
time in the long run. If you track origin/master
and upstream decides to
release a major version which includes significant API changes then your
builds will likely break immediately. But if versioned tags are used, you
will maintain your build integrity even through upstream version upgrades.
- Automatically manages code retrieval and building of CMake projects.
- Built entirely in CMake. Nothing else is required.
- Optionally cache downloaded modules and repositories to a central directory.
- Only supports git (with very limited support for SVN).
All CMake functions that SCPM exposes are listed below.
Adds a CPM module to your project. All arguments except <name>
are optional.
Additionally, one of either the GIT_REPOSITORY
or SOURCE_DIR
arguments
must be present in your call to CPM_AddModule
. Should be called before
either CPM_Finish
or CPM_InitModule
CPM_AddModule(
[GIT_REPOSITORY repo] # Git repository that corresponds to a CPM module.
# If this argument is not specfied, then SOURCE_DIR must be set.
[GIT_TAG tag] # Git tag to checkout. Tags, shas, and branches all work.
[SOURCE_DIR dir] # Uses 'dir' as the source directory instead of cloning
# from a repo. If this is not specified, then
# GIT_REPOSITORY must be specified.
[SOURCE_GHOST_GIT_REPO repo] # Ghost repository when using SOURCE_DIR.
# Used to correctly correlate SOURCE_DIR modules with their
# correct upstream repository.
[SOURCE_GHOST_GIT_TAG tag] # Ghost git tag when using SOURCE_DIR.
)
A utility function that allows you to download and ensure that some repository is up to date and present on the filesystem before proceeding forward with CMakeLists.txt processing. Useful for building CPM externals. You can use this function outside of any call to CPM_Finish or CPM_InitModule
CPM_EnsureRepoIsCurrent(
[TARGET_DIR dir] # Required - Directory in which to place repository.
[GIT_REPOSITORY repo] # Git repository to clone and keep up to date.
[GIT_TAG tag] # Git tag to checkout.
[SVN_REPOSITORY repo] # SVN repository to checkout.
[SVN_REVISION rev] # SVN revision.
[SVN_TRUST_CERT 1] # Trust the Subversion server site certificate
[USE_CACHING 1] # Enables caching of repositories if the user
# has specified CPM_MODULE_CACHING_DIR.
# Not enabled by default.
)
This function is for the top-level application only. Call when you are
finished issuing calls to CPM_AddModule
.
CPM_Finish()
This function is the module's counterpart to CPM_Finish
. Call this to
indicate to CPM that you have finished issuing calls to CPM_AddModule
.
The only argument indicates the name of the module. This name will only be
used for generating the preprocessor definition you should use for your
module.
CPM_InitModule("my_module")
Below are some common issues users encounter and solutions to them.
During the CMake configure step. No repository cloning or fetching occurs during the build step.
CPM supports cached repositories and modules by setting either the
CPM_MODULE_CACHE_DIR
CMake variable or the CPM_CACHE_DIR
environment
variable to an appropriate cache directory (such as ~/.cpm_cache
). When
set, a search will be performed in the cache directory for all modules that
don't already exist in your project's build directory. If the module is not
found in the cache directory, CPM will download the module into the cache
directory. This is useful if you find yourself with no or limited internet
access from time to time as your cache directory will be searched before
attempting to download the repository from the internet.
Here's a quick example of using this variable from the command line:
cmake -DCPM_MODULE_CACHE_DIR=~/.cpm_cache ...
The cache directory is searched only when a module is not found in your project's build directory. If the module is then found in the cache directory, the cache directory will be updated using the appropriate SCM and its directory contents will be copied into your project's build directory. Any subsequent invokation of CMake will find the module in your project's build directory and will not search in the cache directory. Unless you have cleaned the project or removed the build directory's modules.
Use the following code snippet instead of the one given above. This snippet checks to see if CPM exists in the cache directory before attempting to download it.
#------------------------------------------------------------------------------
# Required CPM Setup - See: http://github.com/iauns/cpm
#------------------------------------------------------------------------------
set(CPM_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpm-packages" CACHE TYPE STRING)
find_package(Git)
if(NOT GIT_FOUND)
message(FATAL_ERROR "CPM requires Git.")
endif()
if ((NOT DEFINED CPM_MODULE_CACHE_DIR) AND (NOT "$ENV{CPM_CACHE_DIR}" STREQUAL ""))
set(CPM_MODULE_CACHE_DIR "$ENV{CPM_CACHE_DIR}")
endif()
if ((NOT EXISTS ${CPM_DIR}/CPM.cmake) AND (DEFINED CPM_MODULE_CACHE_DIR))
if (EXISTS "${CPM_MODULE_CACHE_DIR}/github_morpheby_scpm")
message(STATUS "Found cached version of SCPM.")
file(COPY "${CPM_MODULE_CACHE_DIR}/github_morpheby_scpm/" DESTINATION ${CPM_DIR})
endif()
endif()
if (NOT EXISTS ${CPM_DIR}/CPM.cmake)
message(STATUS "Cloning repo (https://github.com/morpheby/scpm)")
execute_process(
COMMAND "${GIT_EXECUTABLE}" clone https://github.com/morpheby/scpm ${CPM_DIR}
RESULT_VARIABLE error_code
OUTPUT_QUIET ERROR_QUIET)
if(error_code)
message(FATAL_ERROR "CPM failed to get the hash for HEAD")
endif()
endif()
include(${CPM_DIR}/CPM.cmake)
# Modules go here...
CPM_Finish()
When building your project define: CPM_SHOW_HIERARCHY=TRUE
.
On the command line this would look something like
cmake -DCPM_SHOW_HIERARCHY=TRUE ...
It is best to run this command after you have successfully built your project so the output is not muddied by status messages.
If you get errors similar to:
The binary directory
/Users/jhughes/me/cpp/cpm/modules/ ... /bin
is already used to build a source directory. It cannot be used to build
source directory.
This means that there exists a circular module reference which is not allowed in CPM. The module graph must not contain cycles. For example, if Module A adds Module B, and Module B adds Module A, you will get this error.