Using a function to add items to a list

I’m trying to write a CMake function that takes a list as an argument, and that adds items to the list, and then returns so that the caller can use the modified list.

It sounds simple but I can’t get it to work. Here’s my best effort so far:

function(appendToList THE_LIST)

    message("On entering appendToList, THE_LIST = ${THE_LIST}")
    list(APPEND THE_LIST "item2")
    list(APPEND THE_LIST "item3")
    
    set(${THE_LIST} "${${THE_LIST}}" PARENT_SCOPE)

endfunction()

set(MY_LIST "item1")
appendToList(MY_LIST)

list(LENGTH MY_LIST nList)
message("On return from appendToList, the length of MY_LIST is ${nList}:")
foreach(item IN LISTS MY_LIST)
    message("- ${item}")
endforeach()

I’d expect the output to be:

On entering appendToList, THE_LIST = item1
On return from appendToList, the length of MY_LIST is 3:
- item1
- item2
- item3

…but what I actually get is:

On entering appendToList, THE_LIST = MY_LIST
On return from appendToList, the length of MY_LIST is 3:
- item2
- item3
-

Where am I going wrong?

You pass the list name to the function, so you have to evaluate the variable THE_LIST to get your list:

function(appendToList THE_LIST)

    message("On entering appendToList, THE_LIST = ${${THE_LIST}}")
    list(APPEND ${THE_LIST} "item2")
    list(APPEND ${THE_LIST} "item3")
    
    set(${THE_LIST} "${${THE_LIST}}" PARENT_SCOPE)

endfunction()

And you can simplify the propagation of the result by using return(PROPAGATE) (require CMake 3.25 or upper).

function(appendToList THE_LIST)

    message("On entering appendToList, THE_LIST = ${${THE_LIST}}")
    list(APPEND ${THE_LIST} "item2")
    list(APPEND ${THE_LIST} "item3")
    
    return(PROPAGATE ${THE_LIST})

endfunction()

Try with

function(appendToList THE_LIST)

    message("On entering appendToList, THE_LIST = ${THE_LIST}")
    list(APPEND ${THE_LIST} "item2")
    list(APPEND ${THE_LIST} "item3")
    
    set(${THE_LIST} "${${THE_LIST}}" PARENT_SCOPE)

endfunction()

Thank you, yes, that works.

A further postscript to this, if I may…

In my ‘real application’ (not the simple case cooked up for this question), I was having a second issue which led to an extra stray empty entry being added to the returned list.

The problem turned out to be that I was using the same variable name for both the list itself in the parent scope, and the argument of the function.

So, in the example above, using THE_LIST as the name of the function argument, whereas MY_LIST is the name of the list in the parent scope, everything works as expected.

But suppose I now change the function so that the argument is called MY_LIST instead:

function(appendToList MY_LIST)

    message("On entering appendToList, THE_LIST = ${${MY_LIST}}")
    list(APPEND ${MY_LIST} "item2")
    list(APPEND ${MY_LIST} "item3")
    
    set(${MY_LIST} "${${MY_LIST}}" PARENT_SCOPE)

endfunction()

set(MY_LIST "item1")
appendToList(MY_LIST)

list(LENGTH MY_LIST nList)
message("On return from appendToList, the length of MY_LIST is ${nList}:")
foreach(item IN LISTS MY_LIST)
    message("- ${item}")
endforeach()

Then things go horribly wrong:

On entering appendToList, THE_LIST = MY_LIST
On return from appendToList, the length of MY_LIST is 4:
- item2
- item2
- item3
-

So it turns out that it’s very important to ensure that functions are not invoked using variables which have the same name as their parameters. I don’t recall seeing this documented anywhere.