try_compile/try_run link against target

I’m executing a try_run command where I need to link against a target. My initial hope was that it would act like target_link_libraries and take things like the include directories and compiler flags and add them to the compilation.

Apparantly it does none of this, it only adds a link flag and thus compilation fails. I think to make this work I’ll need to do the following:

  • Retrieve all the include paths and compiler flags from the target.
  • Recurse through all dependencies the target has and retrieve those flags as well.
  • Feed these flags to try_run like -DCMAKE_FLAGS=-DINCLUDE_DIRECTORIES...

The first one is definitely doable I think, but I cannot find a way to get the dependencies a target has (e.g. introduced using a target_link_libraries). Is this possible? Am I missing another obvious way of doing this?

Using imported targets with try_compile and try_run is difficult. Maybe we could dump all imported targets into the test project, but knowing which targets need to go is the difficult part.

Cc: @brad.king

Well, elsewhere CMake automatically detects when it’s a target and will act accordingly.

So, if you do something like this:

target_link_libraries(my_executable PUBLIC abc)

Then, if CMake knows abc as a target it will correctly use things like include paths and other options on the target for my_executable, but if it’s not then it simply adds a linker option, like -labc for most linkers. It’s probably best to use that same behaviour for try_compile and try_run.

Otherwise we could add another option to those functions, instead of LINK_LIBRARIES something like LINK_TARGETS or USE_TARGETS. For consistency, though, I’d just do it the same way as with target_link_libraries.

CMake would need to push the knowledge of the imported targets into the generated project for this to work. @Matthew_Woehlke, do you know how much work this might be?

It’s not just imported targets. I’m trying to use it with a mix of targets - some of which are regular libraries I define just above.

My goal is to write a set of generated files, the first of which includes a header that defines a given class and then writes the size of the class in bytes to std::cout. That information is then used to create a header that forward-declares the class and defines a buffer of the right size to hold. The class is then constructed using placement-new. That way you can reap the compiler speedup of forward declarations without incurring the overhead of pointer indirection.

Why not something like extern size_t sizeofClass; and then size_t sizeofClass = sizeof(Class); in the implementation of Class?

I’m trying to use [try_run] with […] regular libraries I define just above.

Well, that’s not going to work. try_run happens at configure time; your own libraries don’t exist then. Depending on how you’re using the result, you might be better off refactoring to use a custom command rather than try_run. (If I understand your use-case correctly, this sounds like the right approach, if Ben’s approach won’t work.)

I don’t know how much work it would be to export targets to try_compile/try_run. It would depend on whether we write everything (which will slow down all calls even though almost none will use all imported targets), or only targets that are needed.

I don’t think this will work; only the compiler can truly answer some sizeof questions and then the command won’t be part of target the class is a part of because it needs linked before you can use a compile command to answer such questions…

This is an unsolvable problem with link usage requirements like $<$<C_COMPILER:GNU>:iberty> which (perhaps misguidedly) ask the C compiler’s name and add a link to iberty even if the target itself is C++ or whatever.

That won’t work, unfortunately. The size needs to be a compile-time constant. So it works when defining something like constexpr std::size_t sizeOfClass = ...;, but that cannot be extern.

Maybe I’m missing something here, but when it comes to compiling sources in a target we don’t need all linked libraries to be linked at that point, right? We just need to know which include paths and compiler flags they set.

Only for linking the final library do we need all dependencies to be fully linked. To know the sizeof for a class I don’t need the libraries to be linked, either. I just need to know the right compiler flags to use so that I get the correct answer from sizeof. That’s basically what CMake already does when you’re compiling sources, right?

CMake does not yet have a way to say this. I’ve wanted a $<COMPILE_ONLY> genex, but it doesn’t exist yet. Even so, the safe route is to wait for it to have linked so that any generated sources or headers are guaranteed to exist.

CMake knows how to give it to a compiler, but not an arbitrary tool (like codegen). See one way of doing something like it in this code.