how to run compound script statements from cmake? - cmake

I have a complex/compound command that I want to execute as patch command during execution of my ExternProject_Add using this macro
macro(SET_PATCH_CMD arg)
message(STATUS "Patch command : ${arg};${ARGN}")
set(${PROJECT_NAME}_patch_cmd ${arg};${ARGN})
endmacro(SET_PATCH_CMD)
I dont remeber or am not sure why I added a ; between ${arg} and ${ARGN}, but patch commands like
mkdir -p <SOURCE_DIR>/install
are working as expected in my cmake project.
When I set a simple command like
SET(PATCH_CMD <SOURCE_DIR>/scripts/my_schript.sh)
patch command executes as expected, but the moment I start adding arguments to script with spaces in between
SET(PATCH_CMD <SOURCE_DIR>/scripts/my_schript.sh arg1 arg2 arg3)
SET_PATCH_CMD(${PATCH_CMD})
I get Command failed: No such file or directory
clearly spaces in my command are messing up the way cmake runs the shell script/command. Any help on making it work is appreciated.

Related

CMake do something when command fails

I use CMake. A custom build step saves error output when it fails.
FIND_PROGRAM (BASH bash HINTS /bin)
SET (BASH_CMD "my-script input.file >output.file 2>error.file")
ADD_CUSTOM_COMMAND (
OUTPUT output.file
COMMAND ${CMAKE_COMMAND} -E env ${BASH} -c "${BASH_CMD}"
...)
This works. If my-script fails for the given input.file then the stderr is saved in error.file, however when I run make and the target fails to build, the normal output does not make the location of error.file obvious. (The actual path of this file is generated in a tangly way.)
I don't want the noisy stderr to show in the terminal during make. I would like to do something like
MESSAGE ("input.file failed, see error.file")
(ideally coloured red or something) to be executed when the command for output.file failed.
Can I express this behaviour in CMakeLists.txt recipes?
Not sure about the highlighting, but you could create a cmake script file executing the command via execute_process, check it's error code and print a custom message in case there's an issue. The following example runs on windows, not on linux, but this should be sufficient for demonstration.
Some command that fails: script.bat
echo "some long message" 1>&2
exit 1
CMake script: execute_script_bat.cmake
execute_process(COMMAND script.bat RESULT_VARIBALE _EXIT_CODE ERROR_FILE error.log)
if (NOT _EXIT_CODE EQUAL 0)
message(FATAL_ERROR "command failed; output see ${CMAKE_SOURCE_DIR}/error.log")
endif()
CMakeLists.txt
add_custom_command(
OUTPUT output.file
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_SOURCE_DIR}/execute_script_bat.cmake")
Additional info can be passed by adding -D "SOME_VARIABLE=some value" arguments after "${CMAKE_COMMAND}"

/bin/bash: command not found Google Colab

I am trying to run a ready project on Google Colab.. when I run a shell it gives the following error:
/bin/bash: example.sh: command not found
How I can solve this problem?
You have two options to run shell script in google-colab:
1) Execute a single script with !:
!sh example.sh
!echo "I am your code !!!"
2) Execute entire code-block as shell script with %%shell:
%%shell
sh example.sh
echo "You should add %% "
Note: In the second approach, entire block interpreted as shell script. You do not need ! at beginning of every script.

Execute program conditionally

I am opening a file from CMake which will be open in a installed tool in my PC from the following command but the tool is not available in the server which throwing an error during the build.kindly help me to skip that part in server.
COMMAND ${Src_File_gen} -f "${CMAKE_SRC_DIR}/Source.xyz" -g -m ${_VARIANT}
You might want to use find_program before calling the tool. Then wrap the calling CMake command in an if condition only executing when the program was found:
find_program(SRC_FILE_GEN <src_file_gen_exe>)
if(${SRC_FILE_GEN})
COMMAND ${SRC_FILE_GEN} -f "${CMAKE_SRC_DIR}/Source.xyz" -g -m ${_VARIANT}
endif()

CTest with multiple commands

I'm building some tests using CTest. Usually, I can set up the test by simply the line:
ADD_TEST(Test_Name executable args)
However, I've run into a problem, I have some tests that require two commands to be run in order for it to work, is there any way I can run two programs within a single ctest, or am I required to create a new test for each?
Thank you.
The add_test command only accepts one executable, but you can run any executable that is really a script. To do this in a cross platform way, write the script in CMake itself. CMake has the -P option for running arbitrary chunks of CMake scripting language when you run make or make test, rather than at Makefile generation time.
Sadly you can't pass arguments to such a script. But you can set variables to values, which is just as good.
This script you can call runtests.cmake, it runs the commands CMD1 and CMD2 and checks each for a non-zero return code, returning out of CMake itself with an error if that happens:
macro(EXEC_CHECK CMD)
execute_process(COMMAND ${CMD} RESULT_VARIABLE CMD_RESULT)
if(CMD_RESULT)
message(FATAL_ERROR "Error running ${CMD}")
endif()
endmacro()
exec_check(${CMD1})
exec_check(${CMD2})
... and then add your test cases like so:
add_executable(test1 test1.c)
add_executable(test2 test2.c)
add_test(NAME test
COMMAND ${CMAKE_COMMAND}
-DCMD1=$<TARGET_FILE:test1>
-DCMD2=$<TARGET_FILE:test2>
-P ${CMAKE_CURRENT_SOURCE_DIR}/runtests.cmake)
$<TARGET_FILE:test1> gets expanded to the full path to the executable at build-file generation time. When you run make test or equivalent this will run "cmake -P runtests.cmake" setting the CMD1 and CMD2 variables to the appropriate test programs. The script will then execute your 2 programs in sequence. If either of the test programs fail, the whole test fails. If you need to see the output of the test, you can run make test ARGS=-V
There is a simple, although not cross platform, way to achieve this.
In Linux you can use bash to execute multiple commands:
add_test(
NAME
TestName
COMMAND
bash -c "COMMAND1 ; \
COMMAND2 ; \
${CMAKE_CURRENT_BINARY_DIR}/testExecutable"
)

Running a bash script from the JVM

I'm having trouble running a simple bash script from Java. Specifically:
...
try{
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", command);
pb.directory(new File(dir));
Process shell = pb.start();
int exitVal = shell.waitFor();
...
where 'command' the absolute path to a bash script that is executable by all and 'dir' is the working directory.
When I run my program I get an exit code 127 ("command not found"). I've tried using the Java Runtime class and the process.exec method but neither have worked for me. Any suggestions?
If "command" is a bash script, then instead of passing "/bin/bash" (and the erroneous "-c" like you're doing) to ProcessBuilder, just make sure that command is executable (chmod +x command), that the first line is #!/bin/bash, and then pass the full path to it into ProcessBuilder.
No -c. That means the script is the argument to -c. You are passing it a pathname, and you don't use -c for that.