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

Fix Windows Path Mangling #110

Merged
1 change: 1 addition & 0 deletions .github/.licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ header:
- tests/
- LICENSE
- requirements.txt
- "**/**.txt.in"

comment: never
162 changes: 132 additions & 30 deletions cmake/cmake_test/add_dir.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,159 @@

include_guard()

include(cmakepp_lang/cmakepp_lang)

#[[[
# This function will find all :code:`*.cmake` files in the specified directory as well as recursively through all subdirectories.
# It will then configure the boilerplate template to include() each cmake file and register each configured boilerplate
# with CTest. The configured templates will be executed seperately via CTest during the Test phase, and each *.cmake
# file found in the specified directory is assumed to contain CMakeTest tests.
# This function will find all :code:`*.cmake` files in the specified directory
# as well as recursively through all subdirectories. It will then configure the
# boilerplate template to include() each cmake file and register each
# configured boilerplate with CTest. The configured templates will be executed
# seperately via CTest during the Test phase, and each *.cmake file found in
# the specified directory is assumed to contain CMakeTest tests.
#
# :param dir: The directory to search for *.cmake files. Subdirectories will be recursively searched.
# :type dir: path
# :param test_dir: The directory to search for *.cmake files containing tests.
# Subdirectories will be recursively searched.
# :type test_dir: path
#
# **Keyword Arguments**
#
# :keyword CT_DEBUG_MODE_ON: Enables debug mode when the tests are run.
# :type CT_DEBUG_MODE_ON: bool
# :keyword USE_REL_PATH_NAMES: Enables using shorter, relative paths for
# test names, but increases the chance of name
# collisions.
# :type USE_REL_PATH_NAMES: bool
# :keyword CMAKE_OPTIONS: List of additional CMake options to be
# passed to all test invocations. Options
# should follow the syntax: :code:`-D<variable_name>=<value>`
# should follow the syntax:
# :code:`-D<variable_name>=<value>`
# :type CMAKE_OPTIONS: list
#
#
#]]
function(ct_add_dir _ad_dir)
function(ct_add_dir _ad_test_dir)
set(_ad_multi_value_args "CMAKE_OPTIONS")
set(_ad_options CT_DEBUG_MODE_ON)
# TODO: This name is potentially misleading because it seems to enable
# debug mode for the test projects (see the use of 'ct_debug_mode'
# in cmake/cmake_test/templates/test_project_CMakeLists.txt.in).
# I propose renaming it to something like "ENABLE_DEBUG_MODE_IN_TESTS".
set(_ad_options CT_DEBUG_MODE_ON USE_REL_PATH_NAMES)
cmake_parse_arguments(PARSE_ARGV 1 ADD_DIR "${_ad_options}" "" "${_ad_multi_value_args}")

# This variable will be picked up by the template
# TODO: This variable should be made Config File-specific and may end up
# mirroring the rename of CT_DEBUG_MODE_ON above, if that happens
set(ct_debug_mode "${ADD_DIR_CT_DEBUG_MODE_ON}")

get_filename_component(_ad_abs_test_dir "${_ad_dir}" REALPATH)
file(GLOB_RECURSE _ad_files LIST_DIRECTORIES FALSE FOLLOW_SYMLINKS "${_ad_abs_test_dir}/*.cmake") #Recurse over target dir to find all cmake files
# We expect to be given relative paths wrt the project, like
# ``ct_add_dir("test")``. This ensures we have an absolute path
# to the test directory as well as a CMak-style normalized path.
get_filename_component(_ad_abs_test_dir "${_ad_test_dir}" REALPATH)
file(TO_CMAKE_PATH "${_ad_abs_test_dir}" _ad_abs_test_dir)

# Recurse over the test directory to find all cmake files
# (assumed to all be test files)
file(GLOB_RECURSE
_ad_test_files
LIST_DIRECTORIES FALSE
FOLLOW_SYMLINKS "${_ad_abs_test_dir}/*.cmake"
)

# Each test file will get its own directory and "mini-project" in the
# build directory to facilitate independently running each test case.
# These directories are created from hashes of the test directory and
# test file paths to help ensure that each path is unique
foreach(_ad_test_file ${_ad_test_files})
# Set the test file path for configuring the test mini-project
set(_CT_CMAKELISTS_TEMPLATE_TEST_FILE "${_ad_test_file}")

# Sanitize the full path to the test file to get the mini-project name
# for configuring the test mini-project
cpp_sanitize_string(
_CT_CMAKELISTS_TEMPLATE_PROJECT_NAME "${_ad_test_file}"
)

# Find the relative path to the test file from the test directory to
# reduce the length of the test names
file(RELATIVE_PATH
_ad_test_file_rel_path
"${_ad_abs_test_dir}"
"${_ad_test_file}"
)

foreach(_ad_test_file ${_ad_files})
#Find rel path so we don't end up with insanely long paths under test folders
file(RELATIVE_PATH _ad_rel_path "${_ad_abs_test_dir}" "${_ad_test_file}")
file(TO_CMAKE_PATH "${_ad_abs_test_dir}" _ad_normalized_dir)
string(REPLACE "/" "_" _ad_dir_prefix "${_ad_normalized_dir}")
string(REPLACE ":" "_" _ad_dir_prefix "${_ad_dir_prefix}")
# Mangle the test directory and test file paths, since path strings
# commonly have characters that are illegal in file names
cpp_sanitize_string(_ad_test_dest_prefix "${_ad_abs_test_dir}")
cpp_sanitize_string(_ad_test_proj_dir "${_ad_test_file_rel_path}")

