make doesn't see changes? - makefile

Scenario 1:
I checked out a project, and made some changes to a source file, and did make, and make sees the changes.
Scenario 2:
I checked out the project again to different directory (some reasons), copied the modified source file here, and did make and nothing happens, if I run the program, I don't see my changes, make doesn't see that I made change to this source file

make uses the timestamps of the files to determine what to build.
Perhaps your version-control system is checking all files out with the current time. When you copy your source over, it has a time in the past, making make think that the object file (presumably in your checkout) is newer than your source.
If that's the case, you can use touch to set the timestamp of a file to now.

Adding to existing answers:
To touch the targets, you can use the -t or --touch option of make. This option will not make the target but just touch it so that the next time you invoke make, the target will be made.
Alternatively you can use the -B or --always-make option which will unconditionally make the target irrespective of the modification of it's dependent(s).

okay, I just touched the copied (modified) source and now make recognizes the changes.

If you used cp to copy files options -a --archive -p --preserve will preserve the timestamp. That is not what you want!
Add option --no-preserve=timestamps
cp [options] --no-preserve=timestamps .....

Make sure you have your .PHONY tags and they are correct

Related

make rebuild target depending on zip file

Why make rebuilds the target (I suppose) if the dependency is a binary file?
To reproduce:
create (and enter it) a new empty directory
download the GameLift SDK (it is just an example: the Makefile content on this question is an example with this file)
create a simple Makefile with the content below
issue more times the make command
all: GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt
GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt: GameLift_12_22_2020.zip
unzip -oq GameLift_12_22_2020.zip
I would have expected to see the unzip command to be executed only first time I issue the make command, but it continue to be executed in next make runs... why?
There are two possibilities, we cannot know which is the case with the information you've provided.
The first is that the file GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt is not present in the zip file, so the second time make runs it looks to see if that file exists and it doesn't, so it re-runs the rule. If, in the same directory you run make, you use ls GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt (after the unzip runs) and you get "file not found" or similar, this is your problem.
If that's not it, then the problem is that the timestamp of the file in the zip file is older than the zip file itself, and when unzip unpacks the file it sets the timestamp to this older time.
So when make goes to build it finds the CMakeLists.txt file but the modification time is older than the zip file, so make unpacks the zip file again to try to update it.
You can use ls -l to see the modification time on that file. If this is the case you should touch the file when you unpack it, so it's newer:
GameLift_12_22_2020/GameLift-SDK-Release-4.0.2/GameLift-Cpp-ServerSDK-3.4.1/CMakeLists.txt: GameLift_12_22_2020.zip
unzip -oq GameLift_12_22_2020.zip
touch $#

How to prevent make from regenerating .gitignore?

I'm just learning to use make and I wanted to use my Makefile to generate a simple .gitignore file. I've tried to write various versions of the file but none seem to work the way I'd expect. The main problem I have is that the .gitignore file is remade every time I run make even though nothing has changed.
Here's what I've tried:
".gitignore" :
touch .gitignore
echo 'node_modules' >> .gitignore
This re-appends node_modules to the .gitignore every time I run make. Without the quotes around .gitignore it fails with the error: make: *** No targets. Stop.. I tried adding a bogus dependency that doesn't change (i.e. ".gitignore" : Makefile) but the same rebuilding occurs. I've tried making .gitignore depend on itself but it just tells me that the circular dependency was dropped and I get the same behavior. I've tried with GNU make versions 3.81 and 4.3.
I eventually want to generate an entire repository with make and I don't want the timestamp on the file to change between runs because that causes problems with git.
Quotes are not special characters in a makefile (generally speakig).
A rule like this:
".gitignore" :
tells make that your target is named, literally, ".gitignore" (including quotes). Since there is no file named that way in the directory, make runs your rule to generate it.
Change your rule to remove the quotes:
all: .gitignore
.gitignore:
...
and it should work as you expect. The reason you can't just use .gitignore by itself is that make treats targets that start with a period specially: such a target cannot be the default goal in a makefile.

Touching targets does nothing in Makefile

