Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor CMake to use best practices #321

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 149 additions & 137 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,103 +23,128 @@ string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
# Add the project
project(CLI11 LANGUAGES CXX VERSION ${VERSION_STRING})

# Special target that adds warnings. Is not exported.
add_library(CLI11_warnings INTERFACE)
include(CMakeDependentOption)
include(GNUInstallDirs)
include(CTest)

# Only if built as the main project
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
# User settable
set(CLI11_CXX_STD "11" CACHE STRING "The CMake standard to require")

# Special override for Clang on Linux (useful with an old stdlibc++ and a newer clang)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
option(CLI11_FORCE_LIBCXX "Force Clang to use libc++ instead of libstdc++ (Linux only)" OFF)
if(CLI11_FORCE_LIBCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
endif()
endif()

set(CUR_PROJ ON)
set(CMAKE_CXX_STANDARD ${CLI11_CXX_STD})
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")

# Be moderately paranoid with flags
if(MSVC)
target_compile_options(CLI11_warnings INTERFACE "/W4")
if(CLI11_WARNINGS_AS_ERRORS)
target_compile_options(CLI11_warnings INTERFACE "/WX")
endif()
else()
target_compile_options(CLI11_warnings INTERFACE -Wall -Wextra -pedantic -Wshadow)
if(CLI11_WARNINGS_AS_ERRORS)
target_compile_options(CLI11_warnings INTERFACE -Werror)
endif()
endif()

if(NOT CMAKE_VERSION VERSION_LESS 3.6)
# Add clang-tidy if available
option(CLANG_TIDY_FIX "Perform fixes for Clang-Tidy" OFF)
find_program(
CLANG_TIDY_EXE
NAMES "clang-tidy"
DOC "Path to clang-tidy executable"
)

if(CLANG_TIDY_EXE)
if(CLANG_TIDY_FIX)
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix")
else()
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}")
endif()
endif()
endif()

if(NOT CMAKE_VERSION VERSION_LESS 3.9)
find_package(Doxygen)
if(Doxygen_FOUND)
add_subdirectory(docs)
else()
message(STATUS "Doxygen not found, not building docs")
endif()
else()
message(STATUS "Newer CMake adds Doxygen support, update CMake for docs")
endif()
find_program(CLI11_CLANG_TIDY_EXE NAMES clang-tidy DOC "Path to clang-tidy")
find_package(Doxygen)

