How to execute a shell script using CMake post_build? - c

How to execute a shell script using CMake? The command that should be run is my_script that should be executed after build. The CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(abc)
include_directories("/usr/lib/avr/include")
set(CMAKE_CURRENT_SOURCE_DIR /home/user/Desktop)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmcu=atmega8")
set(SOURCE_FILES main.c)
add_executable(abc ${SOURCE_FILES})
#not working ----->
add_custom_command(TARGET abc
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E my_script
)
DISCLAIMER
Yes, there are similar questions in SO here, here, here, etc; however, they don't give me a clear vision how this can be achieved.

You are invoking CMake with it's command-line tool mode which doesn't execute generic scripts or commands.
Instead do e.g.
add_custom_command(TARGET abc
POST_BUILD
COMMAND /bin/sh /path/to/my_script
)

Related

How to put a test.txt in your CMake to test your program?

So I am using CLion on a Mac and wrote my code and would like to test it. I have an input file called test.txt. I know how to do it using terminal which is simply ./a < test.txt and it will run the binary and take text.txt as input. My question is, can we do it through CMake? So that I don't need to use terminal and just press the "run" button in CLion.
Having:
add_executable(a ...)
The short workaround on systems with sh shell would be to just spawn a shell to do the redirection:
add_test(NAME atest COMMAND sh -c "\"$1\" < \"$2\"" -- $<TARGET_FILE:a> test.txt)
A proper way would be to use CMake instead of shell. So a separate CMake script to run the executable with redirected file. Below is an example that just creates the script from inside CMake - but it can be just a separate file instead.
# redirect_stdin_from_file.cmake
execute_process(
COMMAND "${COMMAND}"
INPUT_FILE "${INPUT_FILE}"
RESULT_VARIABLE ret
)
if(ret)
message(FATAL_ERROR "ERROR: ${COMMAND} failed: ${ret}")
endif()
# CMakeLists.txt
add_test(NAME atest2 COMMAND
${CMAKE_COMMAND}
-D COMMAND=$<TARGET_FILE:a>
-D INPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test.txt
-P ${CMAKE_CURRENT_SOURCE_DIR}/redirect_stdin_from_file.cmake
)

Run executable right after building (c, cmake, make)

