1

I have a parameterised function in mytestprogram.cmake written like below:

function(get_output_from_input output input) 
    set(${output} "test" PARENT_SCOPE)
endfunction()

Question:
How do I call the cmake method get_output_from_input from a shell script?

I learnt that there is -P <source.cmake> flag, that puts CMake into script processing mode and we can execute arbitrary CMake code with it using something like below.

execute_process(COMMAND ${CMAKE_PROGRAM} -P yourscript.cmake)

So, in this case I believe the way to call get_output_from_input from a shell script would be something like below?

input="some_input"
output=""
execute_process(get_output_from_input(output, input) ${CMAKE_PROGRAM} -P mytestprogram.cmake)
echo $output

But above trick does not work. Is the way I am running execute_process correct?

Trying to figure out whats wrong, it seems echo $CMAKE_PROGRAM returns empty? Could that be the reason? What am I missing here to call get_output_from_input?

Environment:
cmake version 3.18.2
macOS Catalina

TheWaterProgrammer
  • 4,740
  • 6
  • 35
  • 102
  • I corrected the syntax of `get_output_from_input`. – TheWaterProgrammer Mar 04 '21 at 20:17
  • 1
    Obviously you cannot run a script or part of it without proper **interpreter**. You cannot run a shell script without a shell (`sh`, `bash`, etc.), you cannot run a CMake script without `cmake`. Also, all definitions (functions, variables) in the script are only visible to its interpreter: you cannot read a variable defined in CMake script outside of CMake, you cannot run a function defined in CMake script outside of CMake. The only exception is **environment** variables, because they are simply not a script variables: the notion of environment variables is shared across scripting languages. – Tsyvarev Mar 04 '21 at 20:26
  • Thats a very good explanation to my question. Thanks for explaining why @Tsyvarev – TheWaterProgrammer Mar 04 '21 at 20:31

2 Answers2

3

cmake -P executes an entire script, not a single function defined in a CMake file, so need to add an additional script file that you can then execute via cmake -P. Arguments to a script executed via cmake -P need to be passed as CMake variables, e.g. cmake -DINPUT=some_input -P myscript.cmake .

Since you need the output of the CMake script in an bash variable, the CMake script "mytestscript.cmake" would look something like this:

# make `get_output_from_input` available in current script
# assuming the script file is located next to `mytestprogram.cmake`
include(${CMAKE_CURRENT_LIST_DIR}/mytestprogram.cmake)

get_output_from_input(RESULT "${INPUT}")

message("${RESULT}")

and the shell script contains

#!/bin/bash
input="some_input"

# assumes `mytestscript.cmake` is located in the current working directory
output=$(cmake -DINPUT="${input}" -P mytestscript.cmake 2>&1)

# Below statement should print the value of output.
echo $output

Regarding execute_process: That is meant to be used if you need to execute another process from within a CMake script, not to execute a CMake script from a shell process. Depending on what you plan to do with the output of the CMake script you could possibly use execute_process instead of additional bash.

Corristo
  • 4,111
  • 1
  • 16
  • 32
  • 1
    Thanks a lot for taking a look. I don't need the output of `get_output_from_input` in an environment variable. I just need it in a variable in the shell script. – TheWaterProgrammer Mar 04 '21 at 20:07
  • 1
    `include(${CMAKE_CURRENT_LIST_DIR}/mytestprogram.cmake)` is to be called from shell script? – TheWaterProgrammer Mar 04 '21 at 20:09
  • 1
    Do you mind simplifying your answer a bit? I just need the output of that cmake method in a variable in my `.sh` shell script and `echo` it out. Thats it. – TheWaterProgrammer Mar 04 '21 at 20:13
  • 1
    Please note that I did not mean to call `get_output_from_input` from `CMakeLists.txt`. I meant, how to call it from a shell script. – TheWaterProgrammer Mar 04 '21 at 20:15
  • 1
    @TheWaterProgrammer `include(${CMAKE_CURRENT_LIST_DIR}/mytestprogram.cmake` needs to be called from the CMake script you intend to execute with `cmake -P`. I've given names to the script files now to hopefully make it easier to follow. – Corristo Mar 04 '21 at 20:22
  • 1
    @TheWaterProgrammer *I meant, how to call it from a shell script* You can't directly call CMake functions from bash, you need a CMake script that calls the function. As shown in the answer that CMake script is just a small additional wrapper that doesn't need to be used in a `CMakeLists.txt` project configuration script. – Corristo Mar 04 '21 at 20:24
  • 1
    Got it. Thanks for explaining. I am trying to make your example code work. – TheWaterProgrammer Mar 04 '21 at 20:26
  • 1
    Have you tried your code? Running the cmake script from the shell complains `CMake Error: Error processing file: mytestscript.cmake`. I even tried hard coding the path of `mytestprogram.cmake`. – TheWaterProgrammer Mar 04 '21 at 20:28
  • @TheWaterProgrammer Yes I've tested it (which is why I just noticed that CMake doesn't output on stdout for some reason, so you need to add the redirect `2>1` in the assignment of the output to the `output` variable in the bash script). Is your working directory correct? You currently need to be in the same directory as the shell and cmake scripts in order to run this. – Corristo Mar 04 '21 at 20:32
  • 1
    I have place all the 3 files in the same directory. Now the error is gone but `output` variable in shell script is empty? Can you please add `echo $output` to print out the value of `output` to the console? – TheWaterProgrammer Mar 04 '21 at 20:35
  • 1
    @TheWaterProgrammer Sorry I forgot the ampersand in the shell redirect, it should work now – Corristo Mar 04 '21 at 20:38
  • 1
    Perfect! Thanks a lot. It works :D This was a fantastic learning for me. Accepting your answer :D – TheWaterProgrammer Mar 04 '21 at 20:40
  • 1
    @TheWaterProgrammer I just noticed that inputs with semicolons weren't handled correctly and have fixed `mytestscript.cmake` accordingly. – Corristo Mar 04 '21 at 20:50
  • 1
    Even better! Still works for me :D – TheWaterProgrammer Mar 04 '21 at 20:54
1

What I think you're asking for—setting a Bash variable directly from CMake's script mode—is impossible. The script mode (-P) just lets you run CMake code without a project / build directory. And execute_process is a CMake command, not a shell command, so calling it from sh/bash is bound to fail.

The CMake function you wrote is wrong, too. All it does is write to a local variable which is never read and immediately gets destroyed. You probably wanted set(VAR "VALUE" PARENT_SCOPE). The return() is pointless and should be removed.

If you explained a bit more about what you were trying to do, maybe this question would be answerable.


Here's a basic demonstration of CMake's script mode:

# ./script.cmake
# Input variable: file
# Output: md5sum of file

cmake_minimum_required(VERSION 3.19)

file(MD5 "${file}" hash)
message("${hash}")

Running this from the terminal (equiv. a shell script):

$ ls
my_file.txt  script.cmake
$ cat my_file.txt
Hello, world!
$ cmake -Dfile=my_file.txt -P script.cmake
746308829575e17c3331bbcb00c0898b
Alex Reinking
  • 6,342
  • 2
  • 28
  • 47
  • 1
    I corrected the syntax in my .cmake file. Thanks for pointing it out. You mean it is impossible to call a function in .cmake file from within a shell script? – TheWaterProgrammer Mar 04 '21 at 20:12
  • 1
    Correct, you cannot directly call a CMake function from a shell script. You can do what I did in the example, however, and write a CMake script that loads your function, calls it, and prints the result. – Alex Reinking Mar 04 '21 at 20:17