In GNU make is it possible to invoke the same program with different command line parameters? - unix

Toy Example:
prog.sh:
#!/usr/bin/bash
echo $*
makefile:
%.sh_log: %.sh
sh $< $(OPTARGS) > $# 2>&1
targ1: OPTARGS=hello world
targ1: prog.sh_log
targ2: OPTARGS=goodbye
targ2: targ1 prog.sh_log
When I make targ2, targ1 is executed as expected but since the result of that is prog.sh_log, the dependency for targ2 is satisfied and it does not fire. Since make is based on timestamps, how would one use the same program with different parameters in separate parts of the makefile. My real world program is a multistep program which takes the steps numbers as command line parameters. Due to changes in source systems, I have added other programs between steps.

If you don't want the command to be run only once, dont make it a target in the makefile:
targ1: OPTARGS=hello world
targ1:
sh prog.sh $(OPTARGS) > prog.sh.log 2>&1
echo > $#
targ2: OPTARGS=goodbye
targ2: targ1
sh prog.sh $(OPTARGS) > prog.sh.log 2>&1
echo > $#

Related

How to run a command that only takes 1 arg with multiple args and redirect stdout?

I'm trying to run this command called codemaker which takes a filename as input but then writes the output to stdout instead of back to the file, so I have to redirect stdout to that file. That works fine, but I want to do this for a whole bunch of files at once, so I came up with this (based on https://stackoverflow.com/a/845928/65387):
ctouch() {
xargs -t -i -0 sh -c 'codemaker "$1" > "$1"' -- {} <<<"${(ps:\0:)#}"
}
But I can't quite get the syntax right. It looks like it's treating everything as a single arg still:
❯ ctouch foo.h bar.cc
sh -c 'codemaker "$1" > "$1"' -- 'foo.h bar.cc'$'\n'
Whereas I just want to run 2 commands:
codemaker foo.h > foo.h
codemaker bar.cc > bar.cc
How do I make an alias/function for that?
(And no, I'm not sure about that <<<"${(ps:\0:)#}" bit either. Really hard to Google. I want the usual "$#" to expand with null separators to feed to xargs)
I don't see a compelling reason to use xargs in your case. You just create additional processes unnecessarily (one for xargs, plus for each argument, one shell process).
A simpler solution (and IMO easier to understand) would be to do it with this zsh-function:
ctouch() {
for f
do
codemaker $f >$f
done
}
I think this is a lot easier to just do with printf.
ctouch() {
printf -- '%s\0' "$#" | xargs -t -i -0 sh -c 'codemaker "$1" > "$1"' -- {}
}

Make rule using `$<` only adding one object to archive library

I feel stupid, but I cannot find the solution myself:
I have a Makefile that correctly builds objects from C source, and I want to place $(OBJECTS) in an archive library.
I'm using GNU Make 4.0.
x.a: $(OBJECTS)
echo "$< ($(OBJECTS))"
$(AR) $(ARFLAGS) $# $<
(Lines are indented with TABs in original. The echo was added for debugging purposes only, The $(AR) $(ARFLAGS) $# $< was copied from make's internal rules)
Make outputs (at the end after the objects were built):
echo "log_thread/log_thread.o (log_thread/log_thread.o log_thread/thread.o)"
log_thread/log_thread.o (log_thread/log_thread.o log_thread/thread.o)
ar rv x.a log_thread/log_thread.o
ar: creating x.a
a - log_thread/log_thread.o
So I expect both objects modules to be added to the library x.a, but $< only contains one object module, as opposed to $(OBJECTS)).
My Make knowledge may be a little rusty, maybe that's why I don't get it.
Of course both object modules exist, and the output was created after having removed x.a (the original file is a bit more complex).
I'm not sure where the confusion lies:
The $< automatic variable contains the first prerequisite only, as per definition.
See the GNU make manual.
To get all prerequisites newer than the target, $? is the correct automatic variable.
So the correct rule probably is:
x.a: $(OBJECTS)
echo "$? ($(OBJECTS))"
$(AR) $(ARFLAGS) $# $?

Autodependency generation in makefiles

I am trying to understand how autodependency is generated in makefiles in the given link, i cannot understand the following piece of code:
DEPDIR = .deps
df = $(DEPDIR)/$(*F)
SRCS = foo.c bar.c ...
%.o : %.c
#$(MAKEDEPEND); \
cp $(df).d $(df).P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
rm -f $(df).d
$(COMPILE.c) -o $# $<
-include $(SRCS:%.c=$(DEPDIR)/%.P)
I got it from this link. I know it will generate dependency files but I am not able to understand what this line does:
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
Can somebody please explain me this code, so many wildcards give me butterflies, i am new to makefiles.
That is a number of distinct commands so break it up.
-e 's/#.*//'
Delete everything that starts with a # (Comments? Pre-processor directives?)
-e 's/^[^:]*: *//'
Delete everything up to a : on any that has : it.
-e 's/ *\\$$//'
Delete line-continuation slashes (and spaces before them) from the end of lines.
-e '/^$$/ d'
Delete any blank lines.
-e 's/$$/ :/'
Add : to the end of every line.
This adds explicit targets for every listed dependency file so that make "knows" how to build them to avoid the "No rule to make target" errors. The reasoning here is explained in your link in an earlier section.
Briefly, this creates a .P file with the original prerequisites list, then adds targets to it by taking each line, removing any existing target information and any line continuation (\) characters, then adding a target separator (:) to the end. This works with the values for MAKEDEPEND I suggest below; it’s possible you will need to modify the translation for other dependency generators you might use.
This is not meant to be an answer to your actual question, but since you said you were new to GNU make, I think spreading the words that a simpler way to handle auto dependencies exists won't do any harm.
Nowadays compilers like GCC or Clang can do this for you while compiling your code !
Simply pass them a preprocessor flag:
# Preprocessor flags
CPPFLAGS += -MMD
And include the generated files into the Makefile:
-include $(wildcard *.d)
And you're done.
You can learn more about the preprocessor options here for GCC, Clang simply mirror these options.
A relatively good example lies here too.

How to write a GNUMAKE based testharness

I am not very good at write make files. But I have a need to write a GNUMAKE based test harness. I did some reserch, but I was not able to find anything useful. So I am not even sure where to begin.
TEST_SUITE_DIR:=testSuite
#Get all test names with path & extention
TEST_SCRIPTS_WITH_PATH:=$(wildcard $(TEST_SUITE_DIR)/*.txt)
#Test name with out path and extention
TEST_SCRIPT_NAME:=$(notdir $(patsubst %.txt,%,$(TEST_SCRIPTS_WITH_PATH)))
vpath %.txt $(TEST_SUITE_DIR)
TEST_LOG_FILE:=$(TEST_SCRIPT_NAME)OutPutFile.txt
#This is the program ./bin/programName being tested
PUT:=man
#Test requrements:
#1) Each test in /testSuite dir should have its own logFile
#2) Each testout will be checked against a goldenout file in /goldenOutput dir to see
# if the expected resuls match with the test output
# #3) If the test & golden output file hasnt been modified, we do not want to run that test so
# we can save time
# #4) STDERR should be redirected to a stderr.log
#5) During the regression, if a test failed, test name should be written into the regressionReport.log
.PHONY: clean test
test:
for i in $(TEST_SCRIPTS_WITH_PATH); do \
echo $$i; \
$(PUT) `head -n 1 $$i` > $$iOutPutFile.txt; \
done
#$(foreach i, $(TEST_SCRIPTS_WITH_PATH), $(PUT) `head -n 1 $($i)` > $($i)OutPutFile.txt )
#$(PUT) `head -n 1 $(TEST_SCRIPTS) ` > $(TEST_SCRIPTS)logFile.log
clean:
rm -f *.d $(OBJ_DIR)/*.o $(PROG)
-include *.d
Here is my dataFile.txt(at the moment, I am only trying to get 1 command working),
date
A makefile is a way of automating certain tasks, so you can't do anything with Make until you know how how to do it without Make.
There is more than one way to do what you want (a common situation with Make), and you should think about how you want the makefile to scale. The simplest way to construct that command is probably:
man `head -n 1 dataFile.txt` > logFile.log
So this makefile would suffice:
.PHONY: all
all:
man `head -n 1 dataFile.txt` > logFile.log
Many advances on this are possible, but not until we know what you intend to do beyond this.
I'm not sure I understand what you are trying to do, why do you need dependencies for the test rule (specially that you made it phony).
Also if you want to test content of file you need to use diff not test

How can I tell if a makefile is being run from an interactive shell?

I have a makefile which runs commands that can take a while. I'd like those commands to be chatty if the build is initiated from an interactive shell but quieter if not (specifically, by cron). Something along the lines of (pseudocode):
foo_opts = -a -b -c
if (make was invoked from an interactive shell):
foo_opts += --verbose
all: bar baz
foo $(foo_opts)
This is GNU make. If the specifics of what I'm doing matter, I can edit the question.
It isn't strictly determining whether it is invoked from an interactive shell or not, but for a cron job in which the output is redirected to a file, the answer to this question would be the same as for How to detect if my shell script is running through a pipe?:
if [ -t 0 ]
then
# input is from a terminal
fi
Edit: To use this to set a variable in a Makefile (in GNU make, that is):
INTERACTIVE:=$(shell [ -t 0 ] && echo 1)
ifdef INTERACTIVE
# is a terminal
else
# cron job
endif
http://www.faqs.org/faqs/unix-faq/faq/part5/section-5.html
5.5) How can I tell if I am running an interactive shell?
In the C shell category, look for the variable $prompt.
In the Bourne shell category, you can look for the variable $PS1,
however, it is better to check the variable $-. If $- contains
an 'i', the shell is interactive. Test like so:
case $- in
*i*) # do things for interactive shell
;;
*) # do things for non-interactive shell
;;
esac
I do not think you can easily find out. I suggest adopting an alternative strategy, probably by quelling the verbose output from the cron job. I would look to do that using a makefile like this:
VERBOSE = --verbose
foo_opts = -a -b -c ${VERBOSE}
all: bar baz
foo $(foo_opts)
Then, in the cron job, specify:
make VERBOSE=
This command-line specification of VERBOSE overrides the one in the makefile (and cannot be changed by the makefile). That way, the specialized task (cron job) that you set up once and use many times will be done without the verbose output; the general task of building will be done verbosely (unless you elect to override the verbose-ness on the command line).
One minor advantage of this technique is that it will work with any variant of make; it does not depend on any GNU Make facility.
I’m not really sure what "am interactive" means. Do you mean if you have a valid /dev/tty? If so, then you could check that. Most of us check isatty on stdin, though, because it answers the questions we want to know: is there someone there to type something.
Just a note: you can also see the related discussion that I had about detecting redirection of STDOUT from inside a Makefile.
I believe it will be helpful to readers of this question - executive summary:
-include piped.mk
all: piped.mk
ifeq ($(PIPED),1)
#echo Output of make is piped because PIPED is ${PIPED}
else
#echo Output of make is NOT piped because PIPED is ${PIPED}
endif
#rm -f piped.mk
piped.mk:
#[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=$${PIPED}" > piped.mk
$ make
Output of make is NOT piped because PIPED is 0
$ make | more
Output of make is piped because PIPED is 1
In my answer there I explain why the [-t 1] has to be done in an action and not in a variable assignment (as in the recommended answer here), as well as the various pitfalls regarding re-evaluation of a generated Makefile (i.e. the piped.mk above).
The term interactive in this question seems to imply redirection of STDIN... in which case replacing [ -t 1 ] with [ -t 0 ] in my code above should work as-is.
Hope this helps.

Resources