How GNU make manages file version control? - makefile

I'm learning GNU make . Suppose I have a hello_world.c file and a Makefile:
hello_world.c:
#include <stdio.h>
int main() {
printf("Hello World!\n");
return 0;
}
Makefile:
hello: hello_world.c
gcc hello_world.c -o hello_world
Now, I think hello is my target and hello_world.c is its dependency. If make somehow detects that hello_world.c is newer than its object-file, it executes the corresponding command.
1- How does make manage file version control and how does it detect something is newer than something else and needs updating?
2- If I change hello_world.o using an editor and corrupt the file, it obviously does not execute but make hello reports that nothing needs to be done!
I mean, make only checked that the dependency is older than the target and exited doing nothing. I think it should have detected that this target is not the one corresponding to its latest invocation. Somehow, it should have compared the "combination of dependency AND target" history instead of just comparing dependency's history w.r.t. target.
3- Is this the limitation of make? How can I circumvent this issue? Because there maybe some external apps messing with the target of my make operation.

1- How does make manage file version control and how does it detect something is newer than something else and needs updating?
You already know the answer to this question. You said it in your next paragraph: "I mean, make only checked that the dependency is older than the target and exited doing nothing." That's right. make updates dependent targets when the dependencies of targets are newer.
2- If I change hello_world.o using an editor and corrupt the file, it obviously does not execute but make hello reports that nothing needs to be done! I mean, make only checked that the dependency is older than the target and exited doing nothing. I think it should have detected that this target is not the one corresponding to its latest invocation. Somehow, it should have compared the "combination of dependency AND target" history instead of just comparing dependency's history w.r.t. target.
You are asking make to do a lot more than what it was intended to do.
3- Is this the limitation of make? How can I circumvent this issue? Because there maybe some external apps messing with the target of my make operation.
From your point of view, it does seem like it is a limitation of make. However, I want to point out that you are sabotaging the workings of make by updating a target manually.
How can you circumvent it?
Don't manually modify targets that are built by make.
Manually update the time stamp of one of the dependencies and then run make. You can use the command touch for that.
Provide a dummy target named clean that will remove all the dependent targets. Then, run make clean followed by make.
Provide a dummy target named rebuild. Forcefully build whatever you need to build in that target. Then, run make rebuild.

The make program simply compare the modification time-stamps of the target (hello) and the dependency (hello_world.c) files.
If the time-stamp of a dependency file is newer than the targets, then it execute the commands.

How does make manage file version control and how does it detect something is newer than something else and needs updating?
Very simple: make does not care about file versions.
Detecting if something is newer than something else is simply done by comparing time stamps from file system.
If I change hello_world.o using an editor and corrupt the file, it obviously does not execute but make hello reports that nothing needs to be done! I mean, make only checked that the dependency is older than the target and exited doing nothing. I think it should have detected that this target is not the one corresponding to its latest invocation. Somehow, it should have compared the "combination of dependency AND target" history instead of just comparing dependency's history w.r.t. target.
You did not tell make anything about your .o file. Why should make check the timestamp of that file?
Checking the dependency is exactly what make is expected to do.
How should this tool possibly know that a file calles hello_world.o is involved in the process if you don't tell it? There is no magic happening but only the rules of your Makefile are followed.
Is this the limitation of make? How can I circumvent this issue? Because there maybe some external apps messing with the target of my make operation.
You can specify hierarchical dependencies:
all: hello
hello: hello_world.o
<gcc linker command...>
hello_world.o: hello_world.c
gcc hello_world.c -o hello_world

Related

Is there better way to build one cpp which is in Makefile project?

I have one huge makefile-based project.
I download it from git and modify some .cpp files.
I want to just build those files I modifyed.
Now I think I can use make -n |grep > build.sh then run the build.sh.
Is there better way ? Thanks.
Without any information about how your makefile works there's not much definitive we can say.
If you just want to rebuild foo.cpp, you should be able to run make foo.o. But of course, this depends on how complex your makefiles are, whether they're recursive, whether they build the object file in a different directory, etc.

How can I continue an interrupted compilation after an error occurred?

