NMake inference rules with both c and cpp files - nmake

I have a project with both c and cpp files, and I've been using NMake to build. My problem is that if I have two inferences rules, one for each file type,
{$(dirSrc)}.c{$(dirObj)}.obj:
cl /nologo /c /EHsc /Fo$(dirObj)\ $<
{$(dirSrc)}.cpp{$(dirObj)}.obj:
cl /nologo /c /EHsc /Fo$(dirObj)\ $<
$(binPath): $(dirObj)\*.obj
link /nologo /dll /out:$(binPath) $(dirObj)\*.obj
only the c files get compiled, presumably because the .c extension is first in the .SUFFIXES list.
I could of course simply change the extensions on the c files to cpp, but I was wondering if anyone knows of a way to have both rules invoked.

Well, to answer my own question, the best I could think of was to compile to 2 separate directories, then point to both when running the linker.
{$(dirSrc)}.c{$(dirObj)\c}.obj:
cl /nologo /c /EHsc /Fo$(dirObj)\c\ $<
{$(dirSrc)}.cpp{$(dirObj)\cpp}.obj:
cl /nologo /c /EHsc /Fo$(dirObj)\cpp\ $<
$(binPath): $(dirObj)\c\*.obj $(dirObj)\cpp\*.obj
link /nologo /dll /out:$(binPath) $(dirObj)\c\*.obj $(dirObj)\cpp\*.obj

