make targets depend on variables - variables

I want (GNU) make to rebuild when variables change. How can I achieve this?
For example,
$ make project
[...]
$ make project
make: `project' is up to date.
...like it should, but then I'd prefer
$ make project IMPORTANTVARIABLE=foobar
make: `project' is up to date.
to rebuild some or all of project.

Make wasn't designed to refer to variable content but Reinier's approach shows us the workaround. Unfortunately, using variable value as a file name is both insecure and error-prone. Hopefully, Unix tools can help us to properly encode the value. So
IMPORTANTVARIABLE = a trouble
# GUARD is a function which calculates md5 sum for its
# argument variable name. Note, that both cut and md5sum are
# members of coreutils package so they should be available on
# nearly all systems.
GUARD = $(1)_GUARD_$(shell echo $($(1)) | md5sum | cut -d ' ' -f 1)
foo: bar $(call GUARD,IMPORTANTVARIABLE)
#echo "Rebuilding foo with $(IMPORTANTVARIABLE)"
#touch $#
$(call GUARD,IMPORTANTVARIABLE):
rm -rf IMPORTANTVARIABLE*
touch $#
Here you virtually depend your target on a special file named $(NAME)_GUARD_$(VALUEMD5) which is safe to refer to and has (almost) 1-to-1 correspondence with variable's value. Note that call and shell are GNU Make extensions.

You could use empty files to record the last value of your variable by using something like this:
someTarget: IMPORTANTVARIABLE.$(IMPORTANTVARIABLE)
#echo Remaking $# because IMPORTANTVARIABLE has changed
touch $#
IMPORTANTVARIABLE.$(IMPORTANTVARIABLE):
#rm -f IMPORTANTVARIABLE.*
touch $#
After your make run, there will be an empty file in your directory whose name starts with IMPORTANTVARIABLE. and has the value of your variable appended. This basically contains the information about what the last value of the variable IMPORTANTVARIABLE was.
You can add more variables with this approach and make it more sophisticated using pattern rules -- but this example gives you the gist of it.

You probably want to use ifdef or ifeq depending on what the final goal is. See the manual here for examples.

I might be late with an answer, but here is another way of doing such a dependency with Make conditional syntax (works on GNU Make 4.1, GNU bash, Bash on Ubuntu on Windows version 4.3.48(1)-release (x86_64-pc-linux-gnu)):
1 ifneq ($(shell cat config.sig 2>/dev/null),prefix $(CONFIG))
2 .PHONY: config.sig
3 config.sig:
4 #(echo 'prefix $(CONFIG)' >config.sig &)
5 endif
In the above sample we track the $(CONFIG) variable, writing it's value down to a signature file, by means of the self-titled target which is generated under condition when the signature file's record value is different with that of $(CONFIG) variable. Please, note the prefix on lines 1 and 4: it is needed to distinct the case, when signature file doesn't exist yet.
Of course, consumer targets specify config.sig as a prerequisite.

Related

Makefile and variable target from command line

Newbie question for Makefiles... why doesn't this work?
TARGET=$#
$(TARGET): * **/*
#echo "TARGET=$(TARGET)"
Where this does?
TARGET=my_target
$(TARGET): * **/*
#echo "TARGET=$(TARGET)"
When started with make my_target?
Result of the former is, "no rule to make target `my_target'."
In addition to the question "why this doesn't work," is there a workaround? I'd like to be able to specify an arbitrary target from the command line. I suppose I could react to an env var, but that makes the CLI clunky, e.g., make target=my_target build or similar.
I've searched, but I'm not getting the right hits to solve this. GNU make 3.81. Thanks!
The automatic variable $# is defined in the context of a pattern rule; outside of any rule it has no value.
If you want Make to do the same thing to whatever target you name, you can use a match-anything rule:
%:
#echo TARGET=$#

can gcc make test if a defined symbol exists in a source file

Is it possible for gcc make to test if a define exists in one of the source files ie.
ifeq (DEFINED_BLAH_BLAH,3)
#echo is equal to 3
else
#echo is not equal to 3
endif
I've looked at this and to expend on the suggestion from the comment, you could do the following. Not elegant, probably not the best available solution, but it works:
echo -ne "#if 1==MYVAL\n0\n#else\n1\n#endif\n" | cpp -P -imacros test.h
Or to call it through gcc or g++:
echo -ne "#if 1==MYVAL\n0\n#else\n1\n#endif\n" | \
gcc -Xpreprocessor -P -E -imacros test.h -
These would return shell style 0 (true for MYVAL defined in test.h and being 1) or 1 on stdout which you could test for in make / shell.
You may also want to strip all blank lines appending | grep -v '^$'.
To elaborate a bit more on the above. I create (echo) as simple file that I run through the preprocessor which check for equality on given macro and results in 0 or 1 being in the output. -P for cpp, because it's not really a C file and we do not need any of the extra bits in the output. -imacros means retain the macros defined in that file, but discard any output generated by processing it.
It should also be noted, if you'd have any conditional definitions needed to consider any defines passed to the compiler, you would need to pass them to this cpp run as well.
Also note whether you test.h being your header file you know should include the macro defintion. Or main.c being a source file including that (and other) headers doesn't really matter / yields the same result (whatver the value was when cpp was done with the file read and (pre)processed for -imacros.

