mirror of
https://github.com/Kitware/CMake.git
synced 2026-11-06 05:16:58 +00:00
Tests/RunCMake/property_init: Add lint/coverage case
Parse the relevant source file and existing tests to prevent new properties from being implemented without a corresponding test.
This commit is contained in:
1 parent
29315ad49e
commit
eba57196e7
3 files changed
+186
-1
No files matched your search
@@ -897,7 +897,7 @@ add_RunCMake_test(option)
|
||||
add_RunCMake_test(PrintHelpers)
|
||||
add_RunCMake_test(project -DCMake_TEST_RESOURCES=${CMake_TEST_RESOURCES})
|
||||
add_RunCMake_test(project_injected)
|
||||
add_RunCMake_test(property_init)
|
||||
add_RunCMake_test(property_init -DCMake_SOURCE_DIR=${CMake_SOURCE_DIR})
|
||||
add_RunCMake_test(DependencyProviders)
|
||||
add_RunCMake_test(return)
|
||||
add_RunCMake_test(SarifOutput)
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
# Check that every property initialized from `StaticTargetProperties` in
|
||||
# `Source/cmTarget.cxx` is tested by the case file matching its
|
||||
# `InitCondition`, and that every property tested here is still initialized.
|
||||
|
||||
set(source_file "${CMake_SOURCE_DIR}/Source/cmTarget.cxx")
|
||||
if (NOT EXISTS "${source_file}")
|
||||
message(FATAL_ERROR
|
||||
"Source file not found: '${source_file}'")
|
||||
endif ()
|
||||
|
||||
# Map each `InitCondition` to the case file testing it.
|
||||
set(case_for_Always "Always")
|
||||
set(case_for_CanCompileSources "CompileSources")
|
||||
set(case_for_NeedsXcodeAndCanCompileSources "CompileSources")
|
||||
set(case_for_NormalTarget "NormalTarget")
|
||||
set(case_for_NonImportedTarget "NonImportedTarget")
|
||||
set(case_for_NormalNonImportedTarget "NonImportedNormalTarget")
|
||||
set(case_for_TargetWithArtifact "TargetsWithArtifact")
|
||||
set(case_for_NonExecutableWithArtifact "LibraryArtifact")
|
||||
set(case_for_LinkableLibraryTarget "Linkable")
|
||||
set(case_for_ExecutableTarget "Executable")
|
||||
set(case_for_SharedLibraryTarget "SharedLibrary")
|
||||
set(case_for_TargetWithSymbolExports "TargetsWithExports")
|
||||
set(case_for_TargetWithCommands "TargetsWithCommands")
|
||||
|
||||
# Properties tested by a case other than the one for their `InitCondition`.
|
||||
set(case_override_POSITION_INDEPENDENT_CODE "PICTargets")
|
||||
|
||||
# Properties in the table which are not tested.
|
||||
set(untested
|
||||
# property reason
|
||||
"XCODE_GENERATE_SCHEME" "NeedsXcode: no Xcode-only, all-target-type case"
|
||||
)
|
||||
|
||||
# Properties tested which are not initialized from the table.
|
||||
set(not_in_table
|
||||
# property reason
|
||||
"notset" "Never initialized, checks the harness itself"
|
||||
"SYSTEM" "Initialized separately for non-normal targets"
|
||||
)
|
||||
|
||||
# Extract the property names from a "property reason" table.
|
||||
function (prepare_properties_table table output)
|
||||
set(_properties)
|
||||
set(is_property 1)
|
||||
foreach (item IN LISTS "${table}")
|
||||
if (is_property)
|
||||
list(APPEND _properties "${item}")
|
||||
set(is_property 0)
|
||||
else ()
|
||||
set(is_property 1)
|
||||
endif ()
|
||||
endforeach ()
|
||||
set("${output}" "${_properties}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
# Parse the source table.
|
||||
file(READ "${source_file}" source)
|
||||
string(REPLACE ";" "" source "${source}")
|
||||
string(REPLACE "\\" "" source "${source}")
|
||||
|
||||
string(FIND "${source}" "#define COMMON_LANGUAGE_PROPERTIES(lang)" macro_begin)
|
||||
string(FIND "${source}" "TargetProperty const StaticTargetProperties[] = {" table_begin)
|
||||
if (macro_begin EQUAL -1 OR table_begin EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"Failed to find `StaticTargetProperties` in '${source_file}'")
|
||||
endif ()
|
||||
math(EXPR macro_length "${table_begin} - ${macro_begin}")
|
||||
string(SUBSTRING "${source}" "${macro_begin}" "${macro_length}" macro_text)
|
||||
string(SUBSTRING "${source}" "${table_begin}" -1 table_text)
|
||||
string(FIND "${table_text}" "\n};" table_end)
|
||||
string(SUBSTRING "${table_text}" 0 "${table_end}" table_text)
|
||||
|
||||
set(language_suffixes)
|
||||
string(REPLACE "\n" ";" macro_lines "${macro_text}")
|
||||
foreach (line IN LISTS macro_lines)
|
||||
if (line MATCHES "\\{ #lang \"([A-Za-z_]+)\"_s, IC::([A-Za-z]+)")
|
||||
list(APPEND language_suffixes "${CMAKE_MATCH_1}")
|
||||
set("language_ic_${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
set(source_properties)
|
||||
string(REPLACE "\n" ";" table_lines "${table_text}")
|
||||
foreach (line IN LISTS table_lines)
|
||||
if (line MATCHES "^ *\\{ \"([A-Za-z_]+)\"_s(, \"[^\"]*\"_s)?(, IC::([A-Za-z]+))?")
|
||||
set(property "${CMAKE_MATCH_1}")
|
||||
set(ic "${CMAKE_MATCH_4}")
|
||||
if (ic STREQUAL "")
|
||||
set(ic "Always")
|
||||
endif ()
|
||||
list(APPEND source_properties "${property}")
|
||||
set("source_ic_${property}" "${ic}")
|
||||
elseif (line MATCHES "^ *COMMON_LANGUAGE_PROPERTIES\\(([A-Za-z]+)\\)")
|
||||
set(lang "${CMAKE_MATCH_1}")
|
||||
foreach (suffix IN LISTS language_suffixes)
|
||||
list(APPEND source_properties "${lang}${suffix}")
|
||||
set("source_ic_${lang}${suffix}" "${language_ic_${suffix}}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
if (NOT source_properties)
|
||||
message(FATAL_ERROR
|
||||
"Failed to parse any properties from '${source_file}'")
|
||||
endif ()
|
||||
|
||||
# Parse the test tables. Rows look like `"PROPERTY" "expected" "alias"`.
|
||||
file(GLOB case_files "${CMAKE_CURRENT_LIST_DIR}/*.cmake")
|
||||
list(REMOVE_ITEM case_files
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Coverage.cmake"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/RunCMakeTest.cmake"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/util.cmake"
|
||||
)
|
||||
|
||||
set(tested_properties)
|
||||
foreach (case_file IN LISTS case_files)
|
||||
get_filename_component(case "${case_file}" NAME_WE)
|
||||
file(STRINGS "${case_file}" lines)
|
||||
set(language_templates)
|
||||
set(languages)
|
||||
foreach (line IN LISTS lines)
|
||||
if (line MATCHES "^ *\"([A-Za-z_]+)\" +\"")
|
||||
list(APPEND tested_properties "${CMAKE_MATCH_1}")
|
||||
list(APPEND "tested_cases_${CMAKE_MATCH_1}" "${case}")
|
||||
elseif (line MATCHES "^ *\"\\\${lang}([A-Za-z_]+)\" +\"")
|
||||
list(APPEND language_templates "${CMAKE_MATCH_1}")
|
||||
elseif (line MATCHES "^add_language_properties\\(([A-Za-z]+) ")
|
||||
list(APPEND languages "${CMAKE_MATCH_1}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
foreach (lang IN LISTS languages)
|
||||
foreach (suffix IN LISTS language_templates)
|
||||
list(APPEND tested_properties "${lang}${suffix}")
|
||||
list(APPEND "tested_cases_${lang}${suffix}" "${case}")
|
||||
endforeach ()
|
||||
endforeach ()
|
||||
endforeach ()
|
||||
list(REMOVE_DUPLICATES tested_properties)
|
||||
|
||||
# Every source property must be tested in its case (or be listed as untested).
|
||||
prepare_properties_table(untested untested_properties)
|
||||
foreach (property IN LISTS source_properties)
|
||||
set(ic "${source_ic_${property}}")
|
||||
if (DEFINED "case_override_${property}")
|
||||
set(case "${case_override_${property}}")
|
||||
elseif (DEFINED "case_for_${ic}")
|
||||
set(case "${case_for_${ic}}")
|
||||
else ()
|
||||
set(case "")
|
||||
endif ()
|
||||
|
||||
if (property IN_LIST untested_properties)
|
||||
if (DEFINED "tested_cases_${property}")
|
||||
message(SEND_ERROR
|
||||
"Property '${property}' is listed as untested in Coverage.cmake, but "
|
||||
"is tested in: ${tested_cases_${property}}")
|
||||
endif ()
|
||||
elseif (case STREQUAL "")
|
||||
message(SEND_ERROR
|
||||
"Property '${property}' uses `IC::${ic}`, which has no case mapped in "
|
||||
"Coverage.cmake")
|
||||
elseif (NOT DEFINED "tested_cases_${property}")
|
||||
message(SEND_ERROR
|
||||
"Property '${property}' (`IC::${ic}`) is initialized in cmTarget.cxx, "
|
||||
"but is not tested in ${case}.cmake")
|
||||
elseif (NOT case IN_LIST "tested_cases_${property}")
|
||||
message(SEND_ERROR
|
||||
"Property '${property}' (`IC::${ic}`) should be tested in "
|
||||
"${case}.cmake, but is tested in: ${tested_cases_${property}}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Every tested property must still be initialized from the table.
|
||||
prepare_properties_table(not_in_table not_in_table_properties)
|
||||
foreach (property IN LISTS tested_properties)
|
||||
if (NOT property IN_LIST source_properties AND
|
||||
NOT property IN_LIST not_in_table_properties)
|
||||
message(SEND_ERROR
|
||||
"Property '${property}' is tested in ${tested_cases_${property}}, but "
|
||||
"is not initialized from `StaticTargetProperties` in cmTarget.cxx")
|
||||
endif ()
|
||||
endforeach ()
|
||||
@@ -19,3 +19,5 @@ run_cmake(SharedLibrary)
|
||||
run_cmake(TargetsWithArtifact)
|
||||
run_cmake(TargetsWithCommands)
|
||||
run_cmake(TargetsWithExports)
|
||||
|
||||
run_cmake_script(Coverage -DCMake_SOURCE_DIR=${CMake_SOURCE_DIR})
|
||||
Reference in new issue
Block a user