Can I get standard error redirected from make info command to a file? E.g. if I wanna print compilation command and redirect 2> to a file, the file gets just an empty string but the command inside the info parenthesis is printed, like bellow:
ifdef VERBOSE
verbose := #cat /tmp/compilation.out
endif
development: $(source)
# Pipe to cat is just to redirect signals from make to don't exit on error
#$(info $(CC) -o $(target) $^ $(DEBUG)) 2> /tmp/compilation.out | cat
$(verbose)
#cat /tmp/compilation.out >> $(log)
Make variables and functions are expanded by make before it invokes the shell to run the recipe. The results of expansion are passed to the shell.
2> is a shell construct, it's not interpreted by make.
The info make function is defined to write its arguments to stdout, and expand to the empty string. So, when make expands this recipe line:
$(info $(CC) -o $(target) $^ $(DEBUG)) 2> /tmp/compilation.out | cat
First it expands the arguments to info and writes the results to stdout, then it replaces the info function with the empty string, then it invokes the shell with the results; so the shell sees:
2> /tmp/compilation.out | cat
The short answer is no, there is no way to get make's info function to write output to a file. You'll have to use something the shell knows about, like echo:
#echo '$(CC) -o $(target) $^ $(DEBUG)' 2> /tmp/compilation.out | cat
Related
I have a makefile which produces an executable from several object files and I include a version number in each object file as it is compiled. However, I want the version number to be incremented only when a particular object file is created (ptarget below, the one containing main). I tried to do this using a conditional statement in the recipe:
ptarget:=$(addsuffix .obj,$(ouf))
%.obj : %.cpp
$(CXX) $(CXXFLAGS) $< -Fo$#
$(info $(ptarget))
$(info $#)
ifeq ($#, $(ptarget))
perl $(perlDir)versionBump/bump.pl -inc -f $(versionFile)
endif
I can see from the info commands that only when ptarget is built that $# == $(ptarget) -- I also tried using strip command to make sure no hidden whitespace, but the perl command to increment the version is never executed, it starts with a tab.
Just to add, this all works just fine without the conditional but the increment happens multiple times during a build, which is what I am trying to avoid. This example suggests it should work but...
This is a very common misunderstanding about make. ifeq is a make statement and is evaluated when make parses the Makefile. What you need is a conditional that gets evaluated when the recipe is executed by the shell, that is a shell if:
%.obj : %.cpp
$(CXX) $(CXXFLAGS) $< -Fo$#
$(info $(ptarget))
$(info $#)
if [ "$#" = "$(ptarget)" ]; then \
perl $(perlDir)versionBump/bump.pl -inc -f $(versionFile); \
fi
Do not forget the line continuations (the trailing \).
Note that I do not want to redirect all make output to file. I only want the output from a $(warning) command to file.
someTarget:
$(warning building $# using $?) >> someLogFile.txt
My example above does not redirect the output from $(warning to someLogFile. Is there a way to do it? Maybe redirect it to a variable and then echo that to a file?
Thanks.
is there a way to redirect $(warning) or $(info) statements to file?
Here's one for GNU Make, but it's not pretty:
Makefile
LOG := log.txt
TARGET_ACQUIRED = \
$(shell echo 'NO_SUCH_TARGET:' | $(MAKE) --eval='$$(info Target acquired: $#...)' -s -f - >> $(LOG))
target_a: target_b
$(TARGET_ACQUIRED)
touch $#
target_b:
$(TARGET_ACQUIRED)
touch $#
clean:
rm -f target_* $(LOG)
With which you'll get:
$ make
touch target_b
touch target_a
$ cat log.txt
Target acquired: target_b...
Target acquired: target_a...
To understand this ruse, see the GNU make commandline options.
If you want this for the purpose of debugging a makefile, you'd probably
fare better with GNU Make's --debug options, documented at the same place.
Is it possible to take a part of the output of a recipe line and use to to set a make variable? For example:
%.o: %.cc
$(eval __time_$* := $(shell date "+%s.%N"))
$(COMPILE.cc) -o $# $<
#echo `date +%s.%N` - $(__time_$*) | bc | xargs printf "%s compile time %6.3f sec\n" $#
Instead of echo'ing the time I want to capture it in a make variable. However, when I use eval and shell instead of the 3rd line above, similar to the 1st line, make appears to read all lines at once, and then schedule the actual recipe shell calls for later. So the result is that the recipe lines are all evaluated at once, so there is little time difference.
The only way I can see to do what you want is to run the compiler inside a $(shell). One problem with doing this is that Make doesn't seem to see the errors, when the command in $(shell) fails. Other than that, here's what I think you wanted:
%.o: %.cc
$(eval __start_$* := $(shell date "+%s.%N"))
#echo $(shell $(COMPILE.cc) -o $# $<)
$(eval __dur_$* := $(shell echo `date +%s.%N` - $(__time_$*) | bc | xargs printf "%s compile time %6.3f sec\n" $#))
Due to the caveat mentioned above, I recommend using the 'time' command to collect these stats, as #wojtow said. If you have different requirements, consider wrapping compilation with a script that collects your timing data and logs it to a file. Then, process the contents of the file as a post-processing step.
The only way I can see to do what you want is to run the compiler
inside a $(shell). One problem with doing this is that Make doesn't
seem to see the errors, when the command in $(shell) fails.
Droid Coder presented a clever approach. A way to solve the problem of make not seeing errors (thereby not aborting on error) is to pass the exit status via the output of the command, while the original compiler output (if any) is redirected to the standard error stream. The rule then is e. g.:
%.o: %.cc
$(eval __start_$* := $(shell date "+%s.%N"))
exit $(shell $(COMPILE.cc) -o $# $< >&2; echo $$?)
$(eval __dur_$* := $(shell echo `date +%s.%N` - $(__time_$*) | bc …))
This is a simple question for a starter like me, but what can I do to do like the following
all: run
run:
DIR=bin/
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $(DIR)$#
Thanks.
Why not go like this?
DIR=bin/
all: $(DIR)/run
$(DIR)/run:
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $#
As written, you have an assignment to the shell variable DIR on one command line. On the next line, you have the expansion of a make variable DIR. This doesn't work because the two lines are executed by different shells, and in any case, make expands $(DIR) before running the shell and DIR is not a make variable.
You could make it work like this:
all: run
run:
DIR=bin/; \
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $${DIR}$#
The backslash means the two lines are treated as one (so much so that the semicolon is needed). The $${DIR} notation is expanded by make to ${DIR} (more precisely, $$ expands to $ and make ignores the {DIR}), and then the shell expands ${DIR} from the value set previously. You could, of course, omit the braces.
However, the answer by BeSerK is probably what you're looking for.
I have a Makefile building many C files with long long command lines and we've cleaned up the output by having rules such as:
.c${MT}.doj:
#echo "Compiling $<";\
$(COMPILER) $(COPTS) -c -o $# $<
Now this is great as the # suppresses the compilation line being emitted.
But when we get an error, all we get is the error message, no command line.
Can anyone think of a "neat" way to emit the command line?
All I can think of doing is echoing it to a file and have a higher level make catch the error and cat the file. Hacky I know.
Tested and it worked (GNU make in Linux):
.c${MT}.doj:
#echo "Compiling $<";\
$(COMPILER) $(COPTS) -c -o $# $< \
|| echo "Error in command: $(COMPILER) $(COPTS) -c -o $# $<" \
&& false
This question is pretty old, but for those of you Googling, I think what I’ll do in this situation is alias make to make -s (silent mode) in my shell, and only put the # prefix before lines where echo or other diagnostic commands are being invoked. When I want the full output from make, I will override my alias by calling it as \make.
Also note that in this situation that you’ll need to do the typical thing and put the #echo on its own line, with the actual rule commands on separate lines and without #’s.
A simple solution would be to use a simple script abc like the following:
#!/bin/bash
$#
code=$?
if (( code )); then
echo error running $#
fi
exit $code
Then you can write abc $(COMPILER) $(COPTS) -c -o $# $< in your Makefile. Do note that this does not work when you have pipes or redirects (as they will be applied to abc instead of the command you want to run).
You can also just put similar code directly in the Makefile if that's preferable.
I recently used a utility called logtext for the likes of tracking what output had occurred during the course of a bat file executing. Check it out, you may find this pretty useful if you want to know what error occurred where.