Skip to content

Commit

Permalink
Merge branch 'externalproject' into 'master'
Browse files Browse the repository at this point in the history
Move to CMake controlled dependencies

See merge request idi/templates/cmake-cpp!17
  • Loading branch information
NouberNou committed Mar 13, 2024
2 parents df01970 + d0253b0 commit 16a32dc
Show file tree
Hide file tree
Showing 27 changed files with 593 additions and 111 deletions.
2 changes: 2 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ build-linux:
- if: $CI_PIPELINE_SOURCE != "merge_request_event" # Only on push events (same as 'except: merge_requests')
before_script:
- apt-get update && apt-get install -y -qq jq
- git config --global credential.helper store
- echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}" > ~/.git-credentials
script:
- cmake --version && $CC --version && $CXX --version
- mkdir -p build && cd build
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

26 changes: 18 additions & 8 deletions cmake/idi/functions/framework/idi_init.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

macro(idi_init)
include(CTest)
include(FetchContent)

idi_cmake_hook(pre-init)

Expand Down Expand Up @@ -119,24 +120,33 @@ macro(idi_init)

idi_new_component()

# Define a nice short hand for 3rd party external library folders
set(IDICMAKE_EXTERNAL_LIB_DIR "${CMAKE_CURRENT_LIST_DIR}/lib")
# Make sure we don't pollute a top level project that doesn't use our folder scheme for dependencies
if(NOT IDICMAKE_IS_SUBDIRECTORY)
set(IDICMAKE_TOP_LEVEL_CMAKE_DEPENDENCY_SUPPORT 1 CACHE BOOL "")
set(IDICMAKE_EXTERNAL_LIB_DIR "${CMAKE_CURRENT_LIST_DIR}/lib/first-party")
set(IDICMAKE_EXTERNAL_THIRD_PARTY_LIB_DIR "${CMAKE_CURRENT_LIST_DIR}/lib/third-party")
else()
if(NOT IDICMAKE_TOP_LEVEL_CMAKE_DEPENDENCY_SUPPORT)
message(STATUS "Top level project does not support the IDI CMake dependency structure, placing dependencies in local lib folder.")
set(IDICMAKE_EXTERNAL_LIB_DIR "${CMAKE_CURRENT_LIST_DIR}/lib/first-party")
set(IDICMAKE_EXTERNAL_THIRD_PARTY_LIB_DIR "${CMAKE_CURRENT_LIST_DIR}/lib/third-party")
else()
set(IDICMAKE_EXTERNAL_LIB_DIR "${CMAKE_SOURCE_DIR}/lib/first-party")
set(IDICMAKE_EXTERNAL_THIRD_PARTY_LIB_DIR "${CMAKE_SOURCE_DIR}/lib/third-party")
endif()
endif()

