How to connect custom command to a target (add_library) in a parent directory

Hi all,

I have a few add_custom_commands that generate files (e.g. via flex_target). Those files are used as input by an add_library command. The CMake documentation is quite clear in that, for the add_custom_command magic to happen, the command and the target should be in the same CMakeLists.txt (emphasis mine):

A target created in the same directory (CMakeLists.txt file) that specifies any output of the custom command as a source file is given a rule to generate the file using the command at build time.

If I wanted to move those add_custom_commands to CMakeLists.txt into different subdirectories, would it be any way of still keeping the add_custom_command functionality/magic? E.g. this is what I’d like to have:

    add_library(blah OBJECT ${files_1} ${files_2})
    add_custom_command(OUTPUT files_1)
    add_custom_command(OUTPUT files_2)


I think you’d need to inject an add_custom_target() that DEPENDS on the files and then depend on them from the blah target.

Thanks for the answer, Ben.

I may have done something wrong, but the following didn’t work for me:

    add_library(blah OBJECT ${files_1})
    add_custom_command(OUTPUT files_1)
    set(files_1 ${files_1} PARENT_SCOPE)
    add_custom_target(generate_files_1 DEPENDS ${files_1})

Does it work if you flip these lines? set(PARENT_SCOPE) has some weird behavior on local variables (namely, it sets the value you request to the parent scope, but it also pulls the previous state down into the current scope).

Unfortunately not!

@ben.boeckel Is there an issue open about this? This is the first I’ve heard of that behavior.

I don’t think so; it’s Just the Way it’s Been™. A policy is probably useful, but it will have to be one of those silent policies and it is highly likely to actually cause subtle differences. The docs do cover this behavior:

In that case, I’d have to sit down and investigate it more. I’m afraid I don’t have time for that though right now :frowning: . Could you please file an issue outlining the problem with a small example so that it’s easier to find. I suspect this will likely just end up as a documentation update to mention the limitation, but even that is an improvement.

I read that differently to the way you seem to be interpreting it. To me, that says set(var someValue PARENT_SCOPE) leaves the value of var untouched in the current scope. And indeed, this is the behavior I see, as confirmed with the following smaller demonstrator (run this with cmake -P):

cmake_minimum_required(VERSION 3.25)

set(var "first")

    set(var "second")
    set(var "third" PARENT_SCOPE)
    message(STATUS "var = ${var}")

This outputs second, as I would expect.

Ok, so we agree, but we’re thinking about it from opposite directions :slight_smile: . In terms of implementation, it pulls the cmDefinition down into the current scope and then tells the parent scope about the new value.

This code would set files_1 in the parent scope and pull its definition “down”, probably leaving an empty/undefined/stale value to end up there for the add_custom_target command.

I still don’t follow this, and find it a bit confusing. From a user’s perspective, there is no “pulling down” like you describe. The variables in the current scope are copied from the parent when the current scope was first created. Any subsequent call to set(files_1 ... PARENT_SCOPE) should only be affecting the files_1 variable in the parent scope, there should be no effect on variables in the current scope. I don’t see where any “pulling down” is occurring.

Maybe my mental model is poisoned from when I attempted optimizations in this area. I’ll have to experiment again.

Yes, this is probably it. I had once attempted to make variable lookups go up the scope parentage looking for variables rather than copying them wholesale at scope creation (IIRC, I had tried to pull used definitions down to avoid chasing the chain all the time as well). The expected behaviors of PARENT_SCOPE had thrown a wrench into things somehow and it never went anywhere and this is where the “pull down” in my mind distilling probably comes from.

Looking at the code, it seems that it was eventually done, but it was just a quick glance.