CUDA object does not get linked in C++/CUDA CMake project

I have a .cu file that implements a kernel and a wrapper function. I have a header exposing the wrapper function and a main.cpp file calling it. However when I build the project I get an Error LNK2019 unresolved external symbol "int __cdecl gpu_side::foo(int)" (?foo@gpu_side@@YAHH@Z) referenced in function main error with the wrapper fucntion.

Could anyone please help me understand what’s going on? This is my minimal (not) working example you can test and debug for yourself:

CMakeLists.txt

project (minimal_cuda LANGUAGES CXX)

add_executable(main_ex main.cpp)

set_target_properties(main_ex PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES)

enable_language(CUDA)

target_sources(main_ex PRIVATE gpu_side.cu)

set_target_properties(main_ex PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
set_target_properties(main_ex PROPERTIES CUDA_RESOLVE_DEVICE_SYMBOLS ON) 

main.cpp

#include <cstdio>
#include "gpu_side.h"

int main() {
  printf("Number is: %d\n", gpu_side::foo(5));
}

gpu_side.cu

#include "gpu_side.h"

namespace gpu_side {

__global__
void real_foo(int number, int *out) { 
    *out = number * 2; 
}

inline int foo(int number) { 
    int* x;
    
    cudaMallocManaged(&x, sizeof(int));
    real_foo<<<1,1>>>(number, x); 
    cudaDeviceSynchronize();
    int y = *x;
    cudaFree(x);
    return y;
}

}

gpu_side.h

#ifndef _GPU_SIDE_H_
#define _GPU_SIDE_H_

namespace gpu_side {

inline int foo(int number);

}
#endif

To build it I just open the folder containing these files with Visual Studio 2019 + CMake plugin, and hit “Build All”, or I use the command line cmake and ninja from the VS terminal.

I also posted on StackOverflow: visual studio - CUDA object does not get linked in CMake C++/CUDA project - Stack Overflow

Just for further info, the error also happens if I immediately enable CUDA and add the .cu file to the executable at target creation time, so that the CMakeLists.txt looks like this:

project (minimal_cuda LANGUAGES CXX CUDA)
cmake_minimum_required(VERSION 3.10)

add_executable(main_ex main.cpp gpu_side.cu)

set_target_properties(main_ex PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES)

set_target_properties(main_ex PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
set_target_properties(main_ex PROPERTIES CUDA_RESOLVE_DEVICE_SYMBOLS ON)

So while I need CUDA and the file to be work when added at a later time, I do not think that the PRIVATE keyword in the target_sources statement is the issue here.