idi_add_third_party_dependency(Catch2 https://github.com/catchorg/Catch2.git v3.5.2)

# Add the main source folder.
idi_cmake_hook(pre-source)
add_subdirectory("src")
idi_cmake_hook(post-source)

# Catch is included by default as a submodule
if(NOT TARGET Catch2)
add_subdirectory(lib/Catch2)
endif()

if(IDICMAKE_BUILD_DEMOS)
add_subdirectory("demo")
endif()


include("${CMAKE_CURRENT_LIST_DIR}/lib/libraries.cmake")

idi_cmake_hook(post-configure)
Expand Down
23 changes: 12 additions & 11 deletions cmake/idi/functions/framework/idi_src.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@ macro(idi_src)
#####################################################################
# CORE LIBRARY #
#####################################################################
set(IDICMAKE_CORE "${IDICMAKE_PROJECT_NAME}_core")
if (IDICMAKE_IS_LIBRARY AND NOT IDICMAKE_IS_SHARED)
set(IDICMAKE_CORE "${IDICMAKE_PROJECT_NAME}")
else()
set(IDICMAKE_CORE "${IDICMAKE_PROJECT_NAME}_core")
endif()
# backwards compat, since CORE is always the main target
set(IDICMAKE_MAIN_TARGET ${IDICMAKE_CORE})

if(IDICMAKE_IS_LIBRARY AND IDICMAKE_IS_SHARED)
set(IDICMAKE_SHARED_NAME ${IDICMAKE_CORE})
add_library("${IDICMAKE_CORE}" SHARED "")
message(STATUS "------ Added SHARED Library ${IDICMAKE_CORE}")
else()
add_library("${IDICMAKE_CORE}" STATIC "")
message(STATUS "------ Added STATIC Library ${IDICMAKE_CORE}")
endif()
add_library("${IDICMAKE_CORE}" STATIC "")

set(IDICMAKE_CORE ${IDICMAKE_CORE} PARENT_SCOPE)
idi_target_compile_settings("${IDICMAKE_CORE}")
Expand All @@ -43,7 +40,7 @@ macro(idi_src)
endif()

#####################################################################
# MAIN TARGET #
# MAIN TARGET (if not just a static lib) #
#####################################################################

if(NOT IDICMAKE_IS_LIBRARY)
Expand All @@ -59,9 +56,11 @@ macro(idi_src)
install(TARGETS "${IDICMAKE_PROJECT_NAME}"
RUNTIME)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/main")
message(STATUS "------ Added EXECUTABLE ${IDICMAKE_PROJECT_NAME}")
else()
if(IDICMAKE_IS_SHARED)
add_library("${IDICMAKE_PROJECT_NAME}" SHARED "")
target_link_libraries("${IDICMAKE_PROJECT_NAME}" PUBLIC "$<LINK_LIBRARY:WHOLE_ARCHIVE,${IDICMAKE_CORE}>")
set(IDICMAKE_SHARED_NAME ${IDICMAKE_PROJECT_NAME})

set_target_properties("${IDICMAKE_SHARED_NAME}" PROPERTIES CXX_VISIBILITY_PRESET hidden)
Expand All @@ -75,8 +74,10 @@ macro(idi_src)
install(TARGETS "${IDICMAKE_SHARED_NAME}"
LIBRARY FILE_SET HEADERS DESTINATION includes/${IDICMAKE_PROJECT_NAME}/public)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/main")

message(STATUS "------ Added SHARED Library ${IDICMAKE_PROJECT_NAME}")
else()
add_library("${IDICMAKE_PROJECT_NAME}" ALIAS ${IDICMAKE_CORE})
message(STATUS "------ Added STATIC Library ${IDICMAKE_PROJECT_NAME}")
endif()
endif()
endmacro()
263 changes: 263 additions & 0 deletions cmake/idi/functions/idi_add_dependency.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
#
# @author Cliff Foster (Nou) <cliff@idi-systems.com>
#
# @copyright Copyright (c) 2024 International Development & Integration Systems LLC
#
# Licensed under a modified MIT License, see TEMPLATE_LICENSE for full license details
#

# cpm_parse_option is provided from the CPM library under an MIT license
#
# CPM.cmake - CMake's missing package manager
# ===========================================
# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions.
#
# MIT License

# splits a package option
function(cpm_parse_option OPTION)
string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}")
string(LENGTH "${OPTION}" OPTION_LENGTH)
string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH)
if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH)
# no value for key provided, assume user wants to set option to "ON"
set(OPTION_VALUE "ON")
else()
math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1")
string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE)
endif()
set(OPTION_KEY
"${OPTION_KEY}"
PARENT_SCOPE
)
set(OPTION_VALUE
"${OPTION_VALUE}"
PARENT_SCOPE
)
endfunction()

