How to call a cmake method from shell script? - shell

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

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.

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

Related

How to use the source command with the system() function?

I need to source a few environment variables in another file. If I use the source command with system() function, it's complaining about "No such file or directory". Am I missing something?
My code looks like below. In my code, I have only the system() function running the source command. The source file has just only one command: pwd (Present working directory).
perl_system.pl
#!/usr/bin/perl
system "source env.mk"
env.mk (contents of env.mk which I want to source has just pwd for now"
pwd
When I run this command, I see the below error
$ perl -w perl_system.pl
Can't exec "source": No such file or directory at perl_system.pl line 2.
source is a shell built-in that executes a shell script using the current shell interpreter. So it doesn't work as an external command and won't change the environment of your perl process even if you change your system call to invoke a shell instead of it trying to run an external program directly.
You could run your env.mk and then output the resulting environment and update perl's environment accordingly, though:
for my $env (`bash -c 'source env.mk;env'`) {
chomp $env;
my ($var,$val) = split /=/, $env, 2;
$ENV{$var} = $val;
}
(with obvious problems if environment variables contain newlines).
Update: just read all of your question, not just the beginning. If all you want to do is execute a shell script, just do:
system "sh env.mk";
source is completely unnecessary for this.

Is there a good way to preload or include a script prior to executing another script?

I am looking to execute a script but have it include another script before it executes. The problem is, the included script would be generated and the executed script would be unmodifiable. One solution I came up with, was to actually reverse the include, by having the include script as a wrapper, calling set to set the arguments for the executed script and then dotting/sourcing it. E.g.
#!/bin/bash
# Generated wrapper or include script.
: Performing some setup...
target_script=$1 ; shift
set -- "$#"
. "$target_script"
Where target_script is the script I actually want to run, importing settings from the wrapper.
However, the potential problem I face is that callers of the target script or even the target script itself may be expecting $0 to be set to the path of it's location on the file system. But because this wrapper approach overrides $0, the value of $0 may be unexpected and could produce undefined behaviour.
Is there another way to perform what is in effect, an LD_PRELOAD but in the scripted form, through bash without interfering with its runtime parameters?
I have looked at --init-file or --rcfile, but these only seem to be included for interactive shells.
Forcing interactive mode does seem to allow me to specify --rcfile:
$ bash --rcfile /tmp/x-include.sh -i /tmp/xx.sh
include_script: $0=bash, $BASH_SOURCE=/tmp/x-include.sh
target_script: $0=/tmp/xx.sh, $BASH_SOURCE=/tmp/xx.sh
Content of the x-include.sh script:
#!/bin/bash
echo "include_script: \$0=$0, \$BASH_SOURCE=$BASH_SOURCE"
Content of the xx.sh script:
#!/bin/bash
echo "target_script: \$0=$0, \$BASH_SOURCE=$BASH_SOURCE"
From the bash documentation:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in
the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read
and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file name.
So that settles it then:
BASH_ENV=/tmp/x-include.sh /bin/bash /tmp/xx.sh

Source in shell script doesn't work when script called with arguments

I'm trying to write a shell script that will start Xilinx programs for me, but I am having a problem with the source command. Here is my script called xilinx:
#!/usr/bin/env bash
cd /home/sclukey/Xilinx
source /opt/Xilinx/14.6/ISE_DS/settings32.sh
$#
If I just call xilinx the script returns
. /opt/Xilinx/14.6/ISE_DS/common/.settings32.sh /opt/Xilinx/14.6/ISE_DS/common
. /opt/Xilinx/14.6/ISE_DS/EDK/.settings32.sh /opt/Xilinx/14.6/ISE_DS/EDK
. /opt/Xilinx/14.6/ISE_DS/PlanAhead/.settings32.sh /opt/Xilinx/14.6/ISE_DS/PlanAhead
. /opt/Xilinx/14.6/ISE_DS/ISE/.settings32.sh /opt/Xilinx/14.6/ISE_DS/ISE
but if I run xilinx ise then the output of the source command is missing and it just returns
/usr/local/bin/xilinx: line 4: ise: command not found
I believe this is because the source command is not running when the script is called with arguments. Why is this and how can I fix it?
Thanks
EDIT:
I've discovered it is because the settings32.sh file uses the $1 argument, and when the xilinx script is called with arguments, those arguments also get passed to the source command which breaks the settings32.sh script. So how can I stop the arguments from getting passed to the source command?
It seems like /opt/Xilinx/14.6/ISE_DS/settings32.sh or one of the scripts it source's responds to command-line argument and swallows them. Try saving them before sourcing, then wiping them:
#!/usr/bin/env bash
args=$#
shift
cd /home/sclukey/Xilinx
source /opt/Xilinx/14.6/ISE_DS/settings32.sh
$args
With your script, you are automating the following commands, if xilinx ise is typed in from the command line:
$ cd /home/sclukey/Xilinx
$ source /opt/Xilinx/14.6/ISE_DS/settings32.sh
$ ise
The response from the script indicates that there's no ise program in the path. I would check to see where ise is, and if settings32.sh sets up a path for it.

bash shell in Cygwin

when I am in a Cygwin terminal, I can easily use the "source" command.
For example, let's say I have a file called "my_aliases.sh", that contains the following
#!/bin/bash -f
alias clear='cmd /c cls'
#unalias clear
Then on the Cygwin terminal, I can type
$source my_aliases.sh
And it just works fine, and whenever I type "clear", I can see that it works.
But I don't know why doing the same thing inside another shell script, and calling that shell script doesn't work.
For example, let's say that I have a file called "run_alias.sh", with the following content:
#!/bin/bash -f
#
a=`source my_aliases.sh`
b=`ls -ltr`
echo $a
echo $b
And when I try to run my file
$ ./run_alias.sh
It just doesn't do anything. For example, I can see that the command (b) takes place, but nothing happens for command (a).
But after I run "run_alias.sh", and type "clear", I get the following error:
$ clear
bash: clear: command not found
I even tried to change run_alias.sh as follows:
#!/bin/bash -f
echo `source my_aliases.sh`
But now when run run_alias.sh, and type clear, I get the exact same error message !!!
Any idea how to call the "source" command from some other shell script in Cygwin?
A child process cannot alter its parent's environment.
When you execute the run_alias.sh script, you launch a new bash process, which sources your alias file. Then the script ends, that bash process terminates and it takes its modified environment with it.
If you want your aliases to be automatically available, source it from your $HOME/.bashrc file.
Backticks create a subshell. The changes made to your environment in that subshell do not affect the calling environment.
Id you want your script (run_alias.sh) to have access to the environment in my_aliases.sh, call source directly.
source my_aliases.sh
b=`ls -lrt`
echo $b
and if you want the changes that run_alias.sh makes to its environment to propagate to it's parent, run source on the command line.
$ source run_alias.sh

How to handle setting up environment in makefile?

So, to compile my executable, I need to have the library locations set up correctly. The problem is, the setup comes from a bunch of scripts that do the env variable exporting, and what needs to be set up may change (beyond my control) so I need to use those scripts instead of copying their functionality. To compile in regular command line, I need to do something like:
setup library1
setup library2
source some_other_setup_script.bash
g++ blah.c
# setup is a executable on my system that run some scripts
How would I write a makefile that accomplishes that? As far as I tried, the env variable exporting does not carry over (i.e. "export VAR=remember; echo $VAR" won't work)
You can also add environment variables properly with the machinery of GNU make, like so:
export TEST:="Something Good!"
test:
echo $$TEST
This (I think) has different semantics from:
TEST2:="Something not quite so useful?"
test2:
echo ${TEST2}
Which (again, I think) does the substitution within make before passing along to the shell. Note that the export command doesn't work within a target block, just unindented as an immediately executed command.
If variable exporting is not working the way it does on your command line, that suggests that Make is choosing a shell different from the one you're using, with different syntax for handling variables (export VAR=remember; echo $VAR works fine for me). Make uses /bin/sh by default, but you can override this with the SHELL variable, which Make does not import from the environment. I suggest setting SHELL (in the Makefile) to whatever you're using in your environment and trying the export VAR=remember experiment again.
Ultimately you will need to define the variable and execute the compiler in a shell list or even a script, rather than in separate make commands. There are a couple of refinements you could add, however. You could tell make about the script:
maintarget: script.sh blah.c
source script.sh; g++ blah.c
script.sh:
setup include script here
Another thing would be to just execute all that stuff in the same shell
maintarget: blah.c
run this; run that; run the other thing; g++ blah.c
I believe all make versions will run a ; list in the same shell, but you can always force a subshell with (list) or by calling specifically a shell script as a compiler command wrapper.
Don't forget to have the appropriate targets depend on your scripts themselves. BTW, some make versions (pmake aka bsd make) can execute a command when defining a make variable, and all versions of make then exports those. But I don't think gmake can do that.
You could write another shell script that executes all those commands, then prints out variable assignments that make can use. Run the script, pipe its output to a file, then include that file from your Makefile. For example:
Makefile:
all:
echo $(FOO)
test.mk: test.sh
./$< > $#
include test.mk
test.sh
echo "FOO=1"
Running "make" in the directory containing this Makefile produces:
make: Entering directory `/home/luser/build/mktest'
Makefile:7: test.mk: No such file or directory
./test.sh > test.mk
make: Leaving directory `/home/luser/build/mktest'
make: Entering directory `/home/luser/build/mktest'
echo 1
1
make: Leaving directory `/home/luser/build/mktest'
make creates test.mk by running the shell script, then includes it. test.mk contains the output of test.sh, and is parsed as a Makefile. See http://www.gnu.org/software/make/manual/make.html#Include for more details.
We use a variant of this in Mozilla's client.mk to let you define options in a "mozconfig" file:
http://mxr.mozilla.org/mozilla-central/source/client.mk#138
Restatement: How do I get a shell variable into a make file?
Something like:
MYVAR := $(shell echo $(MYVAR)) <any_makefile_additions_here>
So, this defines MYVAR inside a MAKEFILE when an environment variable named MYVAR is also set.
It might be of interest, that, in order to override an option that is already defined in a makefile, make supports (I am referring to GNU Make 3.82, but other version probably too) the option -e.
Example:
Makefile:
CC=gcc
...
Run make:
CC=gcc-4.7
make -e
will use gcc-4.7 instead of gcc.

Resources