How to find out where GNU make was started? - gnu-make

If I start (GNU) make with the -C option, it first cd into that directory and
$(CURDIR) will be set to that. I wonder if there is any mechanism to figure out in the makefile where make was originally invoked from?
That is, if I have a makefile in /some/location:
all:
#echo $(CURDIR)
#echo $(SOME_MAGIC_VARIABLE_I_AM_LOOKING_FOR)
and then I do:
$ cd /other/place
$ make -C /some/location
I would get
/some/location
/other/place
printed.
Thanks!

There is no such variable in make.
However, POSIX shells maintain and export a PWD variable to commands that they invoke. All environment variables are imported by make as make variables.
So, if you look in the make variable $(PWD) you will very likely find the original path that the shell that invoked make was using.

Related

How would I create a Makefile that remotely updates itself?

I have a makefile that I've changed up a bit here to look more generalized
.PHONY:
bash hash.sh
all: .PHONY lab tests.zip
lab: .PHONY lab.cpp
g++ -o lab lab.cpp -g -Wall -std=c++11
tests.zip:
curl -L -O https://url/tests.zip
unzip tests.zip
tests: .PHONY lab tests.zip
bash scripts/test.bash lab.cpp
clean:
rm -rf scripts tests bitset-tests.zip
I am a TA for an entry level computer science course at my university, and I've created a makefile here for my students to use to compile and test their code seamlessly.
One thing I want to do though is to have the makefile update itself every time the remote repository has a new version of it. I know I could just have them update the file themselves, but my job is to make the students focus less on setting things up and more on just coding for now, since it's entry level. So for the purposes of this, I'm sticking with the idea I have.
Currently, I'm achieving this with a script hash.sh which fetches a hash of the makefile from the repo, and compares it to a hash of the makefile in the student's directory. If the hashes don't match, then the updated makefile is fetched and replaces the old one. This is done in the .PHONY recipe. I should also mention that I don't want to add a recipe that updates it like make update, because again I want the process to be seamless. You'd be surprised how many students wouldn't utilize that feature, so I want to build it into the ones they will use for sure.
Is there a better method for this, or am I doing something wrong with this one?
Thomas has the right idea, but you can't use .PHONY here because it would means the makefile is ALWAYS out of date; make knows this so it doesn't re-exec itself if its included makefile is marked .PHONY.
You need to create a way for make to know if the makefile was changed since the last time it was run locally. I recommend you do it like this:
<normal makefile here>
Makefile: FORCE
curl https://.../Makefile -o Makefile.tmp
cmp -s Makefile Makefile.tmp && rm -f Makefile.tmp || mv -f Makefile.tmp Makefile
FORCE:
What does this do? First it uses a FORCE target which is an old-school way to emulate a .PHONY target, which is always out of date, without actually using .PHONY (which as I mentioned above, is handled specially by GNU make in this situation).
Second it retrieves the Makefile but only updates the local makefile if it has changed. If it hasn't changed, it doesn't update the local makefile and so make won't re-exec itself.
The whole stuff with fetching a hash sounds overly complicated. If you're going to do a fetch anyway, why not unconditionally fetch the entire makefile? It saves a network round trip, which is probably the limiting factor; the actual data is probably just a few kB anyway.
If you're using curl, notice the --time-cond option, for example:
curl https://... --time-cond Makefile -o Makefile
This will only fetch and update the Makefile if it's newer than the mtime of the current file. It works by sending an If-Modified-Since HTTP header, so you'll need some cooperation from the server for this to work reliably.
If using GNU make, you can use another neat trick: Remake the makefile itself. If you have a rule whose target is Makefile, it will be executed before anything else happens, and make will re-read the updated Makefile before proceeding:
.PHONY: Makefile
Makefile:
curl https://... --time-cond Makefile -o Makefile
Note that this will lead to infinite loops if for whatever reason the --time-cond leads to an unconditional update, so it wouldn't hurt to guard against that:
.PHONY: Makefile
Makefile:
[[ ! -v MAKE_RESTARTS ]] && \
curl https://... --time-cond Makefile -o Makefile

How can I check syntax for Make but be sure I am not executing?

We work with Make files and want to create a precommit check in HG to check Makefile syntax. Originally, our check was just going to be
make -n FOO.mk
However, we realized that if a Makefile were syntactically correct but required some environment variable to be set, the test could fail.
Any ideas? Our default is to resort to writing our own python scripts to check for a limited subset of common Makefile mistakes.
We are using GNUmake.
$ make --dry-run > /dev/null
$ echo $?
0
The output is of no value to me so I always redirect to /dev/null (often stderr too) and rely on exit code. The man page https://linux.die.net/man/1/make explains:
-n, --just-print, --dry-run, --recon
Print the commands that would be executed, but do not execute them.
A syntax error would result in the sample output:
$ make --dry-run > /dev/null
Makefile:11: *** unterminated variable reference. Stop.
It is not a good idea to have makefiles depend on environment variables. Precisely because of the issue you mentioned.
Variables from the Environment:
... use of variables from the environment is not recommended. It is not wise for makefiles to depend for their functioning on environment variables set up outside their control, since this would cause different users to get different results from the same makefile. This is against the whole purpose of most makefiles.
References to an environment variable in the recipe need a $$ prefix so it is not that hard to find references to the pattern '[$][$][{] or the pattern [$][$][A-Z] which will find the direct references. A pretty simple perl filter (sed script) finds them all.
To find the indirect ones I would try the recipe with only PATH set and HOME set to /dev/null, and SHELL set to /bin/false. Make's macro SHELL is not the environment $SHELL, so you can get the recipes to run, you'll have to set SHELL=/bin/sh in the recipe file to run the command from the recipe. That should shake out enough data to help you find the depends.
What you do about the results is another issue.

