diff --git a/.gitignore b/.gitignore index 0def275..2527ffe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ +# builds +build*/* + +# kdevelop +*.kdev4 + # Compiled Object files *.slo *.lo diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7d5e2a4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.6) +project(Foo) + +# Set variables +include(${CMAKE_SOURCE_DIR}/cmake/SetEnv.cmake) + +# Library sources +add_subdirectory(${LIBRARY_FOLDER}) + +# Library examples +add_subdirectory(examples) + +# Install targets +include(${CMAKE_SOURCE_DIR}/cmake/InstallConfig.cmake) diff --git a/README.md b/README.md index 52fc4a3..d6f9e6e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,84 @@ -foo-cmake-lib -============= +cmake-example-library +===================== -How to install a library with cmake +This is an example of how to install a library with CMake, and then use +`find_package()` command to find it. + +The **advantage** of this example is that it is auto generated. It is only +needed to change the *project name*. + +It is based on the these two examples: + * [How to create a ProjectConfig.cmake file (cmake.org)] + (http://www.cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file) + * [How to install a library (kde.org)] + (https://projects.kde.org/projects/kde/kdeexamples/repository/revisions/master/show/buildsystem/HowToInstallALibrary) + +### How to create a library from this example? + +Follow these steps: + + * Copy files in a new folder. + + * Change project name in the top-level `CMakeLists.txt`. + + * (Optionally) Set the variables: `LIBRARY_NAME` and `LIBRARY_FOLDER`. + Otherwise project name in lowercase will be used. + + See `cmake/SetEnv.cmake` file to see the difference. + +### How to compile? + +Assume the following settings: + + project(Foo) + ... + set(LIBRARY_NAME foo) # generated automatically (in lowercase) + set(LIBRARY_FOLDER foo) # generated automatically (in lowercase) + +Example of a local installation: + + > mkdir build + > cd build + > cmake -DCMAKE_INSTALL_PREFIX=../installed .. + > make install + +Installed files: + + > tree ../installed + + ├── bin + │   └── bar + ├── include + │   └── foo + │   ├── foo.h + │   └── version.h + └── lib + ├── CMake + │   └── Foo + │   ├── FooConfig.cmake + │   ├── FooConfigVersion.cmake + │   ├── FooTargets.cmake + │   └── FooTargets-noconfig.cmake + └── libfoo.so + +Unintall library: + + > make uninstall + +### How to use it (internally)? + +See the [bar example](examples/). + +### How to use it (in an external project)? + +Once the library is installed, CMake will be able to find it using +`find_package()` command. For example: + + cmake_minimum_required(VERSION 2.6) + project(Bar) + + find_package(Foo REQUIRED) + include_directories(${FOO_INCLUDE_DIRS}) + + add_executable(bar bar.cpp) + target_link_libraries(bar ${FOO_LIBRARIES}) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in new file mode 100644 index 0000000..cf41347 --- /dev/null +++ b/cmake/Config.cmake.in @@ -0,0 +1,13 @@ +# - Config file for '@PROJECT_NAME@' package +# It defines the following variables +# @PROJECT_NAME_UPPERCASE@_INCLUDE_DIRS - include directories +# @PROJECT_NAME_UPPERCASE@_LIBRARIES - libraries to link against + +# Include directory +set(@PROJECT_NAME_UPPERCASE@_INCLUDE_DIRS "@INSTALL_INCLUDE_DIR@") + +# Import the exported targets +include("@INSTALL_CMAKE_DIR@/@PROJECT_NAME@Targets.cmake") + +# Set the expected library variable +set(@PROJECT_NAME_UPPERCASE@_LIBRARIES @LIBRARY_NAME@) diff --git a/cmake/ConfigVersion.cmake.in b/cmake/ConfigVersion.cmake.in new file mode 100644 index 0000000..a9cb42a --- /dev/null +++ b/cmake/ConfigVersion.cmake.in @@ -0,0 +1,22 @@ +# FIND_PACKAGE() searches for a Config.cmake file and an associated +# Version.cmake file, which it loads to check the version number, +# when a version has been specified in the find_package() call, +# e.g. find_package(Foo 1.0.0). +# +# This file can be used with configure_file() to generate such a file for a +# project with very basic logic. +# +# It sets PACKAGE_VERSION_EXACT if the current version string and the requested +# version string are exactly the same and it sets PACKAGE_VERSION_COMPATIBLE +# if the current version is >= requested version. + +set(PACKAGE_VERSION @PROJECT_VERSION@) + +IF("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if("${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/cmake/InstallConfig.cmake b/cmake/InstallConfig.cmake new file mode 100644 index 0000000..33f6c55 --- /dev/null +++ b/cmake/InstallConfig.cmake @@ -0,0 +1,25 @@ +# This "exports" all targets which have been put into the export set +install(EXPORT ${PROJECT_EXPORT} + DESTINATION ${INSTALL_CMAKE_DIR} + FILE ${PROJECT_NAME}Targets.cmake) + +# Create the Config.cmake.in +configure_file(${CMAKE_SOURCE_DIR}/cmake/Config.cmake.in + "${PROJECT_CMAKE_FILES}/${PROJECT_NAME}Config.cmake" @ONLY) + +# Create the ConfigVersion.cmake.in +configure_file(${CMAKE_SOURCE_DIR}/cmake/ConfigVersion.cmake.in + "${PROJECT_CMAKE_FILES}/${PROJECT_NAME}ConfigVersion.cmake" @ONLY) + +# Install Config.cmake and ConfigVersion.cmake files +install(FILES + "${PROJECT_CMAKE_FILES}/${PROJECT_NAME}Config.cmake" + "${PROJECT_CMAKE_FILES}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) + +# Uninstall targets +configure_file("${CMAKE_SOURCE_DIR}/cmake/Uninstall.cmake.in" + "${PROJECT_CMAKE_FILES}/Uninstall.cmake" + IMMEDIATE @ONLY) +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${PROJECT_CMAKE_FILES}/Uninstall.cmake) diff --git a/cmake/LibraryConfig.cmake b/cmake/LibraryConfig.cmake new file mode 100644 index 0000000..8ab576e --- /dev/null +++ b/cmake/LibraryConfig.cmake @@ -0,0 +1,27 @@ +# Select library type +option(BUILD_SHARED_LIBS "Build @PROJECT_NAME@ as a shared library." ON) +if(BUILD_SHARED_LIBS) + set(LIBRARY_TYPE SHARED) +else() + set(LIBRARY_TYPE STATIC) +endif() + +# Target +add_library(${LIBRARY_NAME} ${LIBRARY_TYPE} ${SOURCES} ${HEADERS}) + +# Install library +install(TARGETS ${LIBRARY_NAME} + EXPORT ${PROJECT_EXPORT} + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT stlib + COMPONENT dev) + +# Create 'version.h' +configure_file(version.h.in + "${CMAKE_CURRENT_BINARY_DIR}/version.h" @ONLY) +set(HEADERS ${HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/version.h) + +# Install headers +install(FILES ${HEADERS} + DESTINATION "${INSTALL_INCLUDE_DIR}/${LIBRARY_FOLDER}" ) diff --git a/cmake/SetEnv.cmake b/cmake/SetEnv.cmake new file mode 100644 index 0000000..cac52f5 --- /dev/null +++ b/cmake/SetEnv.cmake @@ -0,0 +1,56 @@ +# Set PROJECT_NAME_UPPERCASE and PROJECT_NAME_LOWERCASE variables +string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPERCASE) +string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) + +# Version variables +set(MAJOR_VERSION 0) +set(MINOR_VERSION 1) +set(PATCH_VERSION 0) +set(PROJECT_VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}) + +# Offer the user the choice of overriding the installation directories +set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(INSTALL_INCLUDE_DIR include CACHE PATH + "Installation directory for header files") +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR CMake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/CMake/${PROJECT_NAME}) +endif() +set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH + "Installation directory for CMake files") + +# Make relative paths absolute (needed later on) +foreach(p LIB BIN INCLUDE CMAKE) + set(var INSTALL_${p}_DIR) + if(NOT IS_ABSOLUTE "${${var}}") + set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + endif() +endforeach() + +# Set up include-directories +include_directories( + "${PROJECT_SOURCE_DIR}" + "${PROJECT_BINARY_DIR}") + +# Library name (by default is the project name in lowercase) +# Example: libfoo.so +if(NOT LIBRARY_NAME) + set(LIBRARY_NAME ${PROJECT_NAME_LOWERCASE}) +endif() + +# Library folder name (by default is the project name in lowercase) +# Example: #include +if(NOT LIBRARY_FOLDER) + set(LIBRARY_FOLDER ${PROJECT_NAME_LOWERCASE}) +endif() + +# The export set for all the targets +set(PROJECT_EXPORT ${PROJECT_NAME}EXPORT) + +# Path of the CNake files generated +set(PROJECT_CMAKE_FILES ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}) + +# The RPATH to be used when installing +set(CMAKE_INSTALL_RPATH ${INSTALL_LIB_DIR}) diff --git a/cmake/Uninstall.cmake.in b/cmake/Uninstall.cmake.in new file mode 100644 index 0000000..d30b24c --- /dev/null +++ b/cmake/Uninstall.cmake.in @@ -0,0 +1,21 @@ +# Note: http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F + +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else() + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach(file) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..df6051c --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,15 @@ +# (uncomment these lines for an external project) +# +# cmake_minimum_required(VERSION 2.6) +# project(Bar) +# find_package(Foo REQUIRED) +# include_directories(${FOO_INCLUDE_DIRS}) + +add_executable(bar bar.cpp) + +target_link_libraries(bar ${LIBRARY_NAME}) + +install(TARGETS bar +# In order to export target, uncomment next line +# EXPORT ${PROJECT_EXPORT} + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) diff --git a/examples/bar.cpp b/examples/bar.cpp new file mode 100644 index 0000000..637b0ba --- /dev/null +++ b/examples/bar.cpp @@ -0,0 +1,6 @@ +#include "foo/foo.h" + +int main(int argc, char *argv[]) { + foo(); + return 0; +} diff --git a/foo/CMakeLists.txt b/foo/CMakeLists.txt new file mode 100644 index 0000000..1f9b359 --- /dev/null +++ b/foo/CMakeLists.txt @@ -0,0 +1,7 @@ +# Set SOURCES variable +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +# Set HEADERS variable +file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + +include(${CMAKE_SOURCE_DIR}/cmake/LibraryConfig.cmake) diff --git a/foo/foo.cpp b/foo/foo.cpp new file mode 100644 index 0000000..81e7ed0 --- /dev/null +++ b/foo/foo.cpp @@ -0,0 +1,6 @@ +#include "foo.h" +#include + +void foo(void) { + printf("This is foo version %s\n", FOO_VERSION); +} diff --git a/foo/foo.h b/foo/foo.h new file mode 100644 index 0000000..b7ebff4 --- /dev/null +++ b/foo/foo.h @@ -0,0 +1,8 @@ +#ifndef FOO_FOO_H +#define FOO_FOO_H + +#include + +void foo(void); + +#endif diff --git a/foo/version.h.in b/foo/version.h.in new file mode 100644 index 0000000..ff7f5ba --- /dev/null +++ b/foo/version.h.in @@ -0,0 +1,9 @@ +#ifndef @PROJECT_NAME_UPPERCASE@_VERSION_ +#define @PROJECT_NAME_UPPERCASE@_VERSION_ + +#define @PROJECT_NAME_UPPERCASE@_MAJOR_VERSION (@MAJOR_VERSION@) +#define @PROJECT_NAME_UPPERCASE@_MINOR_VERSION (@MINOR_VERSION@) +#define @PROJECT_NAME_UPPERCASE@_PATCH_VERSION (@PATCH_VERSION@) +#define @PROJECT_NAME_UPPERCASE@_VERSION "@PROJECT_VERSION@" + +#endif // @PROJECT_NAME_UPPERCASE@_VERSION_