I would like to run my executable right after building it by using a post-build step (add_custom_command).
So far we used a WSL environment on Windows 10, which worked. Now we switch to directly building it in the Windows cmd, which gives us an error...
CMakeLists.txt extract:
# Execute unit tests after build and write output.txt into artifact directory
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${PROJECT_NAME} ARGS -v > ${ARTIFACTS}/output.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "run ${PROJECT_NAME} in ${CMAKE_CURRENT_SOURCE_DIR}"
VERBATIM
)
Within WSL using commands 'cmake -G "Unix Makefiles" $unit_tests_root' & 'make', CMake generates file: '...\build\Pipeline\CMakeFiles\unit_tests.dir\build.make', which shows:
...
unit_tests: CMakeFiles/unit_tests.dir/link.txt
#$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/mnt/c/repos/project-source/tests/unit_tests/build/Pipeline/CMakeFiles --progress-num=$(CMAKE_PROGRESS_30) "Linking C executable unit_tests"
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/unit_tests.dir/link.txt --verbose=$(VERBOSE)
#$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold "run unit_tests in /mnt/c/repos/project-source/tests/unit_tests"
cd /mnt/c/repos/project-source/tests/unit_tests && /mnt/c/repos/project-source/tests/unit_tests/build/Pipeline/unit_tests -v > /mnt/c/repos/project-source/tests/unit_tests/artifacts/output.txt
# Rule to build all files generated by this target.
CMakeFiles/unit_tests.dir/build: unit_tests
...
This toolchain is working: The unit tests are executed and output is printed to output.txt.
Within cmd using commands 'cmake -G "MinGW Makefiles" %unit_tests_root%' & 'make', CMake generates the same file: '...\build\Pipeline\CMakeFiles\unit_tests.dir\build.make', which shows:
...
unit_tests.exe: CMakeFiles/unit_tests.dir/link.txt
#$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=C:\repos\project-source\tests\unit_tests\build\Pipeline\CMakeFiles --progress-num=$(CMAKE_PROGRESS_30) "Linking C executable unit_tests.exe"
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles\unit_tests.dir\link.txt --verbose=$(VERBOSE)
#$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold "run unit_tests in C:/repos/project-source/tests/unit_tests"
cd /d C:\repos\project-source\tests\unit_tests && C:\repos\project-source\tests\unit_tests\build\Pipeline\unit_tests.exe -v > C:/repos/project-source/tests/unit_tests/artifacts/output.txt
# Rule to build all files generated by this target.
CMakeFiles/unit_tests.dir/build: unit_tests.exe
...
Error message in console: (line 555 corresponds to 'cd /d C:\repos\project-source\tests\unit_tests && ...'
...
[100%] Linking C executable unit_tests.exe
run unit_tests in C:/repos/project-source/tests/unit_tests
make[2]: *** [CMakeFiles\unit_tests.dir\build.make:555: unit_tests.exe] Error 13
make[2]: *** Deleting file 'unit_tests.exe'
make[1]: *** [CMakeFiles\Makefile2:94: CMakeFiles/unit_tests.dir/all] Error 2
make: *** [Makefile:102: all] Error 2
...
What's the problem with line 555?
When I comment out the 'add_custom_command(...)' within CMakeLists.txt, the unit_tests.exe builds successfully and I can execute it manually with:
C:\repos\project-source\tests\unit_tests>.\build\Pipeline\unit_tests.exe -v > .\artifacts\output.txt
Somehow the output is not the same as the output I get when build and executed within WSL, but at least it can be executed...
Edit 1, Additional Information: I am calling these commands (cmake, make) within a python script via subprocess.run(): I have the assumption, that this locks the files - still evaluating this...
Edit 2 / Solution, I found the problem: The error actually is returned by the unit_tests.exe itself as it has 13 errors (I missed the fact, that unity exits with the amount for errors). That's why make exits with errors and deletes the executable. Now I prefer calling the executable explicitly in my python script, where I also call the cmake and make commands. #Tsyvarev, sorry for the inconvenience and thanks for your contribution. ;-)

CMake with flex/bison in CLion

I'm trying to translate a Makefile into CMakeLists.txt.
Makefile which works
fb1-5: fb1-5.l fb1-5.y
bison -d fb1-5.y
flex fb1-5.l
cc -o $# fb1-5.tab.c lex.yy.c -lfl
CMakeLists.txt attempt
cmake_minimum_required(VERSION 3.7)
project(calc)
set(CMAKE_C_STANDARD 99)
FIND_PACKAGE(BISON REQUIRED)
SET(BisonOutput ${CMAKE_SOURCE_DIR}/parser.c)
IF(BISON_FOUND)
ADD_CUSTOM_COMMAND(
OUTPUT ${BisonOutput}
COMMAND ${BISON_EXECUTABLE}
-d
${CMAKE_SOURCE_DIR}/fb1-5.y
COMMENT "Generating parser.c"
)
ENDIF()
FIND_PACKAGE(FLEX REQUIRED)
SET(FlexOutput ${CMAKE_SOURCE_DIR}/scanner.c)
IF(FLEX_FOUND)
ADD_CUSTOM_COMMAND(
OUTPUT ${FlexOutput}
COMMAND ${FLEX_EXECUTABLE}
${CMAKE_SOURCE_DIR}/fb1-5.l
COMMENT "Generating fb1-5.l"
)
ENDIF()
ADD_LIBRARY(calc ${BisonOutput} ${FlexOutput})
It says it finds bison and flex in clion
-- Found BISON: /usr/bin/bison (found version "3.0.4")
-- Found FLEX: /usr/bin/flex (found version "2.6.0")
But my CMake script won't generate an executable. How should I define "executable" and how can I make the CMake build script work in CLion?
But my CMake script won't generate an executable. How should I define "executable" and how can I make the CMake build script work in CLion?
I think you should at least do something like this:
add_executable(fb1-5
${BisonOutput}
${FlexOutput}
)
See add_executable.

Build Jansson 2.7 failed if I copy the directory elsewhere

I'm using Jansson 2.7 for my project. I found something that causes a build failed.
If I try:
tar -zxvf jansson-2.7.tar.gz
cd jansson-2.7/
./configure
make
Everything is just fine. But If I try:
tar -zxvf jansson-2.7.tar.gz
cp jansson-2.7 jansson-2.7-test -r
cd jansson-2.7-test/
./configure
make
Configure will success, but make will fail:
make
CDPATH="${ZSH_VERSION+.}:" && cd . && /bin/bash /home/nick/Downloads/jansson-2.7-test/missing aclocal-1.14
/home/nick/Downloads/jansson-2.7-test/missing: line 81: aclocal-1.14: command not found
WARNING: 'aclocal-1.14' is missing on your system.
You should only need it if you modified 'acinclude.m4' or
'configure.ac' or m4 files included by 'configure.ac'.
The 'aclocal' program is part of the GNU Automake package:
<http://www.gnu.org/software/automake>
It also requires GNU Autoconf, GNU m4 and Perl in order to run:
<http://www.gnu.org/software/autoconf>
<http://www.gnu.org/software/m4/>
<http://www.perl.org/>
make: *** [aclocal.m4] Error 127
Any clues? Thanks!
I ran into this same issue simply extracting and trying to run configure/make. Following the link provided by nick2100, I found the following commands fixed the problem:
./configure
make AUTOCONF=: AUTOHEADER=: AUTOMAKE=: ACLOCAL=:
make AUTOCONF=: AUTOHEADER=: AUTOMAKE=: ACLOCAL=: install

CMake cross-compile with specific linker doesn't pass arguments to armlink

I am trying to cross-compile a project for embedded ARM Cortex builds, but I am unable to get the linker working. I want to use armlink, but no files are passed to armlink and hence no .elf file is produced.
My CMakeLists.txt is pretty simple and given below. The failure is shown after that which shows that armlink was invoked by the makefile without any arguments.
Any pointers will help - I searched and read many posts, but they all seem to have more involved requirements.
cmake_minimum_required(VERSION 2.8)
project(test_arm)
enable_language(C ASM)
# Cross-compilation for ARM
SET(CMAKE_C_COMPILER armcc)
SET(CMAKE_LINKER armlink)
SET(CMAKE_C_LINK_EXECUTABLE armlink)
SET(CMAKE_C_FLAGS "--cpu=Cortex-M3")
SET(LINK_FLAGS "--map --ro-base=0x0 --rw-base=0x0008000 --first='boot.o(RESET)' --datacompressor=off")
SET(CMAKE_EXE_LINKER_FLAGS "--map --ro-base=0x0 --rw-base=0x0008000 --first='boot.o(RESET)' --datacompressor=off")
include_directories(../include)
add_executable(blinky blinky.c)
set_target_properties(blinky PROPERTIES LINKER_LANGUAGE C)
The failure is as follows, but I guess it would be obvious to someone given that I have some stupid issue in my CMakeLists:
$ make VERBOSE=1
[100%] Building C object CMakeFiles/blinky.dir/blinky.c.o
/usr/bin/cmake -E cmake_link_script CMakeFiles/blinky.dir/link.txt --verbose=1
armlink
Linking C executable blinky
Product: DS-5 Professional 5.21.0 [5210017]
Component: ARM Compiler 5.05 update 1 (build 106)
Tool: armlink [4d0efa]
For support see http://www.arm.com/support/
Software supplied by: ARM Limited
Usage: armlink option-list input-file-list
where
....
I was expecting the CMake generated Makefile to invoke armlink with something like:
armlink --map --ro-base=0x0 --rw-base=0x0008000 \
--first='boot.o(RESET)' --datacompressor=off \
CMakeFiles/blinky.dir/blinky.c.o -o blinky.elf
Starting with CMake v3.5 you don't need a toolchain anymore for Keil ARM C/C++ compilation tools:
Support was added for the ARM Compiler (arm.com) with compiler id ARMCC.
Just set your C/CXX compiler variables accordingly
cmake -DCMAKE_C_COMPILER:PATH="C:\Program Files (x86)\DS-5\bin\armcc.exe"
-DCMAKE_CXX_COMPILER:PATH="C:\Program Files (x86)\DS-5\bin\armcc.exe"
...
References
ARMCC toolchain support
Add support for the ARM Compiler (arm.com)
CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found
From my experience, you cannot set CMAKE_EXE_LINKER_FLAGS in a CMakeLists.txt file. It has to be passed via a CMAKE_TOOLCHAIN_FILE when CMake is invoked the very first time in a build directory.
I don't find any documentation regarding this problem, but there is the cross-compilation with CMake page which you should use it if you do cross-compilation.
For a start, just put your set-calls in a toolchain file and run
cmake -DCMAKE_TOOLCHAIN_FILE=<yourfile.toolchain>
in a clean build directory.
A toolchain file may be a good idea. Here is what I've came up the last time I tried CMake 2.8.10 with the DS-5 toolchain (it could still be optimized, but it should give you a starting point):
INCLUDE(CMakeForceCompiler)
# This one is important
SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_PROCESSOR arm)
# Specify the cross compiler
SET(CMAKE_C_COMPILER "C:/Program Files (x86)/DS-5/bin/armcc.exe")
SET(CMAKE_CXX_COMPILER "C:/Program Files (x86)/DS-5/bin/armcc.exe")
SET(CMAKE_AR "C:/Program Files (x86)/DS-5/bin/armar.exe" CACHE FILEPATH "Archiver")
#CMAKE_FORCE_C_COMPILER("C:/Program Files (x86)/DS-5/sw/gcc/bin/arm-linux-gnueabihf-gcc.exe" GNU)
#CMAKE_FORCE_CXX_COMPILER("C:/Program Files (x86)/DS-5/sw/gcc/bin/arm-linux-gnueabihf-g++.exe" GNU)
UNSET(CMAKE_C_FLAGS CACHE)
SET(CMAKE_C_FLAGS "--cpu=Cortex-A9 --thumb -Ospace" CACHE STRING "" FORCE)
UNSET(CMAKE_CXX_FLAGS CACHE)
SET(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "" FORCE)
UNSET(CMAKE_EXE_LINKER_FLAGS CACHE)
SET(CMAKE_EXE_LINKER_FLAGS "" CACHE STRING "" FORCE)
UNSET(CMAKE_AR_FLAGS CACHE)
SET(CMAKE_AR_FLAGS "-p -armcc,-Ospace" CACHE STRING "" FORCE)
# set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> cr <TARGET> <LINK_FLAGS> <OBJECTS>")
SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> ${CMAKE_AR_FLAGS} -o <TARGET> <OBJECTS>" CACHE STRING "C Archive Create")
# set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> cr <TARGET> <LINK_FLAGS> <OBJECTS>")
SET(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> ${CMAKE_AR_FLAGS} -o <TARGET> <OBJECTS>" CACHE STRING "CXX Archive Create")
include_directories("C:/Program Files (x86)/DS-5/include")
#include_directories("C:/Program Files (x86)/DS-5/sw/gcc/arm-linux-gnueabihf/libc/usr/include/arm-linux-gnueabi")
# Where is the target environment
SET(CMAKE_FIND_ROOT_PATH "C:/Program Files (x86)/DS-5")
# Search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Regarding your question
Some failure analysis
What you have tried should work (see also e.g. How do I add a linker or compile flag in a CMake file?). But it seems something goes wrong during the configuration step.
I don't know your command line call for CMake's configuration/generation steps, so some general tips to find the root cause:
You could try calling
cmake.exe --trace ...
to see what went wrong with your CMAKE_EXE_LINKER_FLAGS variable. This will generate a lot of output, so here are some basics on what CMake does:
The project() command will trigger the compiler evaluation
This will write CMAKE_EXE_LINKER_FLAGS into your CMakeCache.txt
You are overwriting it with a local variable (see variable scope docu here)
If you look into share\cmake-2.8\Modules\CMakeCommonLanguageInclude.cmake:
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT} $ENV{LDFLAGS}"
CACHE STRING "Flags used by the linker.")
You could use CMAKE_EXE_LINKER_FLAGS_INIT, but you have to set it before the project() command or in the toolchain file.
Because you set the link language to C take a look into share\cmake-2.8\Modules\CMakeCInformation.cmake:
if(NOT CMAKE_C_LINK_EXECUTABLE)
set(CMAKE_C_LINK_EXECUTABLE
"<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
endif()
So you can use CMAKE_C_LINK_EXECUTABLE to overwrite the complete linker call or you could use CMAKE_C_LINK_FLAGS to set additional flags.
The "official" way
The official way to set the target's linker and compiler flags would be (before CMake 2.8.12):
set_property(TARGET blinky APPEND_STRING PROPERTY COMPILE_FLAGS "--cpu=Cortex-M3")
set_property(TARGET blinky APPEND_STRING PROPERTIES LINK_FLAGS "--map --ro-base=0x0 --rw-base=0x0008000 --first='boot.o(RESET)' --datacompressor=off")
Starting with CMake 2.8.12 it would be something like:
add_compile_options("--cpu=Cortex-M3")

Resources