Adding directory to PATH through Makefile

I'm having some trouble in exporting the PATH I've modified inside the Makefile into the current Terminal.
I'm trying to add to the PATH, the bin folder inside wherever the Makefile directory is.
Here's the relevant strip of the makefile:
PATH := $(shell pwd)/bin:$(PATH)
install:
mkdir -p ./bin
export PATH
echo $(PATH)
The echo prints it correctly but if I redo the echo in the terminal, the PATH remains the same.
Thanks in advance for the help.
If you're using GNU make, you need to explicitly export the PATH variable to the environment for subprocesses:
export PATH := $(shell pwd)/bin:$(PATH)
install:
mkdir -p ./bin
export PATH
echo $(PATH)
What you are trying to do is not possible. Make is running in another process than the shell in your terminal. Changes to the environment in the make process does not transfer to the shell.
Perhaps you are confusing the effect of the export statement. export does not export the values of the variables from the make process to the shell. Instead, export marks variables so they will be transfered any child processes of make. As far as I know there is no way to change the environment of the parent process (the shell where you started make is the parent process of the make process).
Perhaps this answers will make the concept of exporting variables to child processes a bit clearer.
Perhaps you can rely on the user to do it for you. Note the quoting
install_hint:
#echo "Execute this command at your shell prompt:"
#echo "export PATH=$(shell pwd)/bin:\$$PATH"

recursive gmake question

I need to write a quick makefile for building all my projects. This is C++ code and I am using gmake.
Say I have a list of directories, I want to cd to each, issue gmake command and if it succeeds, go to the next one and so on.
I cooked this by looking at the gmake manual
.PHONY: all clean dirs $(DIRS)
dirs: $(DIRS)
$(DIRS): \n\t
$(MAKE) -C $#
It works for the "all" target - if I just type gmake, it does the right thing. But if I do gmake clean it does nothing.
I am learning gmake as I go, so I am certainly doing something silly here :)
Thanks for any help.
In order to recursively make something other than the first target (I'm guessing all is your first target), you need to give the sub-make an idea of what to build. You can do so with the MAKEFLAGS and MAKECMDGOALS variables.
For example:
$(DIRS):
$(MAKE) -C "$#" $(MAKEFLAGS) $(MAKECMDGOALS)
Your rule was not passing along the target names, e.g. clean, so the sub-make had no work to do (since all was already built).

makefile with directory tree creation suitable for parallel (-j ) build

My project needs temporary directories which are created during the build using mkdir -p similarly to this:
all: dirtree $(OBJFILES)
dirtree:
#mkdir -p $(BUILD)/temp_directory
But this approach cannot be used with the -j switch, because first of the OBJFILES get compiled before the mkdir target is made.
Is there a standard way to do this?
The problem with your makefile is that creation of your object files does not depend on creation of the relevant directories (only a phony "all" target does). This kind of dependency is necessary for -j option, and even without it your makefile works only by chance. There are two (right) ways to impose the dependency in question.
Directories as separate targets
You created the target for directory creation; what left is just put it as a prerequisite to object file rule:
$(BUILD)/temp_directory/%.o: %.c | dirtree
$(CC) $^ -o $#
The pipe symbol | means that dirtree is an "order only prerequisite". It is used when "dirtree" is a prerequisite but changes in the dirtree do not invalidate object files and do not affect the outcome of compilation command.
Use of "order-only" prerequisite is important here. The thing is that dirtree target would be remade at each Make invocation. That would cause everything that depends on it be remade as well, so it would rebuild all object files every time.
Create directories in shell commands
Another way is to ensure that the directory is created immediately before you invoke compilation
$(BUILD)/temp_directory/%.o: %.c
#mkdir -p $(#D)
$(CC) $^ -o $#
Note the usage of $(#D). This is expanded as "the directory for the target file". So it may be used uniformly in many places, and even with aid of a variable.
Mkdir=#mkdir -p $(#D)
$(BUILD)/temp_directory/%.o: %.c
$(Mkdir)
$(CC) $^ -o $#
$(INSTALL_DIR)/%: src_dir/%
$(Mkdir)
cp -p $^ $#
Both ways ensure that the directory is created before the compilation commands are invoked. Both ways require you to write some text (either | dirtree or $(Mkdir)) at each rule that needs it. Both ways are -j compatible, but the second solution requires mkdir -p to be thread-safe (as two such commands at once may try to create the same directory, and one of them would fail).
While most systems implement it in such a way that mkdir -p is more or less thread safe, on some systems (as in some Solaris systems, for example), they are less thread-safe than the others. However, even in GNU toolchain mkdir -p may fail if they simultaneously invoke the same mkdir(2) library call.
If you want to be very safe, you can work this around as well. What could be the problem? That two mkdir -p scripts try to create the same directory, and clash somewhere inside C library. Then, one of these mkdir-s will succeed, and the other will fail. However, if the mkdir you invoked failed, then it could be thread-unsafety-related failure only if the directory had been created by a concurrent mkdir. So it would be enough to just check that the target directory is created after mkdir invocation:
Mkdir=#mkdir -p $(#D) || test -d $(#D)
(This solution also has an issue with mode: mkdir may fail when directory exists, but doesn't conform to umask, so you might want to check that as well. But that's too much I guess.)
I'm not sure I fully understand your question. However, I can say this: if your build breaks when you add parallelism, then it's an indication that you haven't defined the dependencies correctly. Ask yourself, "Do the directories need to exist before the object files are generated?" If the answer is "yes", then the directories should be listed as prerequisites of the object files. In other words:
${OBJFILES}: dirtree
And yes, that is pretty much the standard way to do this :)
You could have the rules for building the object files call mkdir -p as their first action.

Resources