Overriding build rules in make - build-process

I'm using a Makefile to build an embedded project. I've inherited the project from numerous previous developers who haven't been using Make to its full potential, and I'd like to be able to specify the project version in the makefile using defines on the build command. However, there's already a build rule that builds all the object (.o) files. Is there any way to override that build rule for a specific object file so that I can add -D flags to the compiler ?
Another reason I'd like to be able to specify the project version in the makefile is so that I can have it generate artifacts with the build version in the names of the resulting files produced by the build process.

Yes, you can override a pattern rule (which is what I bet your .o rule is), just by having a specific rule (and the order of the rules doesn't matter):
%.o:
do_generic_things
x.o:
do_specific_things -Dproject_version
Yes, you can put a build version in a file name. There's more than one way to do it-- the best is probably to put it in the target name:
%$(B_VERSION).o: %.c
$(CC) -c -DBUILD_VERSION=$(B_VERSION) -Whatever $&lt -o $#

If you are using GNU make and you only want to change compiler options, you can use target-specific variables, like so:
x.o: CFLAGS += -DEXTRA_SYMBOL_FOR_X
This also works recursively, i.e. the target-specific value for x.o also is in effect for all targets which x.o depends on, meaning that if you build multiple executables in your makefile, you can set a target-specific variable on the executable itself, which will be in effect for all the object files:
foo: CFLAGS += -DEXTRA_SYMBOL_FOR_FOO_APP

Related

Disable parallel execution in make

I have a building process that creates a header file. In the second stage, several source files are generated from that header file. Then these source files are built into a binary. If anyone is interested these sources are generated with gSOAP utilities (wsdl2h, soapcpp2).
I have made Makefile.am, etc to build these sources, but there are problems when I want to use parallel execution.
Makefile.am would look something like this in a very simplified form
## generate header file
service.h : service.wsdl
wsdl2h -o $# service.wsdl
## list of generated source files
generated_files = source1.cpp source2.cpp source3.cpp
## generate source files
$(generated_files) : service.h
soapcpp2 $^
## build binary
binary: $(generated_files)
gcc -o $# $^
The rules say that service.h will be generated if service.wsdl changes. If service.h changes, soapcpp2 will generate source?.cpp files with one command execution.
Everything works fine until I try to build in parallel (for instance make -j4). The problematic line is the last one which generates many soruce files. If running in parrallel all these files are generated many times, while other make processes already try to compile them.
I followed instructions to disable parallel https://www.gnu.org/software/make/manual/html_node/Parallel-Disable.html, but with no success. If I try
.NOTPARALLEL: $(generated_files)
or
.NOTPARALLEL: service.h
The parallel execution just does not work any more. I also tried with .WAIT, and got no rule to make target .WAIT.
First, the .WAIT special target was introduced in GNU make 4.4. Since you are getting a no rule to make target error for it, it's clear you're using an older version which doesn't support it. It's usually a good idea to include the version of whatever tool you're using when asking for help.
The best thing to do is not disable parallelism but instead tell make that a single invocation of the recipe will generate all the files. If you have GNU make 4.3, then you can use a "grouped target" rule, like this:
## generate source files
$(generated_files) &: service.h
soapcpp2 $^
the &: here tells make that instead of building each target with a different invocation of the recipe, which is the default, a single invocation of the recipe builds all the targets.
If you don't have GNU make 4.3 then you'll need to play a trick to get the same behavior, something like this:
## generate source files
.sentinel : service.h
soapcpp2 $^
#touch $#
$(generated_files) : .sentinel ;
## build binary
binary: $(generated_files)
gcc -o $# $^
This has all the generated files depend on a single file .sentinel (you can name it whatever you want), which is the one make knows is generated by the recipe that also creates all the other source files. This isn't perfect but it will work for simple situations.

Qt: How to create a directory in project file?

I just want to crossplatform make a single directory from *.pro file. I use some commands like $(COPY_DIR) and $(COPY_FILE) well. And I saw in internets a many examples with command $(MKDIR) but it did not work for me.
It prints:
C:/Projects/installer/installer.pro(24): Extra characters after test expression.
when I used $$(MKDIR) on line 24.
Or:
C:/Projects/installer/installer.pro(24): '$' is not a recognized test function.
when I $(MKDIR).
What the proper way to create a directory in qmake projects?
Short answer
Use QMAKE_MKDIR like so:
mytarget.commands += $${QMAKE_MKDIR} $$shell_path($${OUT_PWD}/foo)
Long answer
qmake provides variables holding useful commands. Take a look at Undocumented QMake article on Qt Wiki. The one you are looking for is QMAKE_MKDIR, but you might also be interested in QMAKE_CHK_DIR_EXISTS.
If the values given by qmake do not suite you, you can use the environment to retrieve the mkdir command:
$(MKDIR) $$shell_path($${OUT_PWD}/foo)
$$(MKDIR) $$shell_path($${OUT_PWD}/foo)
The $(...) syntax retrieves the environment variable when make (or nmake...) is run while $$(...) retrieves it when qmake is run.
Also the mkdir command should be call in the context of a "make target" declared with QMAKE_EXTRA_TARGETS. See Adding custom targets in qmake documentation.

Access variables from custom process step command

In Qt Creator, I would like to configure a custom process step to build the project, which needs to access project variables, or at least the path to the .pro file.
When I run the built-in qmake, the .pro file is passed as first parameter, but when using custom step, it does not pass it.
E.g: Built-in
C:\...\bin\qmake.exe
C:\...\mywidget.pro
-r
-spec
win32-msvc2013
CONFIG+=debug
E.g: Custom
C:\...\bin\qmake.exe
-r
-spec
win32-msvc2013
CONFIG+=debug
When I set up the command, how to access these information?
I already tried with several options without success:
command: C:\...qmake.exe
Arguments: $$TARGET $TARGET $(TARGET) ${TARGET} %{TARGET}% %%{TARGET}%%
You can use %{sourceDir}\mywidget.pro as an argument. Looks like only %{buildDir} and %{sourceDir} are available which are project specific.
Of course, you cannot use values that are defined in the .pro file such as TARGET because those are only evaluated while qmake is running.

source_dirs doesn't work in .gpr scipt

I've inherited an Ada/C++ project and I'm trying to use gprbuild to automate the build process (which was previously done with a set of about 12 .bat files). I'm totally new to Ada and gprbuild, but have actually made pretty good progress. I can compile the .exe's that I need, but not the library. I am not at liberty to completely share the .gpr file, but the relevant parts look like this:
[snip]
for Source_Dirs use (
"c_plus_plus_files",
"ada_files",
"..\another_project\some_other_ada_files",
"..\another_project\even_more_ada_files"
);
[snip]
for Source_Files use (
"my_ada_file.ads",
"another_ada_file.ads",
"one_more_ada_file.adb",
"c_plus_plus_file.cpp"
);
[snip]
When I run "gprbuild -P my_project.gpr" it in turn runs "gcc -c gnat5 one_more_ada_file.adb" and complains that it cannot find a certain file that one_more_ada_file.adb depends on. The dependency is in ..\another_project\even_more_ada_files, so I would expect it to be found. But if I copy the dependency into the same folder as one_more_ada_file.adb, the error goes away.
Because of how the VCS is setup and how we're sharing code between two projects, I'd much rather figure out what's wrong with how I'm using "for source_dirs use" than to keep multiple copies of all the ada files.
Again, I'm an Ada/GPS newb, so if I'm leaving out relevant information, please let me know.
Update: It appears that the specific problem isn't that source_dirs isn't doing anything at all, but that it doesn't handle having two source dirs where .ads files in one dir depend on .ads files in the other. That is, even within my "other" project above, an .ads file in some_other_ada_files that depends on an .ads file in even_more_ada_files doesn't get compiled with the gcc -c -gnat05 command when I run gprbuild (error: the file in even_more_ada_files not found), but it does get compiled if I run the gcc command by hand (or in a .bat script) with two -I flags, one for each directory.
When dealing with multiple projects, you should normally create a .gpr-file for each project, and let your projects depend on the other projects as needed.
Thus:
project another_project is
for Source_Dirs use
("some_other_ada_files",
"even_more_ada_files");
end another_project;
and then:
with "..\another_project\another_project.gpr"
project The_Project is
for Source_Dirs use
("c_plus_plus_files",
"ada_files");
end The_Project;

Can 'make' check if mtime of a dependency is *different* between runs, not just if it's newer than target?

If foo_user.cpp depends on foo.h, then foo_user.cpp is built, and then foo.h's modification time is set to further in the past, make will not rebuild foo_user.cpp (because foo.cpp is 'newer'). I'd prefer it if make recorded the modification times of dependencies, and if they changed at all (newer or older), to consider targets of that dependency to be out of date. Can GNU make do this? If not, is there an easy alternative?
In case you're curious how this situation arises: foo.h resides in a symlinked folder. The symlink may point to the foolib-1.0 folder, the foolib-2.0 folder, etc. When the symlink points at a different version of the library, even an older version, foo_user.cpp should be rebuilt. If I simply specifiy symlinkfolder/foo.h as a dependency of foo_user.cpp, make only pays attention to the timestamp of foo.h, not the timestamp of the symlink'd directory through which foo.h is accessed. I can't add the symlink itself as a dependency, because the make rule is generated by the compiler (GCC has a special flag that when given causes it to output a make rule for all the headers a source file depends on).
I'm trying to understand why you can't just add the symlink as a dependency. I imagine your automatic dependencies are on one line, but you can have as many as you want.
x.o: a.h b.h
x.o: c.h
x.o: d.h
But having said that, it seems likely that make will stat the symlink's target, and not the symlink itself, so that may not DTRT. I suppose you could just touch a file somewhere whenever you make the symlink, but I also suppose you've already thought of that...
You could have a rule that runs ls -id link/. > test, which will put the inode number of the link target directory in test. You could then cmp test save, where save is from the last run. You could then have that make rule do make clean && make target if they are different.
targetwrapper:
ls -id link/. > test
cmp test save || make clean
make realtarget
cp test save
clean:
echo cleaned
realtarget:
echo made
No, Make does not support this. You may wish to consider using another build system such as SCons, which does not rely solely on the timestamp but actually computes the MD5 hash of source files and bases its decisions on the hashes.
From "What makes SCons better?" on its web site:
Reliable detection of build changes using MD5 signatures; optional, configurable support for traditional timestamps.
While make doesn't support it out of the box, you can program it.
include more_deps
ifneq ($(MAKE_RESTARTS),)
more_deps:
if (foolink.old differs from what foolink points to) ; then \
readlink foolink > foolink.old ; \
echo "foo_user: foolink_trigger" > more_deps ; \
touch foolink_trigger ; \
else \
echo "" > more_deps ;\
fi
endif
foo_user: foo_user.cpp
g++ $^ -o $#
Here you include makefile more_deps which sometimes will include the dependency on the symlink's trigger. Trigger is a special intermediate flie, all the meaningful informaion in which is its timestamp. When the symlink changes, the timestamp of the trigger is updated to current time (see touch), thus making foo_user outdated and it is the rebuilt.
include and MAKE_RESTARTS are needed to restart make after calculating the dependency described above. If the makefile being included is a target itself, the target is considered to be rebuilt, is rebuilt and then make restarts and re-reads makefile. But when it reads makefile for the second time, it doesn't see more_deps as a target, because MAKE_RESTARTS variable expands to non-empty string.
In fact, the line with if can sound like this:
more_deps:
if (any condition you want with $(VARIABLES) possible!) ; then \
update a file that holds the previous state ;\
...
Through which process do you change the symlink? You could add a make clean type of action to the script that changes the symlink.
You could also set up a "header working folder" in with you let make copy your header files, where the copied header files are dependent on their original and the symlink. The dependencies generated by GCC only take the working headers into account and won't clash with your copy headers into the working folder part of your Makefile.

Resources