function(idi_get_repo_information REPO_DIR)
find_package(Git)
if(NOT Git_FOUND)
message(FATAL_ERROR "Git could not be found!")
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} "status" "--porcelain" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE IDI_TEST_REPO_PORCELAIN)
if("${IDI_TEST_REPO_PORCELAIN}" STREQUAL "")
set(IDI_REPO_IS_PORCELAIN
true
PARENT_SCOPE
)
else()
set(IDI_REPO_IS_PORCELAIN
false
PARENT_SCOPE
)
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} "tag" "--points-at" "HEAD" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE IDI_GET_REPO_TAG)
string(STRIP "${IDI_GET_REPO_TAG}" IDI_GET_REPO_TAG)
set(IDI_REPO_TAG
"${IDI_GET_REPO_TAG}"
PARENT_SCOPE
)
execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "HEAD" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE IDI_GET_REPO_SHA1)
string(STRIP "${IDI_GET_REPO_SHA1}" IDI_GET_REPO_SHA1)
set(IDI_REPO_SHA1
"${IDI_GET_REPO_SHA1}"
PARENT_SCOPE
)
execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "--abbrev-ref" "HEAD" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE IDI_GET_REPO_BRANCH)
string(STRIP "${IDI_GET_REPO_BRANCH}" IDI_GET_REPO_BRANCH)
set(IDI_REPO_BRANCH
"${IDI_GET_REPO_BRANCH}"
PARENT_SCOPE
)

execute_process(COMMAND ${GIT_EXECUTABLE} "branch" "--show-current" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE IDI_TEST_IS_TRACKING_BRANCH)
if("${IDI_TEST_IS_TRACKING_BRANCH}" STREQUAL "")
set(IDI_REPO_IS_TRACKING_BRANCH
false
PARENT_SCOPE
)
else()
set(IDI_REPO_IS_TRACKING_BRANCH
true
PARENT_SCOPE
)
endif()
set(IDI_REPO_BRANCH_IS_AHEAD
false
PARENT_SCOPE
)
if(IDI_REPO_IS_TRACKING_BRANCH)
execute_process(COMMAND ${GIT_EXECUTABLE} "fetch" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE JUNK)
execute_process(COMMAND ${GIT_EXECUTABLE} "status" "-sb" WORKING_DIRECTORY ${REPO_DIR} OUTPUT_VARIABLE IDI_TEST_IS_UP_TO_DATE)
string(REGEX MATCHALL "##.*\[behind [0-9]+\]$" IDI_TEST_BEHIND ${IDI_TEST_IS_UP_TO_DATE})
if (IDI_TEST_BEHIND)
set(IDI_REPO_BRANCH_IS_AHEAD
true
PARENT_SCOPE
)
else()
set(IDI_REPO_BRANCH_IS_AHEAD
false
PARENT_SCOPE
)
endif()
endif()
endfunction()

function(idi_commit_starts_with ACTUAL_SHA1 CHECK_SHA1)
set(IDI_REPO_SHA1_SAME false)
string(FIND "${ACTUAL_SHA1}" "${CHECK_SHA1}" IDI_STARTS_WITH_SHA1)
if("${IDI_STARTS_WITH_SHA1}" EQUAL 0)
set(IDI_REPO_SHA1_SAME true)
endif()
return(PROPAGATE IDI_REPO_SHA1_SAME)
endfunction()

function(__idi_add_dependency IDI_DEP_NAME IDI_DEP_URL IDI_DEP_TAG IDI_DEP_THIRD_PARTY)
set(options DOWNLOAD_ONLY)
set(multiValueArgs DEP_OPTIONS)
cmake_parse_arguments(IDI "${options}" "${oneValueArgs}"
"${multiValueArgs}" ${ARGN} )

string(TOLOWER ${IDI_DEP_NAME} IDI_DEP_NAME_LOWER)

if(TARGET ${IDI_DEP_NAME})
message(STATUS "${IDI_DEP_NAME} already added, skipping.")
return()
endif()

set(IDI_DO_POPULATE false)

if(IDI_DEP_THIRD_PARTY)
set(IDI_DEP_SOURCE_DIR "${IDICMAKE_EXTERNAL_THIRD_PARTY_LIB_DIR}/${IDI_DEP_NAME}")
set(IDI_DEP_BINARY_DIR "${CMAKE_BINARY_DIR}/_deps/${IDI_DEP_NAME_LOWER}")
if(EXISTS ${IDI_DEP_SOURCE_DIR} AND EXISTS "${IDI_DEP_SOURCE_DIR}/.git")