After an error occurred because of a missing flag or incorrectly set environment variable, is it possible to continue compiling once the mistake has been fixed?
I regularly use CMake and make to compile toolkits that take quite a while to compile and, also regularly, I accidentally set variables incorrectly in the process. Just now for example, I was attempting to include OpenInventor headers which on my machine are located in the directory /Users/user/software/prod/coin/include/Inventor.
I mistakenly passed
-DINVENTOR_INCLUDE_DIR=/Users/user/software/prod/coin/include/Inventor
rather than the correct
-DINVENTOR_INCLUDE_DIR=/Users/user/software/prod/coin/include
This only became an issue after 30 minutes when about 95% of the compilation was completed. Because I knew that reconfiguring using CMake would force a recompilation from scratch, I tried to add -I/Users/user/software/prod/coin/include to CMAKE_CXX_FLAGS in CMakeCache.txt but to no avail–it still recompiled from scratch. Since only a single source file actually includes the headers in question, it would be desirable if I could start compiling from the point where it exited with an error once the relevant path has been corrected. How can I do this and, as an aside, why does it force the compiler to start from scratch?
I'm using CMake version 3.11.1 and clang (Apple LLVM version 9.1.0) on macOS 10.13
CMake does not need to recompile everything just because it regenerates its makefiles. It will still perform normal make avoidance operations. However CMake does track the compiler options used to build each target, so if you make a change in the compiler options for all the targets then they'll all need to be rebuilt.
If this compiler option is only needed for one target, you can add it to just that target an no others, with something like this:
set_property(SOURCE my_source.c APPEND PROPERTY
COMPILE_FLAGS -I/foo/bar)
then it should only rebuild that one source file.
CMake looks for files' "last modified" times to decide which files need recompilation. But if you change the input to CMake itself, then it needs to regenerate the Makefiles and therefore recompile everything. But still, one hack may be possible...
CMake stores information about the include directories and the libraries to be linked in various text files in the build directory. So one hack (not recommended, but works) can be to modify these text files.
In the particular example that you mentioned, the hack would be to search and replace all occurrences of /Users/user/software/prod/coin/include/Inventor with /Users/user/software/prod/coin/include in all the files of the build directory.
(As an aside, if you don't already know, you can use make -j <n> to build using multiple threads which can considerably decrease the build times.)

Difference Between 'gmake' and 'gmake clean'

I am currently working on a project on a student job and writing some code. Whenever I want to compile my file, my supervisor told me to first do gmake clean and then do gmake. Otherwise,some errors from previous gmake may not be solved in the fresh gmake.
My project has a lot of unnecessary files that I am not editing but since i gmake clean everytime, the compiler compiles them again everytime which takes a lot of time.
So, if I am not changing the other files, then I don't need to recompile them and the only file that I change is always recompiled simply with gmake, right? So why should I gmake clean everytime?
Is my supervisor just telling me a good programming practice or am I missing some important concept of gmake clean?
The difference between gmake and gmake clean is that the former builds the default target while the latter "builds" the specific target clean, which usually consists of clean-up instructions to remove files that were created during the build process (object files, temporary files, generated code, …). gmake clean should restore the source tree to a clean state. However, if it actually succeeds depends on how the source tree looks like and what instructions the target contains.
definitely not a best practice... this is usually the quick and dirty solution when the makefile contains a few mistakes and the dependencies are not ok
do gmake clean before gmake in case you have difficulties to compile. just to check if the problem does not come from an old compiled file.
otherise do only gmake in orther to compile your newest changed files.
you superior gived you an advice to handle difficult and strange compilation behavior.

Force QtCreator to run "qmake" when building

In some of my projects I use some pre-build step(s) configured in the .pro file. So qmake will execute this step whenever it is activated.
Now in QtCreator, when I build (also when completely rebuilding the whole project), it doesn't always run qmake, since it tries to be clever and optimize this. It only runs it when the .pro file has been changed, causing several issues.
Also a common issue is, when you make a class inheriting from QObject after running qmake on that file, it will not notice it and hence not run moc on it. Such issues are solved by simply manually running qmake via the "Build" menu in QtCreator. But if I forget this I am sometimes confused by the compile errors I get because of this and this is really annoying.
(How) can I force QtCreator to do this step always when building a project?
I thought about adding qmake as a build step in the project configuration, but this seems to be a dirty hack to solve this problem.
Another dirty hack but a little more flexible: On Linux/Mac add "touch yourprojectfile.pro" as a build step or assign an external tool call to sth. like touch "*.pro" run in your current projects working directory. When the pro file is altered (which is mimicked by touch) qmake is executed. Not much cleaner but the external tool plus hotkey solution is more flexible than adding qmake into the buildsteps of each an every project.
Update:
I finally found a completely satisfactory solution for this.
In the pro file add:
qmakeforce.target = dummy
qmakeforce.commands = rm -f Makefile ##to force rerun of qmake
qmakeforce.depends = FORCE
PRE_TARGETDEPS += $$qmakeforce.target
QMAKE_EXTRA_TARGETS += qmakeforce
This deletes the generated Makefile an thus forces qmake to rerun for every build.
I think your best option is customize your QtCreator .This can be done by write a plugin for QtCreator ,or you can change the souce code of a plugin named Qt4ProjectManager ,then build it for yourself . This might be complex ,however, can be a solution.
What I've done is created a makefile that explicitly calls qmake. Of course, that means I have two makefiles, but in my project file, I have
MAKEFILE = makefile_qt
which means that the generated makefile will have that name.
So, for the makefile I manually created, I have:
default:
qmake;
${MAKE} -f makefile_qt;
Then, from QtCreator, I just call the regular make, which will default to makefile. Or you can leave the Qt-generated makefile as is, and just call make -f makefile to call your manually created one. I forget which has precedence, makefile or Makefile, and I'm not sure if it is always the same.
I'm using the following codes in my .pro file.
QMAKE_CLEAN += ./Makefile
This makes run qmake whenever executing clean.

Passing C/C++ #defines to makefile

I develop C/C++ using the Eclipse IDE. Eclipse also generates a makefile which I don't want to edit as it will simply be overwritten.
I want to use that makefile for nightly build within Hudson.
How do I pass #defines which are made in the project file of the IDE to the makefile ? (and why doesn't Eclipse already include them in the generated makefile?)
I actually had this figured out once, then accidentally overwrote it :-( But at least I know that it can be done...
If you are running make from the command line, use
make CPPFLAGS=-DFOO
which will add -DFOO to all compilations. See also CFLAGS, CXXFLAGS, LDFLAGS in the make manual.
You could write a small program to include the headers and write a makefile fragment which you include in the main makefile (requires GNU make).
This is a fairly ugly solution that requires a fair amount of hand hackery. More elegant would be to parse the project file and write the makefile fragment.
For GCC use -D define.
OP commented below that he wants to pass the define into make and have it pass it on to GCC.
Make does not allow this. Typically you just add another make rule to add defines. For instance 'make release' vs 'make debug'. As the makefile creator you make the two rules and have the defines right in the make file. Now if Eclipse is not putting the defines into the makefile for you, I would say Eclipse is broken.
If you're using autotools another options is to have 2 directories 'bin/debug' and 'bin/release'.
# Simple bootstrap script.
# Remove previously generated filed and call autoreconf.
# At the end configure 2 separate builds.
echo "Setting up Debug configuration: bin/debug"
../../configure CXXFLAGS="-g3 -O0 -DDEBUG=1"
echo "Setting up Release configuration: bin/release"
cd bin/release/
../../configure CXXFLAGS="-O2"
Setup Eclipse. Open the project's properties (Project->Properties->C/C++ Build->Builder Settings) and set the Build Location->Build Directory to
${workspace_loc:/helloworld/bin/debug}
Replacing 'helloworld' with your project's directory relative to the workspace (or you can supply an absolute path ${/abs/path/debug}). Do the same thing with the Release config, replacing "/debug" with "release" at the end of the path.
This method seems like a waste of disk space, but a valid alternative to achieve completely separate builds.

Resources