blob: 37f11e6ce4ca906a661fc84cdfbdc0329e98be2b [file] [edit]
cmake_policy(SET CMP0222 NEW)
# The operator is an if() spelling of cmake_path(IS_PREFIX), so assert that
# the two agree rather than repeating a table of expected values here. What
# each input should evaluate to is pinned by the cmake_path(IS_PREFIX) test;
# this asserts only that the two surfaces cannot drift apart. Neither
# normalizes, so the comparison is against the plain form of the command.
#
# Because nothing below states an expected value, the host path model needs
# no branching: parity has to hold on every platform whatever the answer is.
function(assert_parity prefix path)
set(prefix_var "${prefix}")
cmake_path(IS_PREFIX prefix_var "${path}" expected)
if(expected)
set(expected TRUE)
else()
set(expected FALSE)
endif()
if(prefix PATH_IS_PREFIX path)
set(actual TRUE)
else()
set(actual FALSE)
endif()
if(NOT actual STREQUAL expected)
message(SEND_ERROR
"if('${prefix}' PATH_IS_PREFIX '${path}') is ${actual}, but "
"cmake_path(IS_PREFIX) says ${expected}")
endif()
endfunction()
# Each pair appears in both roles, so a reversed call cannot pass.
assert_parity("/a/b" "/a/b/c")
assert_parity("/a/b/c" "/a/b")
assert_parity("/a/b" "/a/b")
# Trailing separators, which are significant and asymmetric.
assert_parity("/a/b" "/a/b/")
assert_parity("/a/b/" "/a/b")
assert_parity("/a/b/" "/a/b/c")
# Without NORMALIZE, '.' and '..' are ordinary components.
assert_parity("/a/b" "/a/b/../../etc")
assert_parity("/a/b" "/a/./b//c")
assert_parity("." "./a/b")
assert_parity("." "a/b")
# Duplicate separators are not components.
assert_parity("a/b" "a///b")
# Sibling sharing a textual prefix.
assert_parity("/a/b" "/a/bc")
# Relative and absolute paths are not reconciled.
assert_parity("/a" "b/c")
assert_parity("b" "b/c")
assert_parity("/" "b/c")
assert_parity("/" "/a/b")
# Empty operands in each position.
assert_parity("" "/a/b")
assert_parity("/a" "")
assert_parity("" "")
# Host path model: backslashes, case, drive-relative paths, UNC root-names.
assert_parity("C:\\a" "C:\\a\\b")
assert_parity("c:/a" "C:/A/b")
assert_parity("C:/" "C:foo")
assert_parity("//host/share" "//host/share/a")
assert_parity("/a" "//a/b")
# Literal operands, not just variables.
if(NOT "/a/b" PATH_IS_PREFIX "/a/b/c")
message(SEND_ERROR "if(PATH_IS_PREFIX): literal operands rejected")
endif()
# NOT composes with the operator, which is the motivating guard idiom.
set(guard_ok FALSE)
if(NOT "/a/b" PATH_IS_PREFIX "/a/bc")
set(guard_ok TRUE)
endif()
if(NOT guard_ok)
message(SEND_ERROR "if(NOT <prefix> PATH_IS_PREFIX <path>) did not compose")
endif()
# Works in an elseif() chain, which a helper function could not.
if("/a" PATH_IS_PREFIX "/x/y")
message(SEND_ERROR "if(PATH_IS_PREFIX): '/a' matched '/x/y'")
elseif("/a/b" PATH_IS_PREFIX "/a/b/c")
else()
message(SEND_ERROR "elseif(<prefix> PATH_IS_PREFIX <path>) did not match")
endif()
# The motivating case: a prefix that if(MATCHES) cannot express.
set(prefix "/proj/lib+ssl(v2)/inc.d")
if(NOT "${prefix}" PATH_IS_PREFIX "${prefix}/f.h")
message(SEND_ERROR "if(PATH_IS_PREFIX): regex metacharacters in prefix")
endif()
# The keyword is consumed as an operator only with both a preceding and a
# following token, so it stays usable as a variable otherwise. Neither
# condition may be written with a leading NOT: that would put the keyword in
# the operator slot and make these a hard error.
set(PATH_IS_PREFIX "yes")
if(PATH_IS_PREFIX)
else()
message(SEND_ERROR "if(PATH_IS_PREFIX): unary use broken")
endif()
if(PATH_IS_PREFIX STREQUAL "yes")
else()
message(SEND_ERROR "if(PATH_IS_PREFIX STREQUAL ...): left-operand use broken")
endif()