How to print a variable's name in a Makefile? - makefile

I need to inform the user of my Makefile about he must set an environment variable.
target: dependency
#if test ! -f $(FILE) ; then\
echo Vitally needed $(FILE) is missed. ;\
echo You can change the path to the file by setting $(get_var_name $(FILE)) environment variable.;\
fi
So, is there a way to obtain the name of $(FILE) (what I've called $(get_var_name ...) make function) so that the user would see the following output?
Vitally needed very.very.important.file is missed.
You can change the path to the file by setting FILE environment variable.

Like so:
target: dependency
#if test ! -f $(FILE) ; then\
echo Vitally needed $(FILE) is missed. ;\
echo You can change the path to the file by setting FILE environment variable.;\
fi
Because the name of the FILE variable is FILE.

Related

Makefile: Reuse environment variables

In my Makefile deploy target I create environment variables and I want to reuse those in the following lines:
SHELL=/bin/sh
deploy:
export $(shell sh this-script-generate-key-values.sh | xargs)
echo ${VAR1} #there is no variable here
echo ${VAR2} #there is no variable here
Where:
this-script-generate-key-values.sh generates this output:
VAR1="somevalue"
VAR2="somevalue"
Why the variables are not set in subsequent lines? How can I make it work?
Notes:
This line works: sh this-script-generate-key-values.sh | xargs
The shell must be /bin/sh (no bash)
All lines in a Makefile recipe run in a separate shell. You need to run the lines in a single shell. Also you need to escape the dollar sign ($) so that variable substitution is not done by make but by the shell.
SHELL=/bin/sh
deploy:
export $$(this-script-generate-key-values.sh | xargs) ;\
echo $${VAR1} ;\
echo $${VAR2}
Just to expand on my comment -- you could output to a file, and use the file to generate your output as so:
vars.txt:
this-script-generate-key-values.sh > $#
deploy : vars.txt
echo VAR1=$$(sed -n 's|VAR1=\(.*\)|\1|p' vars.txt)
echo VAR2=$$(sed -n 's|VAR2=\(.*\)|\1|p' vars.txt)
note: you may have to generate dependencies for vars.txt or declare it .PHONY, otherwise, this will not run on every invocation of make.
If the .ONESHELL special target appears anywhere in the makefile then all recipe lines for each target will be provided to a single invocation of the shell. Newlines between recipe lines will be preserved.
.ONESHELL:
deploy:
export $$(this-script-generate-key-values.sh)
echo $${VAR1}
echo $${VAR2}

How to load and export variables from an .env file in Makefile?

What is the best way to use a .env in a Makefile, i.e. loading that file and exporting all variables for subshells in make?
It would be great if the proposed solution would work with make only, e.g. not using any third party tools. Also .env files support multiline variables like:
FOO="this\nis\na\nmultiline\nvar"
this is why this solution is probably not adequate.
Make does not offer any way to read a content of the file to some variable. So, I consider it impossible to achieve the result without using external tools. However, if I am wrong, I'd be glad to learn some new trick.
So, let's assume there are two files, .env, being a technically correct shell file:
FOO=bar
BAR="notfoo" # comment
#comment
MULTILINE="This\nis\nSparta!"
# comment
and script.sh:
#!/bin/bash
echo FOO=${FOO}
echo BAR=${BAR}
echo -e ${MULTILINE}
One solution is to include the .env file, then make sure variables are exported:
include .env
$(eval export $(shell sed -ne 's/ *#.*$$//; /./ s/=.*$$// p' .env))
all:
./script.sh
Because of different treatment of quotes by shell and make, you will see the quotes in output.
You can avoid that by reprocessing the variables by make:
include .env
VARS:=$(shell sed -ne 's/ *\#.*$$//; /./ s/=.*$$// p' .env )
$(foreach v,$(VARS),$(eval $(shell echo export $(v)="$($(v))")))
all:
./script.sh
but then the multiline variable will become a one-liner.
Finally, you can generate a temporary file to be processed by bash and source it before any command is run:
SHELL=bash
all: .env-export
. .env-export && ./script.sh
.env-export: .env
sed -ne '/^export / {p;d}; /.*=/ s/^/export / p' .env > .env-export
Oh, new lines got messed in this case in multiline variable. You need to additionally quote them.
Finally, you can add export to .env using above sed command, and do:
SHELL=bash
%: .env-export
. .env-export && make -f secondary "$#"
Found this and it worked great:
at top of makefile
ifneq (,$(wildcard ./.env))
include .env
export
endif
Then you have make variables for all your env, for example MY_VAR use as $(MY_VAR)
You can load specific .env file for each target by creating a function and target to use it with other targets when necessary. Here as an sample:
define setup_env
$(eval ENV_FILE := $(1).env)
#echo " - setup env $(ENV_FILE)"
$(eval include $(1).env)
$(eval export)
endef
devEnv:
$(call setup_env, dev)
prodEnv:
$(call setup_env, prod)
clean:
rm -rf bin/
build: clean
GOOS=linux GOARCH=amd64 go build -o bin/ ./cmd/...
dev: build devEnv
cd cmd/api && ./../../bin/api
migrate-dev-db: devEnv
sh +x database/migration/migrate.sh dev
migrate-prod-db: prodEnv
sh +x database/migration/migrate.sh
deploy: prodEnv
sh +x script/deployment/production/ec2-deploy.sh

what does #D mean in shell script

i have a code as
echo -e "\\n" "===== Making: $(#D)\n";\
if [ ! -d $(#D) ]; then \
mkdir $(#D); \
else \
if [ -e $(#D)\PackageBuild.error ]; then \
rm $(#D)\PackageBuild.error;\
fi; \
i am not sure what this #D is doing.
can someone help me out here
Usually $(command) executes command and replaces $(command) with the output of command.
So there must be a file named #D which is executable and located in the search path.
But if this is not a shell script but a make file it means:
$(#D)
The directory part of the file name of the target, with the trailing
slash removed. If the value of $# is dir/foo.o then $(#D) is
dir. This value is . if $# does not contain a slash.
The bash does not have any notion of a syntax like #D. I guess you are in a special context here, I guess again in a Makefile. make is a special program which does not verbatim execute the scripts in the Makefile. Instead it preprocesses the scripts.
$(x) is evaluated prior to calling the scripts and replaced by variables set in the Makefile. So I guess somewhere in the Makefile you have a variable called #D set to a specific value. (And there is a predefined one, as D. Mika found out ;-)
The documentation https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables mention that
‘$(#D)’: The directory part of the file name of the target, with the trailing slash removed [...]
But also
‘$(#F)’: The file-within-directory part of the file name of the target[...]
So if ‘$#’ is dir/foo.o then ‘$(#D)’ is dir and '$(#F)' is foo.o

GNU Make, generate file with default settings/content when absent

I would like make to either copy a file from the source tree into the target/build directory if it exits or generate an empty/default file if not.
It would be easy to do the following:
target/settings.json: src/settings.json
cp $? $#
src/settings.json:
echo "default..." > $#
But that taints the source repository with a file that could inadvertently be checked into RCS.
Is there a simple make rule that can copy the file if it exits, or just generate the target with a command/copy from some other source?
A GNU-Make specific solution is fine
You can check if the file exists using $(wildcard), so maybe something like this:
ifeq ($(wildcard src/settings.json),)
SETTINGS = tmp/settings.json
else
SETTINGS = src/settings.json
endif
target/settings.json: $(SETTINGS)
cp $? $#
tmp/settings.json:
echo "default..." > $#

Passing a path as an argument to a shell script

I've written bash script to open a file passed as an argument and write it into another file. But my script will work properly only if the file is in the current directory. Now I need to open and write the file that is not in the current directory also.
If compile is the name of my script, then ./compile next/123/file.txt should open the file.txt in the passed path. How can I do it?
#!/bin/sh
#FIRST SCRIPT
clear
echo "-----STARTING COMPILATION-----"
#echo $1
name=$1 # Copy the filename to name
find . -iname $name -maxdepth 1 -exec cp {} $name \;
new_file="tempwithfile.adb"
cp $name $new_file #copy the file to new_file
echo "compiling"
dir >filelist.txt
gcc writefile.c
run_file="run_file.txt"
echo $name > $run_file
./a.out
echo ""
echo "cleaning"
echo ""
make clean
make -f makefile
./semantizer -da <withfile.adb
Your code and your question are a bit messy and unclear.
It seems that you intended to find your file, given as a parameter to your script, but failed due to the maxdepth.
If you are given next/123/file.txt as an argument, your find gives you a warning:
find: warning: you have specified the -maxdepth option after a
non-option argument -iname, but options are not positional (-maxdepth
affects tests specified before it as well as those specified after
it). Please specify options before other arguments.
Also -maxdepth gives you the depth find will go to find your file until it quits. next/123/file.txt has a depth of 2 directories.
Also you are trying to copy the given file within find, but also copied it using cp afterwards.
As said, your code is really messy and I don't know what you are trying to do. I will gladly help, if you could elaborate :).
There are some questions that are open:
Why do you have to find the file, if you already know its path? Do you always have the whole path given as an argument? Or only part of the path? Only the basename ?
Do you simply want to copy a file to another location?
What does your writefile.c do? Does it write the content of your file to another? cp does that already.
I also recommend using variables with CAPITALIZED letters and checking the exit status of used commands like cp and find, to check if these failed.
Anyway, here is my script that might help you:
#!/bin/sh
#FIRST SCRIPT
clear
echo "-----STARTING COMPILATION-----"
echo "FILE: $1"
[ $# -ne 1 ] && echo "Usage: $0 <file>" 1>&2 && exit 1
FILE="$1" # Copy the filename to name
FILE_NEW="tempwithfile.adb"
cp "$FILE" "$FILE_NEW" # Copy the file to new_file
[ $? -ne 0 ] && exit 2
echo
echo "----[ COMPILING ]----"
echo
dir &> filelist.txt # list directory contents and write to filelist.txt
gcc writefile.c # ???
FILE_RUN="run_file.txt"
echo "$FILE" > "$FILE_RUN"
./a.out
echo
echo "----[ CLEANING ]----"
echo
make clean
make -f makefile
./semantizer -da < withfile.adb

Resources