How to include output built elsewhere with add_subdirectory() - cmake

I am trying to build a ROM image using CMake. I have a project root directory CMakeLists.txt file which calls add_subdirectory() to build the different components for the ROM image. These components are output in binary format. Once this is done I wish to combine the resulting binaries into a single image.
Currently I have a final add_subdirectory() call that is meant to use objdump to convert the binary output of the other modules to an object file and then link these into a unified elf that is then converted with objdump.
add_executable(rom_image.bin)
foreach (COMPONENT ${COMPONENTS})
set(COMPONENT_BINARY_FILENAME "${CMAKE_BINARY_DIR}/${COMPONENT}/output.bin")
set(COMPONENT_OBJECT_FILENAME "${COMPONENT}.o")
add_custom_command(
TARGET rom_image.bin
PRE_BUILD
OUTPUT ${COMPONENT_OBJECT_FILENAME}
DEPENDS ${COMPONENT_BINARY_FILENAME}
COMMAND ${OBJCOPY_PROGRAM}
ARGS --input-target=binary --output-target=elf32-littlearm --binary-architecture=arm ${COMPONENT_BINARY_FILENAME} ${COMPONENT_OBJECT_FILENAME}
)
endforeach()
This does not work as CMake complains that the target (rom_image.bin) has no source files.

Related

Ninja Make system not accepting GENERATED command

I have a set of assembly files which should be compiled by a special compiler. After this it should be added to the library created by the compiler i have set in CMAKE_C_COMPILER. it was working fine with Mingw Makefile system but not working with Ninja Make.
Below is the code in cmakelists.txt
add_custom_target(
special_asm
COMMAND
${SPECIAL_ASM} ${src_file1}
-I${INCLUDE_PATH} -o file1.o
COMMAND
${SPECIAL_ASM} ${src_file2}
-I${INCLUDE_PATH} -o file2.o
)
add_custom_target(special_asm_cmd COMMAND cmd.exe special_asm*.bat)
add_dependencies(special_asm_cmd special_asm)
add_library(
mylib STATIC
file1.o
file2.o
${mylib_src})
add_dependencies(mylib special_asm_cmd)
set_source_files_properties(
file1.o
file2.o
PROPERTIES EXTERNAL_OBJECT true GENERATED true)
file1.o and file2.o are generated by different assembler. i have set the properties for these files also.
Problem 1:
custom target special_asm is not directly generating the object files. It is generating a batch script. That's why i have created one more custom target called special_asm_cmd to run the batch script which will generate the object files. The Mingw make system was directly generating the object file from special_asm but Ninja is not doing like that.
Problem 2
I have set the property GENERATED true for the special generated object files. But ninja is giving the below error. But Mingw Make was able to solve the dependency and no errors
ninja: error: '<path>/spt_init.o', needed by '<path>/libmylib.a', missing and no known rule to make it
There's no point in using GENERATED here - cmake will know it, when you tell him, how you generate the files anyway.
add_custom_command(
# Put generated files in binary dir, where they belong
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file1.o
# Enumerate all dependencies.
DEPENDS
${src_file1}
${INCLUDE_PATH}/header1.h ${INCLUDE_PATH}/header2.h etc..
COMMAND
${SPECIAL_ASM} ${src_file1} -I${INCLUDE_PATH}
-o ${CMAKE_CURRENT_BINARY_DIR}/file1.o
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file2.o
DEPENDS
${src_file2}
${INCLUDE_PATH}/header1.h ${INCLUDE_PATH}/header2.h etc..
COMMAND
${SPECIAL_ASM} ${src_file2}
-I${INCLUDE_PATH}
-o ${CMAKE_CURRENT_BINARY_DIR}/file2.o
)
add_custom_target(special_asm_cmd
# Enumerate the dependencies.
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/file2.o
${CMAKE_CURRENT_BINARY_DIR}/file1.o
# Glob in add_custom target? Use glob with `file(GLOB ...`
COMMAND cmd.exe special_asm*.bat
)
add_library(mylib STATIC
${CMAKE_CURRENT_BINARY_DIR}/file2.o
${CMAKE_CURRENT_BINARY_DIR}/file1.o
${mylib_src}
)
Cmake is a build system, specifically designed to know what comes from what. Files comes from other files plus commands. Tell cmake what files come from which commands and which source files - cmake will manage the dependencies and do all the rest like parallelizing the work and proper dependency tracking.

How do I condition the build of one target on the execution of another target's binary product?