(As a reference for others in the same boat) the problem is the *.obj wildcard.
That, together with the fact that if there are multiple (inference) rules for a target, only one can be reasonably applied to create/update it.
Now, the build logic from a clean state is the following (simplified):
Need to build $(binPath), let's see what it depends on...
There's that *.obj, so let't look closer...
Doesn't match any existing files; check if we could build it somehow...
Cool, an inference rule says .obj can be built from .c (or .cpp, it really doesn't matter, which one is found first, see below! BTW, it's not the .SUFFIXES order, but the ordering of the rules in the makefile that matters here)...
So, execute the compiler in the rule to create *.obj from its matching source, which is the non-expandable, literal *.c:
→ cl /c /Fo($outdir) *.c
Great, *.obj is now ready, go ahead to linking...
OK, final target, and (even though the linking may now fail for undefined externals) that's all we could do, so let's call it a day.
Notice, how the alternative rule never even came into the picture!
Now, for updates, assuming the broken (incomplete) target isn't there, but the object files built above are, and there're even some updates, both to some .c and .cpp files:
Need to build the missing $(binPath), let's see what it depends on...
There's *.obj, again, which can now match existing files, so let's check them (one by one) if they need updating...
Then, there's the same inference rule again (whichever; there can be only one for an item, let's stick with .c) matching the .obj target in question, so check for changes in the corresponding .c sources...
They are found, so "run compiler", but this time NMAKE is smart enough to only supply the updated C sources via the $< macro:
→ cl /c /Fo($outdir) some.c other.c
(Note: it's even smart enough (apparently, with VS 2019 here) to go ahead and use batch-mode by default, even if it's not explicitly a batch rule.)
OK, *.obj is now up-to-date again, proceed to linking...
Final target, same as before, end of job, celebrate! (Let's assume now that the link has succeeded, just for the sake of the remaining examples below.)
Again: the other rule was never needed/used for anything.
Now, curiously, you can even start deleting .obj files one by one (as long as some remain), and leave NMAKE unfazed:
'test.dll' is up-to-date
What?! Why aren't the missing ones rebuilt?
You know what? Let's delete all of them... And, just for the fun of it, replace them with a single fake one, by copying some random file there (from anywhere), renaming it to fake.obj.
'test.dll' is up-to-date
Jesus! This makes no sense!
OK, let's end this. Create a brand new .c file then. That'll sure trigger a rebuild!
'test.dll' is up-to-date
No way! :-o What the hell's going on?! Maybe adding a new .cpp then...
'test.dll' is up-to-date
OMG!... Such a piece of junk! Incredible, this NMAKE thing... Right?
Well... First of all, for fake.obj, for which there's no source with a matching name, neither rule applies: NMAKE can't "invent" a source for that, so it'll be never rebuilt, it's just sitting there, as a time-bomb, until the next linking round, where the linker would eventually pick it up and find out about it, ending all the fun. :)
To all the other "anomaly":
Any existing .obj file will satisfy the dependency of *.obj (for the lib), so as long as there's at least one, NMAKE will be happy, and never even know that it's not the complete list!
That is why nothing is ever done for any new .c or .cpp added to the project, so using a wildcard this way is shooting oneself in the foot. (Which doesn't mean there aren't any perfectly legitimate cases for wildcards in build scripts, BTW.)
And (to recap), for rebuilding a missing object, NMAKE (just like gmake etc.) has to pick a winner, and ignore the rest, if there are multiple matching rules.
(FYI, gmake even has a page specifically about this wildcard pitfall. And, to mitigate the risks, unlike NMAKE, it seems to refuse building the initial "complete" set of objects for a *.o wildcard, knowing that it'll become incomplete the minute we start adding sources — i.e. by the very act the wildcard was hoped to support! ;) )

Related

Makefile target inside conditional

Sorry if this is a simple question, but most resources I've been able to find for Makefile tutorials cover only the extreme basics.
I'm attempting to interpret a Makefile that has a target inside a conditional ifneq. Additionally, said target is never declared as .PHONY but is called "all default". See below:
ifneq ($(SOME_VARIABLE)$(ANOTHER_VARIABLE,)
all default: check_a check_b
$(MAKE) ARCH=somearch CROSS_COMPILE=archcompiler -C $(KDIR) M=$(PWD) modules
else
obj-m += ...
endif
This is a simplified version, but hopefully gets the point across. My question is essentially:
Why isn't 'all default' declared as .PHONY? Is definitely isn't making a file called 'all default'.
Does a Makefile execute top-to-bottom like normal code? How could you even execute anything above a target?
This Makefile is building a linux kernel (or just kernel modules?), which I'm guessing is why I'm so confused. Can anyone recommend resources to help me begin to understand how all of this works? I don't even know where to start.
This isn't defining a rule to build a target called all default.
It's defining two rules to build two different targets, one called all and one called default. Multiple targets separated by spaces in an explicit rule define that same rule for each target.
Also note, that declaring a target .PHONY is not mandatory. Usually make will work the same way regardless of whether or not the target is explicitly marked .PHONY.
Marking a target .PHONY has these benefits: it's slightly more efficient (but hardly noticeable), and if you happen to create some file or directory with the same name as your target then it will continue to work.
In other words, as long as you don't create a file or directory named all or default, then your makefile will work properly even if these targets are not declared .PHONY.
As for your second question: no, makefiles are not processed procedurally, like programs or shell scripts.
The way it works is that make parses all the makefiles, included makefiles, etc. first and constructs an internal graph of all the targets and their prerequisites. Then after that's done, it chooses a starting node in the graph (either ones you asked for on the command line or else the first one in the makefile) and starts to walk the graph, building things that are out of date.

How to tell SCons to stop computing implicit dependencies in a particular directory?

My SCons project depends on a lot of third party libs, each providing dozens or hundreds of include files.
My understanding of how SCons works is that, on each build, it parses the source files of my project to find the #include directives, and it uses the value of env['CPPPATH'] to find these files and compute their md5 sum.
This scanning is costly, and thus I would like to optimize this process by teaching SCons that all the headers of my third party files will never change. This property is actually enforced by the tool that manages our third party libs.
I know there is a --implicit-deps-unchanged option that forces scons to assume that the implicit dependencies did not change, but it works globally. I did not find a way to restrict this option to a particular directory. I tried to find if the default Scanner of implicit C++ files can be configured, but found nothing. I think it is possible to avoid using CPPPATH, and instead only give the -I option to the compiler directly, but it is cumbersome.
Is there any way to optimize SCons by teaching him that files in a directory will never, ever change?
You can try pre-expanding the list of header file paths into CCFLAGS.
Note that doing so means they will not be scanned.
for i in list_of_third_party_header_directories:
env['CCFLAGS'].append('-I' + i)
In this case the contents of CPPPATH would be your source directories, and not the third-party ones which you assert don't change.
Note that changing the command line of your compile commands in any way (unless the arguments are enclosed in $( $)) will cause your source files to recompile.

When I change SConstruct file, scons doesn't trigger rebuild?

First, I've got SConstruct file like this:
Object('a.s')
Program('mya','a.o')
I run scons, it generates 'mya'. OK. Then I change my SConstruct to be:
Object('a.s',CCFLAGS='-DHello')
Program('mya','a.o')
Run scons again. Nothing is done:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.
This is quite weird to me. When I used make or msbuild systems, whenever there's argumenet change in project configuration file, there'll be a rebuild. This is a default rule.
But seems scons's rule is different, is this by design? Only when source file change will trigger rebuild? If this is the design, I think there's a flaw that when compilation/linker option changes, target file should be different and thus, should trigger rebuild, right?
Is my understanding incorrect, or there's some special points in scons that I still need to know about?
Thanks a lot.
Refering directly to your last paragraph, and based on your last three questions (Using 'LIBS' in scons 'Program' command failed to find static library, why? and When changing the comment of a .c file, scons still re-compile it? and this one) and the depth of them, yes there seem to be a lot of things that you don't know about SCons.
So please take the next steps in reading its MAN page and the UserGuide. You might also want to step your tone down a bit and instead of questioning its design or claiming that there seems to be a "flaw" doing your homework (see also How To Ask Questions The Smart Way).
When you are calling a "scons -c" followed by a "scons" you should see that the "-DHello" doesn't appear in the command-line, even though "a.o" gets rebuilt. The variable $CCFLAGS isn't used to compile assembler files, but $ASFLAGS is...and when setting it instead, you should indeed see a rebuild immediately, without editing the source file.

Is there a makefile option to delete the obj files only but leaving the targets intact?

I am short of disk space while trying to compare different releases of Android (each of them takes about 6-7G after building).
I thought there is an option for make (similar to make clean) that it will delete all the intermediate .obj files and leaves the target(s) alone. But I couldn't remember what it is; or is there an option like that?!
A makefile will do whatever it's written to do. The clean target is a convention for makefiles, not a feature of Make. A makefile need not have a clean rule; if there is a clean rule, and if it is written well it will clean out the files you wanted cleaned out, if it is written badly there's no limit to how badly it can mess things up.
If you are writing (or editing) a makefile, you can put in a rule for removing object files. If you are using a makefile written by someone else, either it has such a rule or it doesn't.
Maybe the .INTERMEDIATE directive will help. From the GNU Make documentation (10.4 Chains of Implicit Rules):
The second difference is that if make' _does_ create B in order to
update something else, it deletes B later on after it is no longer
needed. Therefore, an intermediate file which did not exist before
make' also does not exist after make'.make' reports the deletion
to you by printing a `rm -f' command showing which file it is
deleting.
Ordinarily, a file cannot be intermediate if it is mentioned in the
makefile as a target or prerequisite. However, you can explicitly
mark a file as intermediate by listing it as a prerequisite of the
special target `.INTERMEDIATE'. This takes effect even if the file is
mentioned explicitly in some other way.

How to convert a makefile into readable code?

I downloaded a set of source code for a program in a book and I got a makefile.
I am quite new to Linux, and I want to know whether there is any way I can see the actual source code written in C?
Or what exactly am I to do with it?
It sounds like you may not have downloaded the complete source code from the book web site. As mentioned previously, a Makefile is only the instructions for building the source code, and the source code is normally found in additional files with names ending in .c and .h. Perhaps you could look around the book web site for more files to download?
Or, since presumably the book web site is public, let us know which one it is and somebody will be happy to point you in the right direction.
A Makefile does not contain any source itself. It is simply a list of instructions in a special format which specifies what commands should be run, and in what order, to build your program. If you want to see where the source is, your Makefile will likely contain many "filename.c"'s and "filename.h"'s. You can use grep to find all the instances of ".c" and ".h" in the file, which should correspond to the C source and header files in the project. The following command should do the trick:
grep -e '\.[ch]' Makefile
To use the Makefile to build your project, simply typing make should do something reasonable. If that doesn't do what you want, look for unindented lines ending in a colon; these are target names, and represent different arguments you can specify after "make" to build a particular part of your project, or build it in a certain way. For instance, make install, make all, and make debug are common targets.
You probably have GNU Make on your system; much more information on Makefiles can be found here.
It looks like you also need to download the SB-AllSource.zip file. Then use make (with the Makefile that you've already downloaded) to build.

Resources