Browse files

ENH: ExternalData: Preserve escaped semicolons during argument expansion

The CMake language implicitly flattens lists so a ";" in a list element
must be escaped with a backslash.  List expansion removes backslashes
escaping semicolons to leave raw semicolons in the values.  Teach
ExternalData_Add_Test and ExternalData_Expand_Arguments to re-escape
semicolons found in list elements so the resulting argument lists work
as if constructed directly by the set() command.

For example:

  ExternalData_Add_Test(Data NAME test1 COMMAND ... "a\\;b")
  ExternalData_Expand_Arguments(Data args2 "c\\;d")
  add_test(NAME test2 COMMAND ... ${args2})

should be equivalent to

  set(args1 "a\\;b")
  add_test(NAME test1 COMMAND ... ${args1})
  set(args2 "c\\;d")
  add_test(NAME test2 COMMAND ... ${args2})

which is equivalent to

  add_test(NAME test1 COMMAND ... "a;b")
  add_test(NAME test2 COMMAND ... "c;d")

Note that it is not possible to make ExternalData_Add_Test act exactly
like add_test when quoted arguments contain semicolons because the CMake
language flattens lists when constructing function ARGN values.  This
re-escape approach at least allows test arguments to have semicolons.

While at it, teach ExternalData APIs to not transform "DATA{...;...}"
arguments because the contained semicolons are non-sensical.

Change-Id: I6ebd1e6a21a6621491343b0e85bf1502a3ae19f6
Suggested-by: Jean-Christophe Fillion-Robin <>
  • Loading branch information...
bradking committed Mar 12, 2013
1 parent d42c3ab commit 5a11039b1cbd5c22e9217c3d67f8bf855cb6e8c7
Showing with 12 additions and 5 deletions.
  1. +12 −5 CMake/ExternalData.cmake
@@ -172,7 +172,8 @@
function(ExternalData_add_test target)
- ExternalData_expand_arguments("${target}" testArgs ${ARGN})
+ # Expand all arguments as a single string to preserve escaped semicolons.
+ ExternalData_expand_arguments("${target}" testArgs "${ARGN}")
@@ -250,13 +251,17 @@ endfunction()
function(ExternalData_expand_arguments target outArgsVar)
# Replace DATA{} references with real arguments.
- set(data_regex "DATA{([^{}\r\n]*)}")
+ set(data_regex "DATA{([^;{}\r\n]*)}")
set(other_regex "([^D]|D[^A]|DA[^T]|DAT[^A]|DATA[^{])+|.")
set(outArgs "")
+ # This list expansion un-escapes semicolons in list element values so we
+ # must re-escape them below anywhere a new list expansion will occur.
foreach(arg IN LISTS ARGN)
if("x${arg}" MATCHES "${data_regex}")
+ # Re-escape in-value semicolons before expansion in foreach below.
+ string(REPLACE ";" "\\;" tmp "${arg}")
# Split argument into DATA{}-pieces and other pieces.
- string(REGEX MATCHALL "${data_regex}|${other_regex}" pieces "${arg}")
+ string(REGEX MATCHALL "${data_regex}|${other_regex}" pieces "${tmp}")
# Compose output argument with DATA{}-pieces replaced.
set(outArg "")
foreach(piece IN LISTS pieces)
@@ -270,11 +275,13 @@ function(ExternalData_expand_arguments target outArgsVar)
set(outArg "${outArg}${piece}")
- list(APPEND outArgs "${outArg}")
# No replacements needed in this argument.
- list(APPEND outArgs "${arg}")
+ set(outArg "${arg}")
+ # Re-escape in-value semicolons in resulting list.
+ string(REPLACE ";" "\\;" outArg "${outArg}")
+ list(APPEND outArgs "${outArg}")
set("${outArgsVar}" "${outArgs}" PARENT_SCOPE)

0 comments on commit 5a11039

Please sign in to comment.