I have a test code for my c project, which is built as part of my cmake project.
In order to run properly, the SW needs an input binary file.
The binary file is created from a designated .json file (which is in the test's directory) using a separate executable which is also part of the same cmake project.
Before the test is run (i.e. when building it) I need the bin-building executable to be built (easy enough with target dependency), but I also need the resulting executable to run with my .json as input.
What cmake functions enable this?
I tried building a custom command with the binary as output, but the test target doesn't know it needs the binary in order to run, can't accept the binary as a source, and a simple "add_dependancy" resulted in errors.
function(create_binary input_json dst_bin)
if (IS_WINDOWS)
set(exe_path ${OUTPUT_BIN_DIR}/BinaryGenerator.exe)
elseif(IS_LINUX)
set(exe_path ${OUTPUT_BIN_DIR}/BinaryGenerator)
endif()
add_custom_command(OUTPUT ${dst_bin}
COMMAND ${exe_path} ${input_json} ${dst_bin}
DEPENDS ${exe_path} ${dst_bin}
COMMENT STATUS "Creating bin file ${dst_bin} from ${input_json}"
)
endfunction(create_binary)
create_binary(InputParams.json ${OUTPUT_BIN_DIR}/InputParams.bin)
add_executable(MySwTest TestFile.cpp TestFile.h)
add_dependencies(${OUTPUT_BIN_DIR}/InputParams.bin)
I expected cmake to place a target dependency between my test target and the bin creator target, and also on the existence of the test's bin file, thus running the custom command in order to create it.
The actual output from cmake is "The dependency target InputParams.bin doesn't exist". I assume this is because "add_dependency" is meant to work only with targets.
Would adding a custom target including my binary solve this?
This is indeed solvable by defining a custom target that depends on the resulting binary.
When adding a custom command, the actual command is only called if the command's OUTPUT file is needed for another target - so if it's a source file, it will be called.
My command's output is a generated binary, and therefore is not a source file for my target.
However, using add_custom_target() I can create a target dependent on said bin file, and create a dependency between the end target and my custom target.
The working code for the above looks like this:
set(PARAMS_NAME InputParams)
set(INPUT_JSON ${PARAMS_NAME}.json)
set(DST_BIN ${PARAMS_NAME}.bin)
if (IS_WINDOWS)
set(EXE_PATH ${OUTPUT_BIN_DIR}/BinaryGenerator.exe)
elseif(IS_LINUX)
set(EXE_PATH ${OUTPUT_BIN_DIR}/BinaryGenerator)
endif()
add_custom_command(OUTPUT ${DST_BIN}
COMMAND ${EXE_PATH} ${INPUT_JSON} ${DST_BIN}
DEPENDS BinaryGenerator ${INPUT_JSON}
COMMENT STATUS "Creating bin file ${DST_BIN} from ${INPUT_JSON}"
)
add_custom_target(${PARAMS_NAME}BinGenerated DEPENDS ${DST_BIN})
add_executable(MySwTest TestFile.cpp TestFile.h)
add_dependencies(MySwTest ${PARAMS_NAME}BinGenerated)
By adding a dependency between MySwTest and my custom target, I ensure that the command generating my binary is called for each build.

CMake post-build-event: copy compiled libraries

The binary directory structure of my project is currently like this (Windows):
bin/mainProject/{Debug,Release}
bin/library1/{Debug,Release}
bin/library2/{Debug,Release}
...
bin/libraryN/{Debug,Release}
I'd like to copy the libraries library1lib.dll, ... libraryNlib.dll to the bin/mainProject/{Debug,Release} directory once they are build.
For CMake, I think this is doable using a post-build event, hence I've tried adding this to each of the libraries' CMakeLists.txt:
add_custom_command(TARGET library1 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/library1lib.dll
${CMAKE_BINARY_DIR}/mainProject/${CMAKE_BUILD_TYPE}/
)
Currently, there are two issues:
${CMAKE_BUILD_TYPE} seems to be not defined, at least I get an empty string for that variable in the output window.
Is there a possibility to make that post-build event more generic? Like replacing the actual dll name with some variable?
You can make this more generic by using generator expressions:
add_custom_command(
TARGET library1
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:library1>
$<TARGET_FILE_DIR:mainProject>/$<TARGET_FILE_NAME:library1>
)
Alternative
You could - if every dependency is build within your CMake project - also just give a common output path for all executables and DLLs with something like:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Out")
Note: The absolute path is required here because it would otherwise be relative to each targets default output path. And note that the configuration's sub-directory is appended by CMake automatically.
References
How to copy DLL files into the same folder as the executable using CMake?

Retrieve all link flags in CMake

In CMake, is it possible to programmatically retrieve the complete list of linker flags that will be used for a given target? The only way I can see to do this is to inspect the link.txt file in the target's CMakeFiles directory. Not ideal.
The use case that I'm interested in is to collect the data to include in something like a pkg-config file. I'm writing a library, and it includes a couple executable utilities that use the library. Building the executables (especially when the library is build statically) requires a non-trivial link line to link to my library and its dependencies. So I'd like to write out the link line necessary for building these executables to a data file included with the package such that other clients can know how to link.
As #Tsyvarev has commented there is no build-in command or property "to programmatically retrieve the complete list of linker flags" in CMake.
But inspired by your hint "so I'd like to write out the link line necessary for building these executables to a data file" I think I found a feasible solution (at least for makefile generators).
And if I understand your request correctly, we are not talking about simple verbose outputs like you get with e.g. CMAKE_VERBOSE_MAKEFILE, which would still need you to copy things manually.
So taking the following into account:
You need to run the generator first to get the real link line
CMake allows you to invent any linker language by name
You can define the link line with CMAKE_>LANG<_LINK_EXECUTABLE using variables and expansion rules
I came up with adding an LinkLine executable using my ECHO "linker" with the single purpose to create a link line file of my choosing:
set(CMAKE_ECHO_STANDARD_LIBRARIES ${CMAKE_CXX_STANDARD_LIBRARIES})
set(CMAKE_ECHO_FLAGS ${CMAKE_CXX_FLAGS})
set(CMAKE_ECHO_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS})
set(CMAKE_ECHO_IMPLICIT_LINK_DIRECTORIES ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
set(
CMAKE_ECHO_LINK_EXECUTABLE
"<CMAKE_COMMAND> -E echo \"<FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>\" > <TARGET>"
)
add_executable(LinkLine "")
target_link_libraries(LinkLine MyLibraryTarget)
set_target_properties(
LinkLine
PROPERTIES
LINKER_LANGUAGE ECHO
SUFFIX ".txt"
)
The nice thing about this approach is, that the output of my LinkLine target can be used as any other "officially generated" executable output (e.g. in install() commands or post-build steps with generator expressions):
add_custom_command(
TARGET LinkLine
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:LinkLine> PackageCfg/$<TARGET_FILE_NAME:LinkLine>
)
References
Recursive list of LINK_LIBRARIES in CMake
add_custom_command is not generating a target

CMake wrapper for new command-line tools

I'm trying to provide a simple CMake function to render PlantUML diagrams to PNG as part of my build process. The idea is that I have a bunch of .uml files containing PlantUML diagrams that I want to render to PNG as part of my build process. I would like to have a function similar to add_library() et. al. that renders any diagram for which the image file is older than the source file.
Using add_custom_command(), I came up with the following snippet:
#
# Create top-level target that renders a PlantUML diagram to a PNG image.
#
function(add_diagram target source)
# Program used to render the diagram.
set(plantuml java -jar ${PLANTUML_JARFILE})
# Diagram source file basename used to create output file name.
get_filename_component(output ${source} NAME_WE)
# Render the diagram and write an "${output}.png"
# file in the current binary folder.
add_custom_command(
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/${output}.png
COMMAND
${plantuml} -o ${CMAKE_CURRENT_BINARY_DIR} -tpng ${source}
MAIN_DEPENDENCY
${source}
COMMENT
"Rendering diagram '${output}'."
)
# Top-level target to build the output file.
add_custom_target(${target}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${output}.png)
endfunction()
And I invoke this function as:
add_diagram(foo ${CMAKE_CURRENT_SOURCE_DIR}/foo.uml)
where foo.uml is a file containing a PlantUML diagram. At a very basic level, this "works" in that it creates a named top-level target that I can build manually (e.g. using make foo, nmake foo, jom foo, etc.).
How can I add this target to the default target (all?) such that this is automatically built with the rest of the libraries and executables?
From the CMake documentation:
add_custom_target: Add a target with no output so it will always be built.
If the ALL option is specified it indicates that this target should be added to the default build target so that it will be run every time.
Dependencies listed with the DEPENDS argument may reference files and outputs of custom commands created with add_custom_command() in the same directory (CMakeLists.txt file).
If you are using Visual Studio, the only drawback is that it will create a new project for each target.