gcccmake

How to create and use partially linked object files in CMake?


I am trying to create kernel module for VxWorks. Building simple executable it in my project is a multistep process:

  1. compile source files
  2. use partial linking of the compiled object files, so it creates one object file
  3. run custom script on partially linked object file to create created_by_script.c file
  4. compile created_by_script.c file and link it with previously built partially linked object file

Partially linked object file - multiple object files linked with -r flag. What I'm having problem with is creating partially linked object file in CMake without using custom commands, which would be unreadable as it would require manually writing linking command. Is there a clean way to do it?

CMake version is 3.25.2, but if newer version solves issues, it might be possible to upgrade.

What I've tried:

add_executable(${TARGET_NAME} all_sources.cpp)
  set_target_properties(${TARGET_NAME} PROPERTIES
  RUNTIME_OUTPUT_DIRECTORY "${TARGET_TEMP_DIR}"
  SUFFIX ".o"
  ENABLE_EXPORTS TRUE
)

...(adding custom target to run the script in here)...

add_executable(${FINAL_TARGET}
  created_by_script.c
  $<TARGET_FILE:${TARGET_NAME}>
)
target_link_libraries(${FINAL_TARGET}
  ${TARGET_NAME}
)
add_dependencies(${FINAL_TARGET} ${TARGET})

Problem is

  1. linking against executable doesn't add it to the source files
  2. using generator expression $<TARGET_FILE:${TARGET_NAME}> returns error because the source file doesn't exist (because it would be created by other target, so it doesn't exist during configuration)

Now if I run the build without the generator expression for the 1st time, then add it and run it 2nd time, everything works, but I need to run building process 2 times.

Is there a clean way to create partially linked object file in CMake which can be added later to the executable or to make that generator expression inside source files work?


Solution

  • You were almost there.

    Given:

    $ tree .
    .
    ├── aa.c
    ├── bb.c
    ├── CMakeLists.txt
    ├── dd.c
    └── main.c
    

    where:

    $ tail -n +1 aa.c bb.c dd.c 
    ==> aa.c <==
    #include <stdio.h>
    
    void aa(void)
    {
        puts(__func__);
    }
    
    ==> bb.c <==
    #include <stdio.h>
    
    void bb(void)
    {
        puts(__func__);
    }
    
    ==> dd.c <==
    #include <stdio.h>
    
    void dd(void)
    {
        puts(__func__);
    }
    

    are specimen source files to be compiled and then incrementally/partially linked into an object file bigobj.o, which is to be linked into a program prog with additional source:

    $ cat main.c 
    extern void aa(void);
    extern void bb(void);
    extern void dd(void);
    
    int main(void)
    {
        aa(); bb(); dd();
        return 0;
    }
    

    then CMakeLists.txt can be roughly like this:

    $ cat CMakeLists.txt 
    cmake_minimum_required(VERSION 3.25)
    project(prog)
    
    add_executable(bigobj aa.c bb.c dd.c)
        set_target_properties(bigobj PROPERTIES
        SUFFIX .o
        ENABLE_EXPORTS TRUE
    )
    target_link_options(bigobj PRIVATE -r)
    
    add_executable(prog main.c)
    target_link_directories(prog PRIVATE ${CMAKE_BINARY_DIR})
    target_link_libraries(prog :bigobj.o)
    

    Which generates the build:

    $ mkdir build
    $ cd build/
    $ cmake ..
    -- The C compiler identification is GNU 13.3.0
    -- The CXX compiler identification is GNU 13.3.0
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: /usr/bin/cc - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: /usr/bin/c++ - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done (0.4s)
    -- Generating done (0.0s)
    -- Build files have been written to: /home/imk/develop/so/cmake_prob/build
    $ make VERBOSE=1 | grep -A2 'Linking C executable prog'
    [100%] Linking C executable prog
    /usr/bin/cmake -E cmake_link_script CMakeFiles/prog.dir/link.txt --verbose=1
    /usr/bin/cc CMakeFiles/prog.dir/main.c.o -o prog   \
        -L/home/imk/develop/so/cmake_prob/build  \
        -Wl,-rpath,/home/imk/develop/so/cmake_prob/build -l:bigobj.o 
        
    

    (link line split for readability), and the expected program:

    $ ./prog
    aa
    bb
    dd
    

    This CMakeLists.txt is obviously not independent of the GNU toolchain, but is free of custom commands. I have:

    $ cmake --version
    cmake version 3.28.3