I keep staring at my code and wondering why touching my file does nothing:
FILES = file1 file2
myapp: $(FILES) docs/img/barline.svg
cd docs; make html
docs/img/barline.svg: docs/notebooks/barlines.ipynb docs/data/smbinning
cd docs/notebooks; jupyter nbconvert --execute --output-dir html barlines.ipynb
If I touch docs/img/barline.svg and run make, the jupyter command is not run (as it should be, shouldn't it?). The svg file is generated by running the jupyter command. If I delete it, everything works as expected, but touching seems to do nothing.
Bah, I just spend all this time writing some other answer and it turns out I misinterpreted your problem, oops.
You say that you expect jupyter to run if you touch barline.svg.
Make uses the file timestamps to determine when to run. The rule, for non-phony targets, is simple: If any of the prerequisites is newer than the target, or the target doesn't exist, then the target is rebuilt. Otherwise, it is not. In your case:
docs/img/barline.svg: docs/notebooks/barlines.ipynb docs/data/smbinning
Touching the svg won't meet the conditions for that rule, since it is the target.
Make will only run if barlines.ipynb or smbinning is newer than barline.svg. So your expectations are off, touching barline.svg won't do anything except potentially cause any targets that use it as a prerequisite to be rebuilt themselves.
For this you'd have to touch barlines.ipynb or smbinning. That'll make them newer than barlines.svg and force it to rebuild. Or you could just delete barlines.svg, as you've discovered.
Make doesn't track build timestamps in any special place elsewhere, all its info comes from the filesystem, so if you touch a target it doesn't really have anything to compare it to to say "hey somebody modified the target, maybe I should rebuild it" (and you wouldn't want that behavior anyways, for other reasons).

what is the difference between 'make after make clean' and just 'make'?

there are C files in a directory and I have a makefile.
I usually use makefile to compile.
I have been wandering the role of the 'make clean'
'make clean' is just to remove files.
Though I didn't use 'make clean', t
he error and warning was shown up when there were something wrong.
I cannot realize why I need to use 'make clean' whenever I change the source file.
make is a utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them.
To prepare to use make, you must write a file called the makefile that describes the relationships among files in your program, and the states the commands for updating each file.
Once a suitable makefile exists, each time you change some source files, this simple shell command:
make
suffices to perform all necessary recompilations. The make program uses the makefile data base and the last-modification times of the files to decide which of the files need to be updated.
We generally use make clean as a generic way to tell clean up the code.ie; remove all the compiled object files from the source code. You can name it as anything you like.
It's convention only. The convention is that clean will return you to a state where all you have is the "source" files. In other words, it gets rid of everything that can be built from something else (objects, executables, listings and so on).
So make clean ; make is expected to build everything from scratch. And, in fact, you'll often find a rule like:
rebuild: clean all
which will do both steps for you.
You should never have to do a clean unless you're wanting to (for example) copy just the source files somewhere. If you have to do so after editing a file, then your Makefile is not set up correctly.
And, if you make and get an error, you should get exactly the same error if you subsequently make without fixing said error.

How do I get GNU make to remove intermediate directories created by implicit rules?

GNU make automatically removes intermediate files created by implicit rules, by calling rm filename at the end. This obviously doesn't work if one of the targets was actually a directory. Take the following example:
.PHONY: all
all: test.target
%.target: tempdir.%
touch $#
tempdir.%:
mkdir -p $#
make -n reveals the action plan:
mkdir -p tempdir.test
touch test.target
rm tempdir.test
Is it possible to get GNU make to correctly dispose of intermediate directories? Perhaps by changing rm to rm -rf?
There is no way to make this happen. Although GNU make prints the command "rm", really internally it's running the unlink(2) system call directly and not invoking a shell command. There is no way to configure or modify the command that GNU make runs (except by changing the source code of course).
However, I feel I should point out that it's just not going to work to use a directory as a normal prerequisite of a target. GNU make uses time-last-modified comparison to tell when targets are up to date or not, and the time-last-modified of a directory does not follow the standard rules. The TLM of a directory is updated every time a file (or subdirectory) in that directory is created, deleted, or renamed. This means you will created the directory, then have a bunch of files that depend on it: the first one is built and has timestamp N. The last one is built and has timestamp N+x. That also sets the directory's timestamp to N+x. Then the next time you run make, it will notice that the first one has an older timestamp (N) than one of its prerequisites (the directory, at N+x), and rebuild.
And this will happen forever, until it can build the remaining "out of date" prerequisites fast enough that their timestamp is not newer than the directory.
And, if you were to drop a temporary file or editor backup file or something in that directory, it would start all over again.
Just don't do it.
Some people use an explicit shell command to create directories. Some people create them as a side-effect of the target creation. Some people use order-only prerequisites to ensure they're created on time.

Resources