Why can't I re-export an imported executable?

I want to make Halide’s build support cross-compilation.

  • Host Halide depends on:
    • Host LLVM libraries
    • Host LLVM tools: llvm-as and clang
  • Target Halide depends on:
    • Host Halide tools
    • Host LLVM tools: llvm-as and clang
    • Target LLVM libraries

To pass the host Halide tools to target Halide, there’s a simple solution:

  1. Organize Halide’s tools into a directory that export()s them into a package called HalideHost.
  2. In the parent directory, check if CMAKE_CROSSCOMPILING is set.
    1. If so, find_package(HalideHost)
    2. Otherwise, add_subdirectory(tools).
  3. Set HalideHost_ROOT to the host build directory in the target configure command line.

On the other hand, getting both LLVM host tools and LLVM target libraries is challenging. Calling find_package(LLVM) will always return either one or the other.

My desired solution was to export() the host LLVM tools under the HalideHost:: namespace and use aliases in the host build for consistency. Unfortunately, export() reports a bogus error that it can’t determine the linker language for the imported executable. Setting IMPORTED_LINK_INTERFACE_LANGUAGES to CXX does not resolve this.

My workaround is to use file(GENERATE) to do what I expected export() to do:

     OUTPUT "${CMAKE_BINARY_DIR}/share/HalideHost/HalideHostConfig.cmake"
     CONTENT [[

add_executable(HalideHost::llvm-as IMPORTED)

Why can’t export() do this, and why would it need to know the linker language for an imported executable?

Moreover, is this a good solution? If not, what should I do instead?

VTK has a similar problem. The codebase has an alternate “mode” where it compiles just the tools needed for cross-compilation and exports them under a new namespace (VTKCompileTools::). Everything that uses them first looks for these names and only then falls back to the VTK:: name for them. This allows the selection to be made at the top-level.

As for exporting other project’s targets, I think I would recommend instead just making your own INTERFACE targets which do this (though add_executable(INTERFACE) isn’t really a thing today, so is of little consolation with existing CMake releases). It is basically this issue except that it was compiled (elsewhere).

Yeah, so it’s only executables that I want to do this with. So I think that my solution here is fine, especially given the fact that both the host and target LLVMs must be the same version (llvm-as and clang -emit-llvm are not compatible across major versions, anyway).