#Fill in boilerplate, copy to build dir
# Get hashes for the prefix directory and test project directory
string(SHA256 _ad_test_dest_prefix_hash "${_ad_test_dest_prefix}")
string(SHA256 _ad_test_proj_dir_hash "${_ad_test_proj_dir}")

# Truncate the hashes to 7 characters
set(_ad_hash_length 7)
string(SUBSTRING
"${_ad_test_dest_prefix_hash}"
0
"${_ad_hash_length}"
_ad_test_dest_prefix_hash
)
string(SUBSTRING
"${_ad_test_proj_dir_hash}"
0
"${_ad_hash_length}"
_ad_test_proj_dir_hash
)

# Create the test destination path in the build directory
set(_ad_test_dest_full_path
"${CMAKE_CURRENT_BINARY_DIR}/tests/${_ad_test_dest_prefix_hash}/${_ad_test_proj_dir_hash}"
)

# Configure the CMakeLists.txt for test in the build directory
configure_file(
"${_CT_TEMPLATES_DIR}/lists.txt"
"${CMAKE_CURRENT_BINARY_DIR}/tests/${_ad_dir_prefix}/${_ad_rel_path}/src/CMakeLists.txt"
"${_CT_TEMPLATES_DIR}/test_CMakeLists.txt.in"
"${_ad_test_dest_full_path}/src/CMakeLists.txt"
@ONLY
)

if (ADD_DIR_USE_REL_PATH_NAMES)
# Option 1 - shortest but highest collision likelyhood
# Prepend the test name to the relative path to test file from the
# given test directory

# set(_ad_test_name "${}/${_ad_test_file_rel_path}")


# Option 2 - longest but least collision likelyhood
# Get the path from the root of the project, with the project name
# prepended

# Generate relative path from project root for the test name
# file(RELATIVE_PATH
# _ad_test_file_rel_path_from_proj_root
# "${PROJECT_SOURCE_DIR}"
# "${_ad_test_file}"
# )
# # Prepend the project name to the relative path
# set(_ad_test_name "${PROJECT_NAME}/${_ad_test_file_rel_path_from_proj_root}")


# Option 3 - in-between length and collision likelyhood
# Prepend the project name and given test directory name to the
# test file relative path

get_filename_component(_ad_test_dir_name "${_ad_test_dir}" NAME)
set(_ad_test_name "${PROJECT_NAME}::${_ad_test_dir_name}/${_ad_test_file_rel_path}")
else()
set(_ad_test_name "${_ad_test_file}")
endif()
add_test(
NAME
"${_ad_dir_prefix}_${_ad_rel_path}"
COMMAND
"${CMAKE_COMMAND}"
-S "${CMAKE_CURRENT_BINARY_DIR}/tests/${_ad_dir_prefix}/${_ad_rel_path}/src"
-B "${CMAKE_CURRENT_BINARY_DIR}/tests/${_ad_dir_prefix}/${_ad_rel_path}"
${ADD_DIR_CMAKE_OPTIONS}
NAME
"${_ad_test_name}"
COMMAND
"${CMAKE_COMMAND}"
-S "${_ad_test_dest_full_path}/src"
-B "${_ad_test_dest_full_path}"
${ADD_DIR_CMAKE_OPTIONS}
)

endforeach()
endfunction()
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
#The configured file will be used as the CTest entrypoint and will
#include() the intended test file before executing the tests found.


cmake_minimum_required(VERSION @_ct_min_cmake_version@) #Required for FetchContent_MakeAvailable()
set(_ct_min_cmake_version @_ct_min_cmake_version@) #Propagate min version down
project(@_ad_test_file@ LANGUAGES C) #Needed so dummy libraries don't complain about not having a linkage language

# A language is set so the dummy projects don't complain about not having a
# linkage language.
project(@_CT_CMAKELISTS_TEMPLATE_PROJECT_NAME@ LANGUAGES C)

#Enable colors in Unix environments, ignored on Windows. Will not work with pipes
set(CMAKETEST_USE_COLORS "@CMAKETEST_USE_COLORS@")
Expand All @@ -16,10 +17,16 @@ if(NOT CT_PRINT_LENGTH GREATER 0)
set(CT_PRINT_LENGTH "@CT_PRINT_LENGTH@")
endif()

set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@" CACHE STRING "" FORCE)


# Propagate the module CMake module path for the project so they stick when
# the tests are run separately
set(CMAKE_MODULE_PATH
"@CMAKE_MODULE_PATH@"
CACHE STRING "" FORCE
)

# Unconfirmed: This assignment is done in a seemily redundant way
# because some CMakePPLang debug statements trigger just by
# CMAKEPP_LANG_DEBUG_MODE being defined at all
if(@ct_debug_mode@)
set(CMAKEPP_LANG_DEBUG_MODE "TRUE")
endif()
Expand All @@ -30,5 +37,5 @@ cpp_set_global("CT_DEBUG_MODE" "@ct_debug_mode@")
include("cmake_test/cmake_test")
set(CMAKEPP_LANG_DEBUG_MODE "@CMAKEPP_LANG_DEBUG_MODE@")

include([[@_ad_test_file@]])
include([[@_CT_CMAKELISTS_TEMPLATE_TEST_FILE@]])
ct_exec_tests()
Loading