Save CMake function parameter forwarding

Is it generally possible to safely forward function parameters? I know that ${ARGV} is less than ideal since it flattens lists, etc. So I am looking for a solution that doesn’t have the same issues.

More specifically I am analyzing the CMake trace output, and I want to keep track of the CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR at the same time (variable_watch doesn’t seem to work for these for some reason).

The best solution I have come up with so far is to override the functions in question and insert some code to keep track of the variables manually. See https://github.com/mesonbuild/meson/blob/master/mesonbuild/cmake/data/preload.cmake. This works OK-ish for add_custom_{command,target}, however, I also want to add set_property and set_source_files_properties, where ${ARGV} will break for arbitrary projects.

If possible, the solution should work for CMake 3.7+ (since 3.7 is the first version with the CMake server). And yes, I am aware that the _original_function behavior is (currently) undocumented.

cmake_parse_arguments(PARSE_ARGV 0) may help here. It might still collapse list arguments, but it should be able to “see” quoting and handle that properly at least.

Thanks for the response, but how do I pass the result of cmake_parse_arguments to set_source_files_properties without flattening the list (the actual properties are passed as key/value paired lists)?

Example:

function(set_source_files_properties)
    set(options)
    set(oneValueArgs)
    set(multiValueArgs PROPERTIES)
    cmake_parse_arguments(PARSE_ARGV 0 MY_FPROP "${options}" "${oneValueArgs}" "${multiValueArgs}")
                            
    _set_source_files_properties(${PARSE_ARGV_UNPARSED_ARGUMENTS} PROPERTIES ${PARSE_ARGV_PROPERTIES})
endfunction()

set_source_files_properties(foo.cpp PROPERTIES HEADER_FILE_ONLY ON COMPILE_FLAGS "-Dasd=C;M;a;k;e")

As far as I can tell, the above example would still flatten the properties in PARSE_ARGV_PROPERTIES.

Hmm. Indeed. Maybe set_property is the better thing to do here? You may need to just iterate over the ARGN list and build up a list of quoted arguments you then pass down to set_source_files_properties.

Yeah, that function wouldn’t be a problem, however, I have no control over what function is used. The script in the link is injected via CMAKE_PROJECT_INCLUDE and should work for any project. More specifically I want to track the HEADER_FILE_ONLY property since there is no way to get this information out of the server API (which we still have to support).

Ah, OK. Then I suggest iterating over ARGN yourself and calling set_property internally with quoted arguments. Unfortunately, it sounds like you need to support an older CMake than has foreach (key value IN LISTS ARGN), so you’ll have to do the pairing manually.

Thanks, I didn’t think about converting set_source_files_properties to set_property.

For others following this thread, the issues with argument forwarding and list flattening are discussed in the following article:

Since that was written, the cmake_command() command has landed in CMake master. There could potentially be a way to use that to do argument forwarding without the list flattening (specifically the cmake_command(EVAL CODE...) form). It could be a bit convoluted, but perhaps possible. Not useful for this query, but worth keeping in mind for anyone else stumbling across this thread sometime in the future.

For anyone interested, this is the set_source_files_properties to set_property conversion function I came up with:

function(set_source_files_properties)
  set(FILES)
  set(I 0)
  set(PROPERTIES OFF)

  while(I LESS ARGC)
    if(NOT PROPERTIES)
      if("${ARGV${I}}" STREQUAL "PROPERTIES")
        set(PROPERTIES ON)
      else()
        list(APPEND FILES "${ARGV${I}}")
      endif()

      math(EXPR I "${I} + 1")
    else()
      set(ID_IDX ${I})
      math(EXPR PROP_IDX "${ID_IDX} + 1")

      set(ID   "${ARGV${ID_IDX}}")
      set(PROP "${ARGV${PROP_IDX}}")

      set_property(SOURCE ${FILES} PROPERTY "${ID}" "${PROP}")
      math(EXPR I "${I} + 2")
    endif()
  endwhile()
endfunction()

This may be made simpler with foreach (I IN LISTS ARGN) rather than math(EXPR), but that’s more a style thing.

Thanks, but with foreach (I IN LISTS ARGN) the “group two parameters” logic will be more anoying.