diff --git a/.gitignore b/.gitignore index 6ec82c5..f37d11b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ test/** !test/test.cpp !test/example.cpp +!test/CMakeLists.txt build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 72ef501..4bbdba7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,132 @@ -cmake_minimum_required( VERSION 3.2.2 ) -project( mio ) +cmake_minimum_required(VERSION 3.8) -### Standard -set( CMAKE_CXX_STANDARD 11 ) +# +# Here we check whether mio is being configured in isolation or as a component +# of a larger project. To do so, we query whether the `PROJECT_NAME` CMake +# variable has been defined. In the case it has, we can conclude mio is a +# subproject. +# +# This convention has been borrowed from the Catch C++ unit testing library. +# +if(DEFINED PROJECT_NAME) + set(subproject ON) +else() + set(subproject OFF) +endif() + +project(mio VERSION 1.0.0 LANGUAGES C CXX) +include (CTest) +include (CMakeDependentOption) # Generate 'compile_commands.json' for clang_complete -set( CMAKE_EXPORT_COMPILE_COMMANDS ON ) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -### Flags/Options -option( BUILD_TESTS "Enable the building of mio unit tests" OFF ) +# +# The `mio.testing` options only appear as cmake-gui and ccmake options iff +# mio is the highest level project. In the case that mio is a subproject, these +# options are hidden from the user interface and set to `OFF` +# +# Iff mio is the highest level project, this option is defaulted to the value +# of the traditional course grain testing option `BUILD_TESTING` established by +# the CTest module +# +CMAKE_DEPENDENT_OPTION(mio.tests + "Build the mio tests and integrate with ctest" + ${BUILD_TESTING} "NOT subproject" OFF) -### Library targets -add_library( mio INTERFACE) -target_include_directories( mio INTERFACE include ) -install( - DIRECTORY include/ - DESTINATION include -) +# +# mio has no compiled components. As such, we declare it as an `INTERFACE` +# library, which denotes a collection of target properties to be applied +# transitively to linking targets. In our case, this amounts to an include +# directory and project header files. +# +add_library(mio INTERFACE) +add_library(mio::mio ALIAS mio) -### Test targets -if( BUILD_TESTS ) - ## test - add_executable( - test - test/test.cpp - ) - target_link_libraries( test PRIVATE mio ) - - ## example - add_executable( - example - test/example.cpp - ) - target_link_libraries( example PRIVATE mio ) +# +# mio requires C++ 11 support, at a minimum. Setting the `cxx_std_11` compile +# features ensures that the corresponding C++ standard flag is populated in +# targets linking to mio +# +target_compile_features(mio INTERFACE cxx_std_11) + +# +# The include directory for mio can be expected to vary between build +# and installaion. Here we use a CMake generator expression to dispatch +# on how the configuration under which this library is being consumed. +# +target_include_directories(mio INTERFACE + $ + $) + +add_subdirectory(include/mio) + +if(mio.tests) + add_subdirectory(test) endif() + +# +# Non-testing header files (preserving relative paths) are installed to the +# `include` subdirectory of the `$INSTALL_DIR/${CMAKE_INSTALL_PREFIX}` +# directory. Source file permissions preserved. +# +install(DIRECTORY include/ + DESTINATION include + USE_SOURCE_PERMISSIONS + FILES_MATCHING PATTERN "*.*pp") + +# +# As a header-only library, there are no target components to be installed +# directly (the PUBLIC_HEADER property is not white listed for INTERFACE +# targets for some reason). +# +# However, it is worthwhile export our target description in order to later +# generate a CMake configuration file for consumption by CMake's `find_package` +# intrinsic +# +install(TARGETS mio EXPORT mioConfig) +install(EXPORT mioConfig + FILE mioConfig.cmake + NAMESPACE mio:: + DESTINATION share/cmake/mio + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +include(CMakePackageConfigHelpers) # provides `write_basic_package_version_file` +write_basic_package_version_file("mioConfigVersion.cmake" + VERSION ${mio_VERSION} + COMPATIBILITY SameMajorVersion) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mioConfigVersion.cmake" + DESTINATION share/cmake/mio + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +# +# Rudimentary CPack support. +# +# CPack provides a mechanism to generate installation packaging for a project, +# e.g., self-extracting shell scripts, compressed tarballs, Debian Package files, +# RPM Package Manager files, Windows NSIS installation wizards, +# Apple Disk Images (.dmg), etc. +# +# Any system libraries required (runtimes, threading, etc) should be bundled +# with the project for this type of installation. The +# `InstallRequiredSystemLibraries` CMake module attempts to provide this +# functionality in an automated way. Additional libraries may be specified as +# +# ```cmake +# list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ) +# ``` +# +# A packaged installation can be generated by calling +# +# ```sh +# cpack -G --config CPackConfig.cmake +# ``` +# +# See `cpack --help` or the CPack documentation for more information. +# +include( InstallRequiredSystemLibraries ) +set( CPACK_PACKAGE_VENDOR "mandreyel" ) +set( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" ) +set( CMAKE_PROJECT_HOMEPAGE_URL "https://github.com/mandreyel/mio" ) +include( CPack ) diff --git a/README.md b/README.md index 3cc34e0..3089960 100644 --- a/README.md +++ b/README.md @@ -156,19 +156,147 @@ using mmap_sink = mio::basic_mmap_sink; Though generally not needed, since mio maps users requested offsets to page boundaries, you can query the underlying system's page allocation granularity by invoking `mio::page_size()`, which is located in `mio/page.hpp`. -### Installation -mio is a header-only library, so just copy the contents in `mio/include` into your system wide include path, such as `/usr/include`, or into your project's lib folder. - ## CMake -A `CMakeLists.txt` is provided to allow easy git submodule usage or installation. +As a header-only library, mio has no compiled components. Nevertheless, a [CMake](https://cmake.org/overview/) build system is provided to allow easy testing, installation, and subproject composition on many platforms and operating systems. -To use as a submodule, clone mio within your project's dependencies/externals folder and add: +### Testing +Mio is distributed with a small suite of tests and examples. +When mio is configured as the highest level CMake project, this suite of executables is built by default. +Mio's test executables are integrated with the CMake test driver program, [CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html). + +CMake supports a number of backends for compilation and linking. + +To use a static configuration build tool, such as GNU Make or Ninja: + +```sh +cd +mkdir build +cd build + +# Configure the build +cmake -D CMAKE_BUILD_TYPE= \ + -G <"Unix Makefiles" | "Ninja"> .. + +# build the tests +< make | ninja | cmake --build . > + +# run the tests +< make test | ninja test | cmake --build . --target test | ctest > ``` -add_subdirectory( dependencies_folder/mio ) -target_link_libraries( MyCoolProject mio ) + +To use a dynamic configuration build tool, such as Visual Studio or Xcode: + +```sh +cd +mkdir build +cd build + +# Configure the build +cmake -G <"Visual Studio 14 2015 Win64" | "Xcode"> .. + +# build the tests +cmake --build . --config + +# run the tests via ctest... +ctest --build-config + +# ... or via CMake build tool mode... +cmake --build . --config --target test ``` -to your project's `CMakeLists.txt` to add mio into `MyCoolProject`'s include-space. -To install, do an out-of-source build(such as making a `build` folder and running `cmake ..` inside of it) and then run `sudo make install` to copy relevant include files to +Of course the **build** and **test** steps can also be executed via the **all** and **test** targets, respectively, from within the IDE after opening the project file generated during the configuration step. -The optional `BUILD_TESTS` option can be used to build unit tests(off by default) by instead using `cmake -DBUILD_TESTS=ON ..` +Mio's testing is also configured to operate as a client to the [CDash](https://www.cdash.org/) software quality dashboard application. Please see the [Kitware documentation](https://cmake.org/cmake/help/latest/manual/ctest.1.html#dashboard-client) for more information on this mode of operation. + +### Installation + +Mio's build system provides an installation target and support for downstream consumption via CMake's [`find_package`](https://cmake.org/cmake/help/v3.0/command/find_package.html) intrinsic function. +CMake allows installation to an arbitrary location, which may be specified by defining `CMAKE_INSTALL_PREFIX` at configure time. +In the absense of a user specification, CMake will install mio to conventional location based on the platform operating system. + +To use a static configuration build tool, such as GNU Make or Ninja: + +```sh +cd +mkdir build +cd build + +# Configure the build +cmake [-D CMAKE_INSTALL_PREFIX="path/to/installation"] \ + [-D BUILD_TESTING=False] \ + -D CMAKE_BUILD_TYPE=Release \ + -G <"Unix Makefiles" | "Ninja"> .. + +# install mio + +``` + +To use a dynamic configuration build tool, such as Visual Studio or Xcode: + +```sh +cd +mkdir build +cd build + +# Configure the project +cmake [-D CMAKE_INSTALL_PREFIX="path/to/installation"] \ + [-D BUILD_TESTING=False] \ + -G <"Visual Studio 14 2015 Win64" | "Xcode"> .. + +# install mio +cmake --build . --config Release --target install +``` + +Note that the last command of the installation sequence may require administrator privileges (e.g. `sudo`) if the installation root directory lies outside your home directory. +This installation ++ copies the mio header files to the `include/mio` subdirectory of the installation root ++ generates and copies several CMake configuration files to the `share/cmake/mio` subdirectory of the installation root + +This latter step allows downstream CMake projects to consume mio via `find_package`, e.g. + +```cmake +find_package( mio REQUIRED ) +target_link_libraries( MyTarget PUBLIC mio::mio ) +``` + +If mio was installed to a non-conventional location, it may be necessary for downstream projects to specify the mio installation root directory via either + ++ the `CMAKE_PREFIX_PATH` configuration option, ++ the `CMAKE_PREFIX_PATH` environment variable, or ++ `mio_DIR` environment variable. + +Please see the [Kitware documentation](https://cmake.org/cmake/help/v3.0/command/find_package.html) for more information. + +In addition, mio supports packaged relocatable installations via [CPack](https://cmake.org/cmake/help/latest/manual/cpack.1.html). +Following configuration, from the build directory, invoke cpack as follows to generate a packaged installation: + +```sh +cpack -G -C Release +``` + +The list of supported generators varies from platform to platform. See the output of `cpack --help` for a complete list of supported generators on your platform. + +### Subproject Composition +To use mio as a subproject, copy the mio repository to your project's dependencies/externals folder. +If your project is version controlled using git, a git submodule or git subtree can be used to syncronize with the updstream repository. +The [use](https://services.github.com/on-demand/downloads/submodule-vs-subtree-cheat-sheet/) and [relative advantages](https://andrey.nering.com.br/2016/git-submodules-vs-subtrees/) of these git facilities is beyond the scope of this document, but in brief, each may be established as follows: + +```sh +# via git submodule +cd +git submodule add -b master https://github.com/mandreyel/mio.git + +# via git subtree +cd +git subtree add --prefix /mio \ + https://github.com/mandreyel/mio.git master --squash +``` + +Given a mio subdirectory in a project, simply add the following lines to your project's to add mio include directories to your target's include path. + +```cmake +add_subdirectory( path/to/mio/ ) +target_link_libraries( MyTarget PUBLIC ) +``` + +Note that, as a subproject, mio's tests and examples will not be built and CPack integration is deferred to the host project. diff --git a/include/mio/CMakeLists.txt b/include/mio/CMakeLists.txt new file mode 100644 index 0000000..57268b6 --- /dev/null +++ b/include/mio/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# While not strictly necessary to specify header files as target sources, +# doing so populates these files in the source listing when CMake is used +# to generate XCode and Visual Studios projects +# +target_sources(mio INTERFACE + $ + $) + +add_subdirectory(detail) diff --git a/include/mio/detail/CMakeLists.txt b/include/mio/detail/CMakeLists.txt new file mode 100644 index 0000000..5711e21 --- /dev/null +++ b/include/mio/detail/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# iff mio is the highest level project, include the implementation +# detail files in the source listing for CMake-generated IDE projects +# +if(NOT subproject) + target_sources(mio INTERFACE + $) +endif() diff --git a/include/mio/detail/basic_mmap.hpp b/include/mio/detail/basic_mmap.hpp index f5d3303..4582626 100644 --- a/include/mio/detail/basic_mmap.hpp +++ b/include/mio/detail/basic_mmap.hpp @@ -21,7 +21,7 @@ #ifndef MIO_BASIC_MMAP_HEADER #define MIO_BASIC_MMAP_HEADER -#include "../page.hpp" +#include "mio/page.hpp" #include #include @@ -159,6 +159,6 @@ bool operator>=(const basic_mmap& a, const basic_mmap& b); } // namespace detail } // namespace mio -#include "basic_mmap.ipp" +#include "mio/detail/basic_mmap.ipp" #endif // MIO_BASIC_MMAP_HEADER diff --git a/include/mio/detail/basic_mmap.ipp b/include/mio/detail/basic_mmap.ipp index 835e9f1..cfd5c50 100644 --- a/include/mio/detail/basic_mmap.ipp +++ b/include/mio/detail/basic_mmap.ipp @@ -21,9 +21,9 @@ #ifndef MIO_BASIC_MMAP_IMPL #define MIO_BASIC_MMAP_IMPL -#include "basic_mmap.hpp" -#include "string_util.hpp" -#include "../page.hpp" +#include "mio/detail/basic_mmap.hpp" +#include "mio/detail/string_util.hpp" +#include "mio/page.hpp" #include #include diff --git a/include/mio/shared_mmap.hpp b/include/mio/shared_mmap.hpp index 015f7d5..2d58a30 100644 --- a/include/mio/shared_mmap.hpp +++ b/include/mio/shared_mmap.hpp @@ -21,7 +21,7 @@ #ifndef MIO_SHARED_MMAP_HEADER #define MIO_SHARED_MMAP_HEADER -#include "mmap.hpp" +#include "mio/mmap.hpp" #include // std::error_code #include // std::shared_ptr diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..6985390 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(mio.test test.cpp) +target_link_libraries(mio.test PRIVATE mio::mio) +add_test(NAME mio.test COMMAND mio.test) + +add_executable(mio.example example.cpp) +target_link_libraries(mio.example PRIVATE mio::mio) +add_test(NAME mio.example COMMAND mio.example)