idi_get_repo_information(${IDI_DEP_SOURCE_DIR})

message(DEBUG "${IDI_DEP_NAME} IDI_DEP_TAG: ${IDI_DEP_TAG}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_IS_PORCELAIN: ${IDI_REPO_IS_PORCELAIN}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_TAG: ${IDI_REPO_TAG}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_SHA1: ${IDI_REPO_SHA1}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_BRANCH: ${IDI_REPO_BRANCH}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_IS_TRACKING_BRANCH: ${IDI_REPO_IS_TRACKING_BRANCH}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_BRANCH_IS_AHEAD: ${IDI_REPO_BRANCH_IS_AHEAD}")

if(NOT IDI_REPO_IS_PORCELAIN)
message(WARNING "Third-party dependency '${IDI_DEP_NAME}' has uncomitted or untracked files.")
endif()

idi_commit_starts_with("${IDI_REPO_SHA1}" "${IDI_DEP_TAG}")

if (
(NOT ("${IDI_REPO_TAG}" STREQUAL "${IDI_DEP_TAG}")) AND
(NOT IDI_REPO_SHA1_SAME) AND
(NOT ("${IDI_REPO_BRANCH}" STREQUAL "${IDI_DEP_TAG}"))
)
set(IDI_DO_POPULATE true)
endif()

if(IDI_REPO_IS_PORCELAIN AND IDI_REPO_IS_TRACKING_BRANCH AND IDI_REPO_BRANCH_IS_AHEAD)
message(STATUS "Updating ${IDI_DEP_NAME} to current head of branch '${IDI_DEP_TAG}'")
set(IDI_DO_POPULATE true)
endif()

else()
set(IDI_DO_POPULATE true)
endif()
else()
set(IDI_DEP_SOURCE_DIR "${IDICMAKE_EXTERNAL_LIB_DIR}/${IDI_DEP_NAME}")
set(IDI_DEP_BINARY_DIR "${CMAKE_BINARY_DIR}/_deps/${IDI_DEP_NAME_LOWER}")
if(EXISTS ${IDI_DEP_SOURCE_DIR} AND EXISTS "${IDI_DEP_SOURCE_DIR}/.git")

idi_get_repo_information(${IDI_DEP_SOURCE_DIR})

message(DEBUG "${IDI_DEP_NAME} IDI_DEP_TAG: ${IDI_DEP_TAG}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_IS_PORCELAIN: ${IDI_REPO_IS_PORCELAIN}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_TAG: ${IDI_REPO_TAG}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_SHA1: ${IDI_REPO_SHA1}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_BRANCH: ${IDI_REPO_BRANCH}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_IS_TRACKING_BRANCH: ${IDI_REPO_IS_TRACKING_BRANCH}")
message(DEBUG "${IDI_DEP_NAME} IDI_REPO_BRANCH_IS_AHEAD: ${IDI_REPO_BRANCH_IS_AHEAD}")

if(NOT IDI_REPO_IS_PORCELAIN)
message(WARNING "First-party dependency '${IDI_DEP_NAME}' has uncomitted or untracked files.")
endif()

idi_commit_starts_with("${IDI_REPO_SHA1}" "${IDI_DEP_TAG}")

set(IDI_REPO_SAME_COMMIT true)

if (
(NOT ("${IDI_REPO_TAG}" STREQUAL "${IDI_DEP_TAG}")) AND
(NOT IDI_REPO_SHA1_SAME) AND
(NOT ("${IDI_REPO_BRANCH}" STREQUAL "${IDI_DEP_TAG}"))
)
message(WARNING "First-party dependency '${IDI_DEP_NAME}' is not on the same commit as defined by configuration. ${IDI_DEP_TAG} != ${IDI_REPO_TAG}|${IDI_REPO_SHA1}|${IDI_REPO_BRANCH}")
set(IDI_REPO_SAME_COMMIT false)
endif()