list(APPEND force-libcxx "CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"")
list(APPEND force-libcxx "CMAKE_SYSTEM_NAME STREQUAL \"Linux\"")
list(APPEND force-libcxx "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")

list(APPEND clang-tidy-fix "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
list(APPEND clang-tidy-fix "CMAKE_VERSION VERSION_GREATER 3.5")
list(APPEND clang-tidy-fix "CLI11_CLANG_TIDY_EXE")
list(APPEND clang-tidy-fix "CLI11_BUILD_EXAMPLES")

list(APPEND build-docs "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
list(APPEND build-docs "CMAKE_VERSION VERSION_GREATER 3.10")
list(APPEND build-docs "Doxygen_FOUND")

option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
option(CLI11_SINGLE_FILE "Generate a single header file")

cmake_dependent_option(CLI11_BUILD_DOCS
"Build CLI11 documentation" ON
"${build-docs}" OFF)

cmake_dependent_option(CLI11_BUILD_TESTS
"Build CLI11 tests" ON
"BUILD_TESTING;CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)

cmake_dependent_option(CLI11_BUILD_EXAMPLES
"Build CLI11 examples" ON
"CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)

cmake_dependent_option(CLI11_BUILD_EXAMPLE_JSON
"Build CLI11 json example" OFF
"CLI11_BUILD_EXAMPLES" OFF)

cmake_dependent_option(CLI11_SINGLE_FILE_TESTS
"Duplicate all the tests for a single file build" OFF
"BUILD_TESTING;CLI11_SINGLE_FILE" OFF)

cmake_dependent_option(CLI11_INSTALL
"Install the CLI11 folder to include during install process" ON
"CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)

cmake_dependent_option(CLI11_CLANG_TIDY_FIX
"Perform fixes for clang-tidy" OFF
"${clang-tidy-fix}" OFF)

cmake_dependent_option(CLI11_FORCE_LIBCXX
"Force clang to use libc++ instead of libstdc++ (Linux only)" OFF
"${force-libcxx}" OFF)

if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()

if (NOT DEFINED CMAKE_CXX_EXTENSIONS)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()

if (NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
else()
set(CUR_PROJ OFF)
get_property(cli11-use-folders GLOBAL PROPERTY USE_FOLDERS)
Copy link
Collaborator

@henryiii henryiii Oct 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit: that's not what this is doing. Why is this reading then writing the same property?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this might have been a small check on my part and I forgot to remove the whole else() block 😅

set_property(GLOBAL PROPERTY USE_FOLDERS ${cli11-use-folders})
endif()

# Allow dependent options
include(CMakeDependentOption)
if (CMAKE_VERSION VERSION_LESS 3.9)
message(STATUS "CMake 3.10 and later adds Doxygen support. Update CMake to build documentation")
endif()

# Allow IDE's to group targets into folders
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if (NOT Doxygen_FOUND)
message(STATUS "Doxygen not found, building docs has been disabled")
endif()

file(GLOB CLI11_headers "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/*")
# To see in IDE, must be listed for target
# Special target that adds warnings. Is not exported.
add_library(CLI11_warnings INTERFACE)

target_compile_options(CLI11_warnings
INTERFACE
$<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>
$<$<CXX_COMPILER_ID:MSVC>:/W4 $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:/WX>>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic -Wshadow $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:-Werror>>)
henryiii marked this conversation as resolved.
Show resolved Hide resolved
if (CMAKE_VERSION VERSION_GREATER 3.13)
target_link_options(CLI11_warnings
INTERFACE
$<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
endif()
if(CLI11_FORCE_LIBCXX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
endif()

# Allow IDE's to group targets into folders
add_library(CLI11 INTERFACE)
add_library(CLI11::CLI11 ALIAS CLI11) # for add_subdirectory calls

# Duplicated because CMake adds the current source dir if you don't.
target_include_directories(CLI11 INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
set(header-patterns "${PROJECT_SOURCE_DIR}/include/CLI/*")
if (CMAKE_VERSION VERSION_GREATER 3.11)
list(INSERT header-patterns 0 CONFIGURE_DEPENDS)
endif()

# Make add_subdirectory work like find_package
add_library(CLI11::CLI11 ALIAS CLI11)
file(GLOB CLI11_headers ${header-patterns})
# To see in IDE, must be listed for target

option(CLI11_INSTALL "Install the CLI11 folder to include during install process" ${CUR_PROJ})
# Duplicated because CMake adds the current source dir if you don't.
target_include_directories(CLI11 INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

# This folder should be installed
if(CLI11_INSTALL)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/CLI DESTINATION include)

# Make an export target
install(TARGETS CLI11
EXPORT CLI11Targets)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# Make an export target
install(TARGETS CLI11 EXPORT CLI11Targets)
endif()

# Use find_package on the installed package
Expand All @@ -128,13 +153,13 @@ endif()
# import Targets.cmake

# Add the version in a CMake readable way
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CLI11ConfigVersion.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/CLI11ConfigVersion.cmake" @ONLY)
configure_file("cmake/CLI11ConfigVersion.cmake.in"
"CLI11ConfigVersion.cmake" @ONLY)

# These installs only make sense for a local project
if(CUR_PROJ)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
# Make version available in the install
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/CLI11ConfigVersion.cmake"
install(FILES "${PROJECT_BINARY_DIR}/CLI11ConfigVersion.cmake"
DESTINATION lib/cmake/CLI11)

# Install the export target as a file
Expand All @@ -152,67 +177,54 @@ if(CUR_PROJ)
export(PACKAGE CLI11)
endif()

option(CLI11_SINGLE_FILE "Generate a single header file" OFF)

if(CLI11_SINGLE_FILE)
# Single file test
if(CMAKE_VERSION VERSION_LESS 3.12)
find_package(PythonInterp REQUIRED)
set(Python_VERSION ${PYTHON_VERSION_STRING})
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
else()
find_package(Python REQUIRED)
endif()

file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
COMMAND "${Python_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI11_headers}
)
add_custom_target(generate_cli_single_file ALL
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
set_target_properties(generate_cli_single_file
PROPERTIES FOLDER "Scripts")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp DESTINATION include)
add_library(CLI11_SINGLE INTERFACE)
target_link_libraries(CLI11_SINGLE INTERFACE CLI11)
add_dependencies(CLI11_SINGLE generate_cli_single_file)
target_compile_definitions(CLI11_SINGLE INTERFACE -DCLI11_SINGLE_FILE)
target_include_directories(CLI11_SINGLE INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include/")
if(CMAKE_VERSION VERSION_LESS 3.12)
find_package(PythonInterp REQUIRED)
add_executable(Python::Interpreter IMPORTED)
set_target_properties(Python::Interpreter
PROPERTIES
IMPORTED_LOCATION "${PYTHON_EXECUTABLE}"
VERSION "${PYTHON_VERSION_STRING}")
else()
find_package(Python COMPONENTS Interpreter REQUIRED)
endif()

file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
COMMAND Python::Interpreter
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py"
"${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp"
${CLI11_headers})
add_custom_target(CLI11-generate-single-file ALL
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
set_property(TARGET CLI11-generate-single-file PROPERTY FOLDER "Scripts")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp
DESTINATION include)
add_library(CLI11_SINGLE INTERFACE)
target_link_libraries(CLI11_SINGLE INTERFACE CLI11)
add_dependencies(CLI11_SINGLE CLI11-generate-single-file)
target_compile_definitions(CLI11_SINGLE INTERFACE -DCLI11_SINGLE_FILE)
target_include_directories(CLI11_SINGLE INTERFACE
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/>
$<INSTALL_INTERFACE:include>)
endif()

cmake_dependent_option(CLI11_SINGLE_FILE_TESTS
"Duplicate all the tests for a single file build"
OFF
"CLI11_SINGLE_FILE"
OFF)


if(DEFINED CLI11_TESTING)
set(CLI11_TESTING_INTERNAL "${CLI11_TESTING}")
elseif(CUR_PROJ)
option(BUILD_TESTING "Build the tests" ON)
set(CLI11_TESTING_INTERNAL "${BUILD_TESTING}")
else()
set(CLI11_TESTING_INTERNAL OFF)
endif()

if(CLI11_TESTING_INTERNAL)
enable_testing()
add_subdirectory(tests)
if(CLI11_BUILD_TESTS)
add_subdirectory(tests)
endif()

cmake_dependent_option(CLI11_EXAMPLES "Build the examples" ON "CUR_PROJ" OFF)
if(CLI11_EXAMPLES)
add_subdirectory(examples)
if(CLI11_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

# Packaging support
set(CPACK_PACKAGE_VENDOR "github.com/CLIUtils/CLI11")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Command line interface")
set(CPACK_PACKAGE_VERSION_MAJOR ${CLI11_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${CLI11_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${CLI11_VERSION_PATCH})
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Command line parser with simple and intuitive interface")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
Expand Down
6 changes: 5 additions & 1 deletion cmake/AddGoogletest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF)

add_subdirectory("${CLI11_SOURCE_DIR}/extern/googletest" "${CLI11_BINARY_DIR}/extern/googletest" EXCLUDE_FROM_ALL)
add_subdirectory("${googletest_SOURCE_DIR}" "${googletest_BINARY_DIR}" EXCLUDE_FROM_ALL)


if(GOOGLE_TEST_INDIVIDUAL)
Expand Down Expand Up @@ -37,6 +37,10 @@ macro(add_gtest TESTNAME)
else()
add_test(${TESTNAME} ${TESTNAME})
set_target_properties(${TESTNAME} PROPERTIES FOLDER "Tests")
if (CLI11_FORCE_LIBCXX)
set_property(TARGET ${T} APPEND_STRING
PROPERTY LINK_FLAGS -stdlib=libc++)
endif()
endif()

endmacro()
Expand Down
Loading