Variables expansion in Make

Let us assume I have the following Makefile
N := 1
.PHONY : target_$(N)
target_$(N) :
#echo $(N)
N := 2
.PHONY : target_$(N)
target_$(N) :
#echo $(N)
The result is:
$ make target_1
2
$ make target_2
2
Is there any way of achieving what I want? My idea was to copy-paste a recipe using a variable as parameter. The variable has to be expanded in the target name and in the recipe for that target, perhaps it could appear in the dependencies too, but not for now. The problem is that recipe expansion seems to occur at the time of recipe execution, but I was expecting, and needing, it to occur at the same time that target expansion.
Another option is to use target-specific variables:
N := 1
.PHONY : target_$(N)
target_$(N) :
#echo $(N)
target_$(N) : N := $(N)
Instead of all the ugly nested function invocations, I'd say KEEP IT SIMPLE and use a command line variable as a parameter for just a single target.
target:
#echo ${N}
#if test ${N} -eq 1; then DO THIS; else DO THAT; fi
and call your Makefile with
make target N=1
That way you have all the power of the shell's control constructs. And your Makefile remains fully portable to non-GNU make.
I can achieve the result you want with automatic variables and a pattern rule:
target_%:
#echo $*
Output:
$ make target_1
1
$ make target_2
2
To make the value available in the prerequisites list you need the .SECONDEXPANSION special target:
.SECONDEXPANSION:
target_%: prerequisite_$$*
#echo $<
prerequisite_%:
#echo "do something here to build prerequisite nb.$*"
Output:
$ make target_1
do something here to build prerequisite nb.1
prerequisite_1
The expansion of make variables occurs when necessary. In target lines, such as target_$(N):, that occurs as the line is read. In command (recipe) lines, it occurs when the action is executed. Thus, you can change the value of N as the file is read, but the final value is the one that will be used when the command is executed, hence the value 2 appears twice. (Doing that will lead to confusion; don't use it in production makefiles.)
AFAIK, there isn't a way to change that behaviour. You can't freeze the macro set at the point where the command line is read. Some of the macros may not have been defined until after they were used in a command line, and you need to be able to change the values of the macros used by the default rules, etc.
I have found that the best way to do what I wanted was to stripe the parameter from the target names themselves:
TARGETS := target_1 target_2
.PHONY : $(TARGETS)
$(TARGETS) :
$(eval $#_N := $(subst target_,,$#))
#echo $($#_N)
If the target names become too complicated, one may programatically create them from the parameter set to start with.

Why is my Makefile's target specific variable getting set for all targets?

I have a Makefile similar to the following:
target1: DEFAULT_VALUE ?= $(shell bash -c 'read -p "Enter DEFAULT_VALUE to set: " value && echo $$value')
target2:
echo "Hello"
target1:
echo "World"
I expect that the code to set DEFAULT_VALUE will only execute if I run make target1, however I find that it runs even if I run make target2
Does anyone know why this happens?
Your "similar" makefile is not similar enough. Your example above works fine for me: if I run make target2 then the shell command is not executed. If I run make target1, then it is.
Please check your example before posting it here and provide one that really fails.
My suspicion is that in your real environment, whatever is represented by target2 is a prerequisite of whatever is represented by target1, which means that target2 will inherit all of target1's target-specific variable assignments.
With the above Makefile, the shell command will never run, for any target. That's because that style of variable is a recursively expanding variable, so it will be expanded (and the shell command run) every time the variable is used.
If you change the last action in the Makefile to
target1:
echo $(DEFAULT_VALUE)
echo $(DEFAULT_VALUE)
then it will run TWICE when you make target1, echoing potentially two different things
If you want the shell command to run only once, you need to use := to set it. But if you do that, it will be run when the Makefile is read (before its even considering which targets to build), so it will run regardless of which target you eventually specify.
If you want something that will only run when a given target is built, you need to put it in the actions for that target. The easiest way to do that is with a recursive make call
target1:
read -p "Enter DEFAULT_VALUE to set: " value && \
$(MAKE) real_target1 DEFAULT_VALUE=$$value

Git - how do I view the change history of a method/function?

So I found the question about how to view the change history of a file, but the change history of this particular file is huge and I'm really only interested in the changes of a particular method. So would it be possible to see the change history for just that particular method?
I know this would require git to analyze the code and that the analysis would be different for different languages, but method/function declarations look very similar in most languages, so I thought maybe someone has implemented this feature.
The language I'm currently working with is Objective-C and the SCM I'm currently using is git, but I would be interested to know if this feature exists for any SCM/language.
Recent versions of git log learned a special form of the -L parameter:
-L :<funcname>:<file>
Trace the evolution of the line range given by "<start>,<end>" (or the function name regex <funcname>) within the <file>. You may not give any pathspec limiters. This is currently limited to a walk starting from a single revision, i.e., you may only give zero or one positive revision arguments. You can specify this option more than once.
...
If “:<funcname>” is given in place of <start> and <end>, it is a regular expression that denotes the range from the first funcname line that matches <funcname>, up to the next funcname line. “:<funcname>” searches from the end of the previous -L range, if any, otherwise from the start of file. “^:<funcname>” searches from the start of file.
In other words: if you ask Git to git log -L :myfunction:path/to/myfile.c, it will now happily print the change history of that function.
Using git gui blame is hard to make use of in scripts, and whilst git log -G and git log --pickaxe can each show you when the method definition appeared or disappeared, I haven't found any way to make them list all changes made to the body of your method.
However, you can use gitattributes and the textconv property to piece together a solution that does just that. Although these features were originally intended to help you work with binary files, they work just as well here.
The key is to have Git remove from the file all lines except the ones you're interested in before doing any diff operations. Then git log, git diff, etc. will see only the area you're interested in.
Here's the outline of what I do in another language; you can tweak it for your own needs.
Write a short shell script (or other program) that takes one argument -- the name of a source file -- and outputs only the interesting part of that file (or nothing if none of it is interesting). For example, you might use sed as follows:
#!/bin/sh
sed -n -e '/^int my_func(/,/^}/ p' "$1"
Define a Git textconv filter for your new script. (See the gitattributes man page for more details.) The name of the filter and the location of the command can be anything you like.
$ git config diff.my_filter.textconv /path/to/my_script
Tell Git to use that filter before calculating diffs for the file in question.
$ echo "my_file diff=my_filter" >> .gitattributes
Now, if you use -G. (note the .) to list all the commits that produce visible changes when your filter is applied, you will have exactly those commits that you're interested in. Any other options that use Git's diff routines, such as --patch, will also get this restricted view.
$ git log -G. --patch my_file
Voilà!
One useful improvement you might want to make is to have your filter script take a method name as its first argument (and the file as its second). This lets you specify a new method of interest just by calling git config, rather than having to edit your script. For example, you might say:
$ git config diff.my_filter.textconv "/path/to/my_command other_func"
Of course, the filter script can do whatever you like, take more arguments, or whatever: there's a lot of flexibility beyond what I've shown here.
The closest thing you can do is to determine the position of your function in the file (e.g. say your function i_am_buggy is at lines 241-263 of foo/bar.c), then run something to the effect of:
git log -p -L 200,300:foo/bar.c
This will open less (or an equivalent pager). Now you can type in /i_am_buggy (or your pager equivalent) and start stepping through the changes.
This might even work, depending on your code style:
git log -p -L /int i_am_buggy\(/,+30:foo/bar.c
This limits the search from the first hit of that regex (ideally your function declaration) to thirty lines after that. The end argument can also be a regexp, although detecting that with regexp's is an iffier proposition.
git log has an option '-G' could be used to find all differences.
-G Look for differences whose added or removed line matches the
given <regex>.
Just give it a proper regex of the function name you care about. For example,
$ git log --oneline -G'^int commit_tree'
40d52ff make commit_tree a library function
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
7b9c0a6 git-commit-tree: make it usable from other builtins
The correct way is to use git log -L :function:path/to/file as explained in eckes answer.
But in addition, if your function is very long, you may want to see only the changes that various commit had introduced, not the whole function lines, included unmodified, for each commit that maybe touch only one of these lines. Like a normal diff does.
Normally git log can view differences with -p, but this not work with -L.
So you have to grep git log -L to show only involved lines and commits/files header to contextualize them. The trick here is to match only terminal colored lines, adding --color switch, with a regex. Finally:
git log -L :function:path/to/file --color | grep --color=never -E -e "^(^[\[[0-9;]*[a-zA-Z])+" -3
Note that ^[ should be actual, literal ^[. You can type them by pressing ^V^[ in bash, that is Ctrl + V, Ctrl + [. Reference here.
Also last -3 switch, allows to print 3 lines of output context, before and after each matched line. You may want to adjust it to your needs.
Show function history with git log -L :<funcname>:<file> as showed in eckes's answer and git doc
If it shows nothing, refer to Defining a custom hunk-header to add something like *.java diff=java to the .gitattributes file to support your language.
Show function history between commits with git log commit1..commit2 -L :functionName:filePath
Show overloaded function history (there may be many function with same name, but with different parameters) with git log -L :sum\(double:filepath
git blame shows you who last changed each line of the file; you can specify the lines to examine so as to avoid getting the history of lines outside your function.