From a60137d0a4adc2c8e9b8dbc8e7c3f683d93cf2ea Mon Sep 17 00:00:00 2001 From: Cliff Foster Date: Wed, 28 Feb 2024 15:56:38 -0800 Subject: [PATCH] Add dependency helpers --- cmake/idi/functions/framework/idi_init.cmake | 8 +- cmake/idi/functions/idi_add_dependency.cmake | 193 +++++++++++++++++++ cmake/idi/idi-template.cmake | 1 + cmake/idi/updater/updater_imp.cmake | 1 + cmake/idi/updater/updater_version.cmake | 6 +- lib/.gitignore | 5 + lib/libraries.cmake | 3 +- 7 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 cmake/idi/functions/idi_add_dependency.cmake create mode 100644 lib/.gitignore diff --git a/cmake/idi/functions/framework/idi_init.cmake b/cmake/idi/functions/framework/idi_init.cmake index 1bad2d6..c914830 100644 --- a/cmake/idi/functions/framework/idi_init.cmake +++ b/cmake/idi/functions/framework/idi_init.cmake @@ -123,13 +123,7 @@ macro(idi_init) # Define a nice short hand for 3rd party external library folders set(IDICMAKE_EXTERNAL_LIB_DIR "${CMAKE_CURRENT_LIST_DIR}/lib") - FetchContent_Declare(Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.2 - EXCLUDE_FROM_ALL - SYSTEM - ) - FetchContent_MakeAvailable(Catch2) + 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) diff --git a/cmake/idi/functions/idi_add_dependency.cmake b/cmake/idi/functions/idi_add_dependency.cmake new file mode 100644 index 0000000..0958c6b --- /dev/null +++ b/cmake/idi/functions/idi_add_dependency.cmake @@ -0,0 +1,193 @@ +# +# @author Cliff Foster (Nou) +# +# @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 + ) +endfunction() + +function(__idi_add_dependency IDI_DEP_NAME IDI_DEP_URL IDI_DEP_TAG IDI_DEP_THIRD_PARTY) + 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 "${CMAKE_SOURCE_DIR}/lib/.third-party/${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("${IDI_DEP_NAME} IDI_REPO_IS_PORCELAIN: ${IDI_REPO_IS_PORCELAIN}") + message("${IDI_DEP_NAME} IDI_REPO_TAG: ${IDI_REPO_TAG}") + message("${IDI_DEP_NAME} IDI_REPO_SHA1: ${IDI_REPO_SHA1}") + message("${IDI_DEP_NAME} IDI_REPO_BRANCH: ${IDI_REPO_BRANCH}") + + if(NOT IDI_REPO_IS_PORCELAIN) + message(WARNING "Third-party dependency '${IDI_DEP_NAME}' has uncomitted or untracked files.") + endif() + + if ( + (NOT ("${IDI_REPO_TAG}" STREQUAL "${IDI_DEP_TAG}")) AND + (NOT ("${IDI_REPO_SHA1}" STREQUAL "${IDI_DEP_TAG}")) AND + (NOT ("${IDI_REPO_BRANCH}" STREQUAL "${IDI_DEP_TAG}")) + ) + set(IDI_DO_POPULATE true) + endif() + + else() + set(IDI_DO_POPULATE true) + endif() + else() + set(IDI_DEP_SOURCE_DIR "${CMAKE_SOURCE_DIR}/lib/${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("${IDI_DEP_NAME} IDI_REPO_IS_PORCELAIN: ${IDI_REPO_IS_PORCELAIN}") + message("${IDI_DEP_NAME} IDI_REPO_TAG: ${IDI_REPO_TAG}") + message("${IDI_DEP_NAME} IDI_REPO_SHA1: ${IDI_REPO_SHA1}") + message("${IDI_DEP_NAME} IDI_REPO_BRANCH: ${IDI_REPO_BRANCH}") + + + if(NOT IDI_REPO_IS_PORCELAIN) + message(WARNING "First-party dependency '${IDI_DEP_NAME}' has uncomitted or untracked files.") + endif() + + set(IDI_REPO_SAME_COMMIT true) + + if ( + (NOT ("${IDI_REPO_TAG}" STREQUAL "${IDI_DEP_TAG}")) AND + (NOT ("${IDI_REPO_SHA1}" STREQUAL "${IDI_DEP_TAG}")) 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_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 "Adding dependency '${IDI_DEP_NAME}' from ${IDI_DEP_URL} at tag ${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(STATUS "${IDI_DEP_NAME} already populated, skipping.") + return() + endif() + + FetchContent_Populate(${IDI_DEP_NAME}) + else() + message(STATUS "${IDI_DEP_NAME} already exists, skipping download.") + endif() + + foreach(OPTION ${IDI_DEP_OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + add_subdirectory(${IDI_DEP_SOURCE_DIR} ${IDI_DEP_BINARY_DIR} EXCLUDE_FROM_ALL SYSTEM) + 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() diff --git a/cmake/idi/idi-template.cmake b/cmake/idi/idi-template.cmake index cd0c085..5a65b9f 100644 --- a/cmake/idi/idi-template.cmake +++ b/cmake/idi/idi-template.cmake @@ -26,6 +26,7 @@ if(NOT IDICMAKE_DID_UPDATE) "If you updated the template recently, also update the CMakeList.txt in the src/base component directory") endif() + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/idi/functions/idi_add_dependency.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/idi/functions/idi_add_sources.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/idi/functions/idi_add_subdirectory.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/idi/functions/idi_component_test.cmake) diff --git a/cmake/idi/updater/updater_imp.cmake b/cmake/idi/updater/updater_imp.cmake index b513a3d..5787b4a 100644 --- a/cmake/idi/updater/updater_imp.cmake +++ b/cmake/idi/updater/updater_imp.cmake @@ -25,3 +25,4 @@ endif() file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/src/base/include/template_project" "${CMAKE_CURRENT_SOURCE_DIR}/src/base/include/${IDICMAKE_PROJECT_NAME}") do_replace_file("CMakeLists.txt") do_replace_file("src/CMakeLists.txt") +do_replace_file("lib/.gitignore") diff --git a/cmake/idi/updater/updater_version.cmake b/cmake/idi/updater/updater_version.cmake index 07dad23..b79b98f 100644 --- a/cmake/idi/updater/updater_version.cmake +++ b/cmake/idi/updater/updater_version.cmake @@ -6,10 +6,6 @@ # Licensed under a modified MIT License, see TEMPLATE_LICENSE for full license details # # This is the _framework_ **updater** version number, and is NOT related to the underlying project. -# -# If the local updater is older than the update's updater it will require the user to re-run the updater -# with a specific flag that allows for and consents to "remote code" execution. -# # Any time the updater is changed this version should be incremented!!! -set(IDICMAKE_CPP_UPDATER_VERSION 2) +set(IDICMAKE_CPP_UPDATER_VERSION 3) diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..3099a57 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,5 @@ +# Do not edit this file, it can/will be replaced by template updates. +* +!libraries.cmake +!README.mod +!.gitignore diff --git a/lib/libraries.cmake b/lib/libraries.cmake index 5d509d2..6fcfa2c 100644 --- a/lib/libraries.cmake +++ b/lib/libraries.cmake @@ -1,4 +1,5 @@ # Include libraries here. # Catch2 is included by deault, no need to re-add it here. -# add_subdirectory(${IDI_EXTERNAL_LIB_DIR}/lib_folder) +# idi_add_third_party_dependency(Catch2 https://github.com/catchorg/Catch2.git v3.5.2) +