if(IDI_REPO_IS_PORCELAIN AND IDI_REPO_IS_TRACKING_BRANCH AND IDI_REPO_BRANCH_IS_AHEAD)
message(STATUS "Updating ${IDI_DEP_NAME} to current head of branch '${IDI_DEP_TAG}'")
set(IDI_DO_POPULATE true)
endif()


if(IDI_SYNC_DEPENDENCIES)
if(NOT IDI_REPO_IS_PORCELAIN)
message(FATAL_ERROR "Attempted to sync dependencies but first-party dependency '${IDI_DEP_NAME}' has uncomitted or untracked files, commit or stash changes and try again.")
else()
set(IDI_DO_POPULATE true)
endif()
endif()

else()
set(IDI_DO_POPULATE true)
endif()
endif()

if(${IDI_DO_POPULATE})
message(STATUS "Populating dependency '${IDI_DEP_NAME}' from ${IDI_DEP_URL} at ref ${IDI_DEP_TAG}")
FetchContent_Declare(
${IDI_DEP_NAME}
GIT_REPOSITORY ${IDI_DEP_URL}
GIT_TAG ${IDI_DEP_TAG}
SOURCE_DIR ${IDI_DEP_SOURCE_DIR}
EXCLUDE_FROM_ALL
SYSTEM
)

if(${${IDI_DEP_NAME_LOWER}_POPULATED})
message(DEBUG "${IDI_DEP_NAME} already populated, skipping.")
return()
endif()

FetchContent_Populate(${IDI_DEP_NAME})
else()
message(DEBUG "${IDI_DEP_NAME} already exists, skipping download.")
endif()

foreach(OPTION ${IDI_DEP_OPTIONS})
cpm_parse_option("${OPTION}")
set(${OPTION_KEY} "${OPTION_VALUE}")
endforeach()

if(NOT IDI_DOWNLOAD_ONLY)
if (IDI_DEP_THIRD_PARTY)
add_subdirectory(${IDI_DEP_SOURCE_DIR} ${IDI_DEP_BINARY_DIR} EXCLUDE_FROM_ALL SYSTEM)
else()
add_subdirectory(${IDI_DEP_SOURCE_DIR} ${IDI_DEP_BINARY_DIR})
endif()
endif()
message(STATUS "Added dependency ${IDI_DEP_NAME}")
endfunction()

function(idi_add_third_party_dependency IDI_DEP_NAME IDI_DEP_URL IDI_DEP_TAG)
__idi_add_dependency(${IDI_DEP_NAME} ${IDI_DEP_URL} ${IDI_DEP_TAG} true ${ARGN})
endfunction()

function(idi_add_first_party_dependency IDI_DEP_NAME IDI_DEP_URL IDI_DEP_TAG)
__idi_add_dependency(${IDI_DEP_NAME} ${IDI_DEP_URL} ${IDI_DEP_TAG} false ${ARGN})
endfunction()
6 changes: 4 additions & 2 deletions cmake/idi/functions/idi_component_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ function(__idi_component_test component_name test_file)
add_executable("${CURRENT_LIBRARY_TEST}" ${test_file})
idi_target_compile_settings("${CURRENT_LIBRARY_TEST}")

target_include_directories("${CURRENT_LIBRARY_TEST}" SYSTEM PRIVATE
"${IDICMAKE_EXTERNAL_LIB_DIR}/Catch2/single_include")
# target_include_directories("${CURRENT_LIBRARY_TEST}" SYSTEM PRIVATE
# "${IDICMAKE_EXTERNAL_LIB_DIR}/Catch2/single_include")

target_link_libraries("${CURRENT_LIBRARY_TEST}" PRIVATE Catch2::Catch2WithMain)

set(ADD_MODE "ADDITIONAL_SOURCES")
set(ADD_CORE true)
Expand Down
Loading

0 comments on commit 16a32dc

Please sign in to comment.