Jam SubDir Rule
Introduction
Now we’ve built an MFC application
and a DLL, we’d like to include them in
the same build process. This is what Jam’s SubDir
rule does.
In the top-level directory, i.e. jam-test
, we place a Jamfile
looking like this:
SubDir TOP ;
SubInclude TOP lib ;
SubInclude TOP apps ;
We also need to create an empty Jamrules
file, in order to supress a warning. This is not a problem: we’ll probably be
putting project-specific rules in there in a moment.
We also have to create corresponding Jamfile
files for the other directories. The one in the apps
directory looks
like this:
SubDir TOP apps ;
SubInclude TOP apps mfc_exe ;
And we have to add SubDir
invocations to the top of the original Jamfiles, so that they know where they are.
We run the build from the top-level directory:
S:\jam-test>jam -d2 -f /jam-test/Jambase
Compiler is Microsoft Visual C++
...found 116 target(s)...
...updating 8 target(s)...
C++ apps\mfc_exe\ChildFrm.obj
cl /nologo /c /MTd /W3 /Gm /GX /ZI /Od /D WIN32 /D _DEBUG /D _WINDOWS /D _MBCS /D _USRDLL /D WIN32_D
LL_EXPORTS /MDd /W3 /Gm /GX /ZI /Od /D WIN32 /D _DEBUG /D _WINDOWS /D _AFXDLL /D _MBCS /Foapps\mfc_exe\Chil
dFrm.obj /Iapps\mfc_exe /IP:\VStudio\VC98\include /Tpapps\mfc_exe\ChildFrm.cpp
Command line warning D4025 : overriding '/MTd' with '/MDd'
ChildFrm.cpp
... etc. ...
Rc apps\mfc_exe\mfc_exe.res
rc /d _DEBUG /d _AFXDLL /l 0x809 /Fo apps\mfc_exe\mfc_exe.res apps\mfc_exe\mfc_exe.rc
Link apps\mfc_exe\mfc_exe.exe
link /nologo /dll /incremental:yes /debug /machine:I386 /subsystem:windows /incremental:yes /debug /
machine:I386 /out:apps\mfc_exe\mfc_exe.exe apps\mfc_exe\ChildFrm.obj apps\mfc_exe\MainFrm.obj apps\mfc_exe\
mfc_exe.obj apps\mfc_exe\mfc_exeDoc.obj apps\mfc_exe\mfc_exeView.obj apps\mfc_exe\StdAfx.obj apps\mfc_exe\mfc
_exe.res
...updated 8 target(s)...
If we attempt to run the mfc_exe
executable, we find that it’s “not a valid Win32 executable”. This would
appear to be down to the /dll
switch being passed to the linker: we’ve managed to build a DLL and give it a .exe
extension.
This behaviour is down to the way that Jam runs. Unlike recursive make, jam loads all of the named and included
Jamfiles into the same “namespace”. Thus, C++FLAGS
and LINKFLAGS
are persistant from one Jamfile to the next.
At this point, we do something we should have done before: Since we’re invoking our SharedLibrary
rule to build the
DLL, we should add the /dll
switch to the linker command line at that point.
We also don’t like the “overriding ‘/MTd’ with ‘/MDd’” warning. At this point, we introduce the
SUBDIRC++FLAGS
rule. It’s like C++FLAGS
, but the flags only stay in effect until the next SubDir rule. We should
change the DLL and executable Jamfiles to use this. For example, mfc_exe\Jamfile
looks like this:
SubDir TOP apps mfc_exe ;
SUBDIRC++FLAGS += /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" ;
LINKFLAGS += /subsystem:windows /incremental:yes /debug /machine:I386 ;
RCFLAGS += /d "_DEBUG" /d "_AFXDLL" /l 0x809 ;
Main mfc_exe : ChildFrm.cpp MainFrm.cpp mfc_exe.cpp mfc_exeDoc.cpp mfc_exeView.cpp StdAfx.cpp ;
Resource mfc_exe : mfc_exe.rc ;
We also note that the LINKFLAGS are accumulating. There’s (strangely) no SUBDIRLINKFLAGS corresponding to
SUBDIRC++FLAGS, but it’s not a major problem. The flags only differ slightly, and that’s according to the exe/dll
nature of the target. We can move them into Jambase
:
rule MainFromObjects
{
local _s _t ;
# Add grist to file names
# Add suffix to exe
_s = [ FGristFiles $(>) ] ;
_t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;
if $(_t) != $(<)
{
DEPENDS $(<) : $(_t) ;
NOTFILE $(<) ;
}
# make compiled sources a dependency of target
DEPENDS exe : $(_t) ;
DEPENDS $(_t) : $(_s) ;
MakeLocate $(_t) : $(LOCATE_TARGET) ;
Clean clean : $(_t) ;
<b>LINKFLAGS on $(_t) += /subsystem:windows /incremental:yes /debug /machine:I386 ;</b>
Link $(_t) : $(_s) ;
}
rule SharedLibraryFromObjects
{
local _s _t ;
# Add grist to file names
# Add suffix to dll
_s = [ FGristFiles $(>) ] ;
_t = [ FAppendSuffix $(<) : $(SUFSHR) ] ;
if $(_t) != $(<)
{
DEPENDS $(<) : $(_t) ;
NOTFILE $(<) ;
}
# make compiled sources a dependency of target
DEPENDS exe : $(_t) ;
DEPENDS $(_t) : $(_s) ;
MakeLocate $(_t) : $(LOCATE_TARGET) ;
Clean clean : $(_t) ;
<b>LINKFLAGS on $(_t) += /dll /incremental:yes /debug /machine:I386 ;</b>
Link $(_t) : $(_s) ;
}
Conclusions
That was easy enough. We’ve still got a couple of things to work out:
- We’re not actually attempting to link the EXE with the DLL, so there’s no dependency established.
- The resource compiler flags aren’t being reset. We’ve only got one resource script so far, so it’s not an issue yet.
You can find the source resulting from this here.