I am building a library for an embedded target with a cross-toolchain. To generate this library, I must create another binary before with my host compiler. This binary generates some headers for my library. I find everywhere that I cannot use two different compilers in the same CMakeLists.txt
file. CMake was not designed to do that.
So I am trying to do this with a subdirectory and 2 CMakeLists.txt
. I have a main CMakeLists.txt
file building the library. There is a add_subdirectory
call in this main CMakeLists.txt
file to generate my binary in a separate cmake file. The problem is that I cannot tell my main Cmake to do the subdirectory build before the main one.
My problem in a very minimalist example:
[arthur ] ls
CMakeLists.txt my_subdir source.c
[arthur ] ls my_subdir/
CMakeLists.txt main.c
The top cmake file:
[arthur ] cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
message("Invoke my_subdir/CMakeList.txt")
add_subdirectory (my_subdir)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_VERBOSE_MAKEFILE ON)
#set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1)
project(doing_test)
# Build the great library
set(MY_GREAT_LIBRARY_SOURCE source.c)
add_library(great_library STATIC ${MY_GREAT_LIBRARY_SOURCE})
target_include_directories(great_library PRIVATE PUBLIC ".")
set_target_properties(great_library PROPERTIES OUTPUT_NAME my_super_great_library)
the subdirectory cmake file:
[arthur ] cat my_subdir/CMakeLists.txt
project (coucou)
message("Building mysubdir sources...")
# add the executable
add_executable(my_needed_subbinary main.c)
Building:
[arthur ] mkdir build && cd build
[arthur ] cmake .. && cmake --build .
Invoke my_subdir/CMakeList.txt
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
Building mysubdir sources...
-- Configuring done
-- Generating done
-- Build files have been written to: /home/arthur/work/repository/cmake_test/build
/usr/bin/cmake -S/home/arthur/work/repository/cmake_test -B/home/arthur/work/repository/cmake_test/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/arthur/work/repository/cmake_test/build/CMakeFiles /home/arthur/work/repository/cmake_test/build/CMakeFiles/progress.marks
/usr/bin/make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/arthur/work/repository/cmake_test/build'
/usr/bin/make -f CMakeFiles/great_library.dir/build.make CMakeFiles/great_library.dir/depend
make[2]: Entering directory '/home/arthur/work/repository/cmake_test/build'
cd /home/arthur/work/repository/cmake_test/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/arthur/work/repository/cmake_test /home/arthur/work/repository/cmake_test /home/arthur/work/repository/cmake_test/build /home/arthur/work/repository/cmake_test/build /home/arthur/work/repository/cmake_test/build/CMakeFiles/great_library.dir/DependInfo.cmake --color=
Scanning dependencies of target great_library
make[2]: Leaving directory '/home/arthur/work/repository/cmake_test/build'
/usr/bin/make -f CMakeFiles/great_library.dir/build.make CMakeFiles/great_library.dir/build
make[2]: Entering directory '/home/arthur/work/repository/cmake_test/build'
[ 25%] Building C object CMakeFiles/great_library.dir/source.c.o
/usr/bin/cc -I/home/arthur/work/repository/cmake_test/. -o CMakeFiles/great_library.dir/source.c.o -c /home/arthur/work/repository/cmake_test/source.c
[ 50%] Linking C static library libmy_super_great_library.a
/usr/bin/cmake -P CMakeFiles/great_library.dir/cmake_clean_target.cmake
/usr/bin/cmake -E cmake_link_script CMakeFiles/great_library.dir/link.txt --verbose=1
/usr/bin/ar qc libmy_super_great_library.a CMakeFiles/great_library.dir/source.c.o
/usr/bin/ranlib libmy_super_great_library.a
make[2]: Leaving directory '/home/arthur/work/repository/cmake_test/build'
[ 50%] Built target great_library
/usr/bin/make -f my_subdir/CMakeFiles/my_needed_subbinary.dir/build.make my_subdir/CMakeFiles/my_needed_subbinary.dir/depend
make[2]: Entering directory '/home/arthur/work/repository/cmake_test/build'
Scanning dependencies of target my_needed_subbinary
make[2]: Leaving directory '/home/arthur/work/repository/cmake_test/build'
/usr/bin/make -f my_subdir/CMakeFiles/my_needed_subbinary.dir/build.make my_subdir/CMakeFiles/my_needed_subbinary.dir/build
make[2]: Entering directory '/home/arthur/work/repository/cmake_test/build'
[ 75%] Building C object my_subdir/CMakeFiles/my_needed_subbinary.dir/main.c.o
[100%] Linking C executable my_needed_subbinary
make[2]: Leaving directory '/home/arthur/work/repository/cmake_test/build'
[100%] Built target my_needed_subbinary
make[1]: Leaving directory '/home/arthur/work/repository/cmake_test/build'
/usr/bin/cmake -E cmake_progress_start /home/arthur/work/repository/cmake_test/build/CMakeFiles 0
This is a basic need, but I cannot resolve my issue. I want to build my main.c
to generate my subdirectory binary before building the main library. We can see in the output that he is calling the CMakeLists.txt
of the subdirectory first but without building it.
I tried to add add_dependencies
command in the top cmake but without success.
add_dependencies(great_library my_subdir) -> don't know my_subdir project
add_dependencies(great_library coucou) -> don't know coucou project
Solution inspired from @KamilCuk. I also change some names to have a more understandable example.
The top Cmake:
cmake_minimum_required(VERSION 3.10)
project(multiple_compiler_example)
# use cross toolchain for this project
set(CMAKE_C_COMPILER "arm-linux-gnueabi-gcc")
# define the header that the intermediate binary is generated. The header are generated in header_autogenerated directory
set(MY_GENERATED_HEADER
"${CMAKE_CURRENT_SOURCE_DIR}/header_autogenerated/my_generated_header1.h"
"${CMAKE_CURRENT_SOURCE_DIR}/header_autogenerated/my_generated_header2.h"
)
# Cmake command to build the binary which generater the header
# compile in build directory and generate output binary in bin directory
add_custom_command(
COMMENT "Generate header generator binary"
DEPENDS
header_generator/header_generator.c
header_generator/CMakeLists.txt
OUTPUT
${MY_GENERATED_HEADER}
COMMAND
${CMAKE_COMMAND}
-B ${CMAKE_CURRENT_SOURCE_DIR}/header_generator/build
-S ${CMAKE_CURRENT_SOURCE_DIR}/header_generator
-D CMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}/header_generator/bin
COMMAND
${CMAKE_COMMAND}
--build ${CMAKE_CURRENT_SOURCE_DIR}/header_generator/build
)
# Cmake code to run header_generator before compiling the library to generate all header
add_custom_target(generate_parameter_header
COMMAND ./header_generator
DEPENDS ${MY_GENERATED_HEADER}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/header_generator/bin
COMMENT "run header_generator...")
# my great library
add_library(my_great_library
source_library1.c
source_library2.c
source_library3.c
# List the needed dependencies. ONE BY ONE
${CMAKE_CURRENT_SOURCE_DIR}/header_autogenerated/my_generated_header1.h
${CMAKE_CURRENT_SOURCE_DIR}/header_autogenerated/my_generated_header2.h
)
target_include_directories(my_great_library PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/header_autogenerated
.
)
add_dependencies(my_great_library generate_parameter_header)
My subdirectory Cmake :
project (header_generator)
# always use the x86 host compiler
set(CMAKE_C_COMPILER "gcc")
message("Building the header generator...")
# add the executable
add_executable(header_generator header_generator.c)
target_include_directories(header_generator PUBLIC "${PROJECT_BINARY_DIR}" ".")
The subproject that's a completely different cmake. So run a completely different cmake.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(doing_test)
add_custom_command(
COMMENT "generate the includes that I wanna"
DEPENDS
my_subdir/main.c
my_subdir/CMakeLists.txt
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes/include1.h
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes/include2.h
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes/etc.h
COMMAND
${CMAKE_COMMAND}
-B ${CMAKE_CURRENT_BINARY_DIR}/my_subdir
-S ${CMAKE_CURRENT_SOURCE_DIR}/my_subdir
-D CMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/my_subdir/bin
COMMAND
${CMAKE_COMMAND}
--build ${CMAKE_CURRENT_BINARY_DIR}/my_subdir
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/bin/my_needed_subbinary
--outputdir ${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes
)
add_library(great_library
source.c
# List the needed dependencies. ONE BY ONE
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes/include1.h
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes/include2.h
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes/etc.h
)
target_include_directories(great_library PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/my_subdir/genincludes
.
)