blob: bc51324dab1d46a6b9c4ca1959127f2ce7af0016 [file]
# 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 ()