<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META NAME="VPSiteProject" CONTENT="file:///D|/rld/Docs/Project.vpp"><META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
<META NAME="GENERATOR" Content="Visual Page 2.0 for Windows">
<TITLE>Building an MFC Application with Jam: Introduction</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="roger.css">
<STYLE TYPE="text/css">
<!--
P {
margin : 0 0 .5em 0
}
P.links {
margin : 1em 0 3em 0 ;
Text-Align : Left ;
Font-Size : 0.900000em
}
H1.node {
Font-Size : 1.300000em
}
PRE {
padding : 1em ;
Font-Size : 0.900000em ;
Background-Color : #EEEEEE
}
EM.node {
margin : 0 0 1em 0 ;
display : block ;
Font-Size : 0.800000em ;
Color : gray ;
Font-Style : Normal
}
.node {
}
-->
</STYLE>
</HEAD>
<BODY>
<H1>Constructing a Medium-sized Project with Jam</H1>
<H2>Introduction</H2>
<P>Jam is a replacement for <CODE>make(1)</CODE>. See <A HREF="Jam.html">here</A> for more details.</P>
<P>I'm attempting to use jam to build our Windows code, but in order to keep the scale of this discussion down,
I'm just going to explain the aspects of our codebase that caused difficulty, and then I'm going to fake them up
in a mock build tree. This will enable me to explain things in isolation.</P>
<H2>Introduction</H2>
<UL>
<LI><A HREF="#BuildJam">Building jam on Windows NT</A>.
</UL>
<P>
<H2>Tutorial</H2>
<P>I've written this article in the form of a tutorial, because I think better when trying to formulate reproducible
instructions for this kind of thing.</P>
<UL>
<LI><A HREF="#mfc">Building an MFC program</A>.
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/">Building a shared library (DLL)</A>.
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/sub_dir/">Bringing it together with the SubDir
rule</A>.
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/link_dll/">Linking with a shared library</A>.
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/static_lib/">Building (and linking with) static
libraries</A>.
<LI><A HREF="http://www.differentpla.net/node/view/96">Resource script dependencies</A>.
<LI><A HREF="http://www.differentpla.net/node/view/95">Separate Debug/Release directories</A>.
</UL>
<P>
<H2>Other Stuff</H2>
<UL>
<LI><A HREF="http://www.differentpla.net/node/view/37">Conflicting 'lib' target</A>.
<LI><A HREF="http://www.differentpla.net/node/view/36">Linker command line length</A>.
</UL>
<P>
<H2>Miscellaneous</H2>
<P>This is some stuff I wrote earlier. I'm going to try to factor it into the main discussion, but for now, you
can find it here:</P>
<UL>
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/misc/system_libs.html">Linking with system libraries</A>.
</UL>
<P>
<H1><A NAME="%23mfc"></A>Building an MFC Application with Jam: Introduction</H1>
<H2>Introduction</H2>
<P>Since the majority of our Windows applications are written using MFC, it's a useful experiment to get jam to
build a freshly-generated MFC application. Once we've got this working, we can turn our attention to the things
that make our build process different.</P>
<P>I've attempted to break down the process of getting an MFC application to build into discrete chunks. They're
not particularly self-contained at the moment, but they attempt to describe one problem (and hopefully its solution)
each:<BR>
You can find the resulting source code from this example <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/src/jam-test-20010711a.tar.gz">here</A>.</P>
<P>
<HR ALIGN="CENTER">
<H2>Using AppWizard to generate the application</H2>
<P>Run up Visual C++, and generate a new "MFC AppWizard (exe)" project. I'm going to be replicating parts
of our build system around it, so I called it <CODE>mfc_exe</CODE> and put it in <CODE>s:\jam-test\apps\mfc_exe</CODE>.
The default settings for the application are fine, so just keep clicking "Next".</P>
<P>When you've got your application generated, get Visual C++ to build it, just for sanity's sake.</P>
<H2>Creating the Jamfile</H2>
<P>The obvious thing to do at this point is to put the names of the .cpp files into a Jamfile, like this:</P>
<DIV CLASS="snippet">
<PRE>Main mfc_exe : ChildFrm.cpp MainFrm.cpp mfc_exe.cpp mfc_exeDoc.cpp mfc_exeView.cpp StdAfx.cpp ;
</PRE>
</DIV>
<P>...and then to try building it, using -d2 to see what's going on. Not surprisingly, it doesn't work:
<DIV CLASS="results">
<PRE>Link mfc_exe.exe
nafxcw.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already def
ined in libc.lib(new.obj)
nafxcw.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) already defined
in libc.lib(delete.obj)
libc.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
nafxcw.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
nafxcw.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
mfc_exe.exe : fatal error LNK1120: 3 unresolved externals
link /nologo /out:mfc_exe.exe ChildFrm.obj MainFrm.obj mfc_exe.obj mfc_exeDoc.obj mfc_exeView.obj StdAfx.
obj P:\VStudio\VC98\lib\advapi32.lib P:\VStudio\VC98\lib\libc.lib P:\VStudio\VC98\lib\oldnames.lib P:\VStudi
o\VC98\lib\kernel32.lib
...failed Link mfc_exe.exe ...
</PRE>
</DIV>
<P>
<H1>Building an MFC Application with Jam: Compiler Flags</H1>
<P>
<HR ALIGN="CENTER">
</P>
<P>Essentially, jam's invocation of Visual C++ isn't using the multithreaded libraries (the __beginthreadex unresolved
external), and there's something else wrong with the implicit link instructions.</P>
<P>Let's take a look at the compiler settings and see what's different. Jam is invoking the compiler like this:</P>
<DIV CLASS="snippet">
<PRE>cl /nologo /c /FoChildFrm.obj /IP:\VStudio\VC98\include /TpChildFrm.cpp
</PRE>
</DIV>
<P>Developer Studio is invoking the compiler like this (taken from the .plg file):
<DIV CLASS="snippet">
<PRE>cl /nologo /MDd /W3 /Gm /GX /ZI /Od
/D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS"
/Fp"Debug/mfc_exe.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/"
/FD /GZ /c ChildFrm.cpp
</PRE>
</DIV>
<P>Looking at the results of <CODE>cl /?</CODE> tells us the following:
<TABLE BORDER="0">
<tbody> <TR>
<TH>
<P>Switch
</TH>
<TH>
<P>Description
</TH>
<TH WIDTH="25">
<P>
</TH>
<TH>
<P>Switch
</TH>
<TH>
<P>Description
</TH>
</TR>
<TR>
<TD>
<P>/nologo
</TD>
<TD>
<P>Don't output a version banner
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/MDd
</TD>
<TD>
<P>Link with the MSVCRTD.lib file.
</TD>
</TR>
<TR>
<TD>
<P>/W3
</TD>
<TD>
<P>Set the warning level
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/Gm
</TD>
<TD>
<P>Enable minimal rebuild
</TD>
</TR>
<TR>
<TD>
<P>/GX
</TD>
<TD>
<P>Enable exceptions
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/ZI
</TD>
<TD>
<P>Enable Edit and Continue debug info
</TD>
</TR>
<TR>
<TD>
<P>/Od
</TD>
<TD>
<P>Disable optimisations (debug)
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/D
</TD>
<TD>
<P>Define some stuff
</TD>
</TR>
<TR>
<TD>
<P>/Fp
</TD>
<TD>
<P>Name precompiled header file
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/Yu
</TD>
<TD>
<P>Use .PCH file
</TD>
</TR>
<TR>
<TD>
<P>/Fo
</TD>
<TD>
<P>Name object file
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/Fd
</TD>
<TD>
<P>Name .PDB file
</TD>
</TR>
<TR>
<TD>
<P>/FD
</TD>
<TD>
<P>Generate file dependencies
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/GZ
</TD>
<TD>
<P>Enable runtime debug checks
</TD>
</TR>
<TR>
<TD>
<P>/c
</TD>
<TD>
<P>Don't link; just compile
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>/I
</TD>
<TD>
<P>Name include directory
</TD>
</TR>
<TR>
<TD>
<P>/Tp
</TD>
<TD>
<P>Treat the file as C++
</TD>
<TD WIDTH="25">
<P>
</TD>
<TD>
<P>
</TD>
<TD>
<P>
</TD>
</TR>
</tbody></TABLE>
</P>
<P>Obviously, we'd like the warnings and debug information. We'll probably need the <CODE>/D</CODE> switches, as
well. Diagnosing the error messages above suggests that we'll need <CODE>/MDd</CODE>. For now we can ignore the
precompiled header stuff, and we'll come back to the file naming things.</P>
<P>That leaves us with a file looking like this:</P>
<DIV CLASS="snippet">
<PRE>C++FLAGS += /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" ;
Main mfc_exe : ChildFrm.cpp MainFrm.cpp mfc_exe.cpp mfc_exeDoc.cpp mfc_exeView.cpp StdAfx.cpp ;
</PRE>
</DIV>
<P>...and the following results:
<DIV CLASS="snippet">
<PRE>LINK : warning LNK4098: defaultlib "msvcrtd.lib" conflicts with use of other libs; use /NODEFAULTLIB:library
libc.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
mfc_exe.exe : fatal error LNK1120: 1 unresolved externals
</PRE>
</DIV>
<P>...which looks like it's caused by <CODE>Jambase</CODE> adding libraries we don't want.</P>
<H1>Building an MFC Application with Jam: Link Libraries</H1>
<P>
<HR ALIGN="CENTER">
</P>
<P>We'll copy <CODE>Jambase</CODE> from the distribution directory and put it in into <CODE>S:\jam-test</CODE>,
which is where it'll end up in our final build system. Looking through the file reveals the following:</P>
<DIV CLASS="snippet">
<PRE> else if $(MSVCNT) {
ECHO "Compiler is Microsoft Visual C++" ;
AR ?= lib ;
AS ?= masm386 ;
CC ?= cl /nologo ;
CCFLAGS ?= "" ;
C++ ?= $(CC) ;
C++FLAGS ?= $(CCFLAGS) ;
LINK ?= link /nologo ;
LINKFLAGS ?= "" ;
LINKLIBS ?= $(MSVCNT)\\lib\\advapi32.lib
$(MSVCNT)\\lib\\libc.lib
$(MSVCNT)\\lib\\oldnames.lib
$(MSVCNT)\\lib\\kernel32.lib ;
OPTIM ?= "" ;
STDHDRS ?= $(MSVCNT)\\include ;
UNDEFFLAG ?= "/u _" ;
}
</PRE>
</DIV>
<P>We'll take out the LINKLIBS line, leaving it looking like this:
<DIV CLASS="snippet">
<PRE> LINKLIBS ?= "" ;
</PRE>
</DIV>
<P>Remembering to invoke jam as: <CODE>jam -f /jam-test/Jambase</CODE> leaves us with this:</P>
<DIV CLASS="snippet">
<PRE>LINK : fatal error LNK1561: entry point must be defined</PRE>
</DIV>
<H1>Building an MFC Application with Jam: Entry Point</H1>
<P>
<HR ALIGN="CENTER">
</P>
<P>Jam is invoking link like this:</P>
<DIV CLASS="snippet">
<PRE>link /nologo /out:mfc_exe.exe ChildFrm.obj MainFrm.obj mfc_exe.obj mfc_exeDoc.obj mfc_exeView.obj
StdAfx.obj
</PRE>
</DIV>
<P>Developer Studio is invoking link with a response file containing the following:
<DIV CLASS="snippet">
<PRE>/nologo /subsystem:windows /incremental:yes /pdb:"Debug/mfc_exe.pdb"
/debug /machine:I386 /out:"Debug/mfc_exe.exe" /pdbtype:sept
.\Debug\mfc_exe.obj
.\Debug\StdAfx.obj
.\Debug\MainFrm.obj
.\Debug\ChildFrm.obj
.\Debug\mfc_exeDoc.obj
.\Debug\mfc_exeView.obj
.\Debug\mfc_exe.res
</PRE>
</DIV>
<P>We'll add some of the more interesting switches to our Jamfile, and see what happens:</P>
<DIV CLASS="snippet">
<PRE>C++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 ;
Main mfc_exe : ChildFrm.cpp MainFrm.cpp mfc_exe.cpp mfc_exeDoc.cpp mfc_exeView.cpp StdAfx.cpp ;
</PRE>
</DIV>
<P>It builds! Does it run? It does. Unfortunately, it bails out immediately. It should have brought up a window
of some kind. Perhaps if we run it in the debugger?</P>
<H1>Building an MFC Application with Jam: Resource Files</H1>
<P>
<HR ALIGN="CENTER">
</P>
<P>Running our newly-built MFC application in the debugger reveals the following smoking gun in the output window:</P>
<DIV CLASS="snippet">
<PRE>Warning: no document names in string for template #129.
Warning: no document names in string for template #129.
Warning: no shared menu for document template #129.
Warning: no document names in string for template #129.
Warning: no shared menu for document template #129.
Warning: failed to load menu for CFrameWnd.
</PRE>
</DIV>
<P>Looks to me like it's not linking in the resource files. We'd better sort that out now. What we'd like to do
is simply add the <CODE>.rc</CODE> file to the list of source files in the Jamfile, and have it magically work.
However, when we try that, we get:</P>
<DIV CLASS="snippet">
<PRE>Unknown suffix on mfc_exe.rc - see UserObject rule in Jamfile(5)
</PRE>
</DIV>
<P>Looking in the <CODE>Jambase.html</CODE> file included in the distribution, we find a section that suggests
overriding the <CODE>UserObject</CODE> rule in order to tell jam about .rc files. It says to put it in <CODE>Jamrules</CODE>,
but since we don't have one, we'll put it in our <CODE>Jamfile</CODE> for the time being:</P>
<DIV CLASS="snippet">
<PRE>RC ?= rc ;
C++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" ;
rule UserObject {
switch $(>) {
case *.rc : ResourceCompiler $(<) : $(>) ;
case * : EXIT "Unknown suffix on" $(>) "- see UserObject rule in Jamfile(5)." ;
}
}
rule ResourceCompiler {
DEPENDS $(<) : $(>) ;
Clean clean : $(<) ;
}
actions ResourceCompiler {
$(RC) /l 0x809 /fo $(<) $(RCFLAGS) $(>)
}
Main mfc_exe : ChildFrm.cpp MainFrm.cpp mfc_exe.cpp mfc_exeDoc.cpp mfc_exeView.cpp StdAfx.cpp mfc_exe.rc ;
</PRE>
</DIV>
<P>Unfortunately, this approach is flawed: The generated file is called .obj, rather than .res. This causes a major
problem in AppWizard-generated MFC applications, because the .rc file has the same base name as the main application
source file, and they're both configured to generate a file with the .obj suffix.</P>
<P>Thus, one of the build steps will overwrite the output from the other. This is a bad thing.</P>
<P>It looks like we'll have to give up on our ideal of simply adding the filename to the list of .cpp files --
at least until we can figure out the magic in the <CODE>Main</CODE> rule in <CODE>Jambase</CODE>. Since the odds
of that happening are slim, we'll cast our net a little further afield.</P>
<H1>Building an MFC Application with Jam: Resource Files (2)</H1>
<P>
<HR ALIGN="CENTER">
</P>
<P>One of the mailing list participants, Chris Antos, forwarded me a copy of his Jambase file a little while ago.
It contains all sorts of useful rules, but I'm not entirely sure what some of it does yet.</P>
<P>Lifting the relevant sections, and simplifying them results in the following:</P>
<DIV CLASS="snippet">
<PRE># Resource prog : resources.rc ;
rule Resource {
# _s is the source (.rc) file.
# _r is the target (.res) file.
# _e is the executable (.exe) file.
local _s = [ FGristFiles $(>) ] ;
local _r = $(_s:S=.res) ; # Chris Antos has some stuff here to set grist ...:G=)
local _e = [ FAppendSuffix $(<) : $(SUFEXE) ] ;
# Make the executable depend on the .res file, and
# make the .res file depend on the .rc file.
DEPENDS $(_e) : $(_r) ;
DEPENDS $(_r) : $(_s) ;
LOCATE on $(_r) = $(LOCATE_TARGET) ;
SEARCH on $(_s) = $(SEARCH_SOURCE) ;
NEEDLIBS on $(_e) += $(_r) ;
# TODO: Header file scanning.
Rc $(_r) : $(_s) ;
Clean clean : $(<) ;
}
actions Rc {
$(RC) $(RCFLAGS) /I$(HDRS) /I$(RCHDRS) /Fo $(<) $(>)
}
</PRE>
</DIV>
<P>
<P>This works fine. If we build the program, we get a working executable!</P>
<H1>Building an MFC Application with Jam: What Next?</H1>
<P>
<HR ALIGN="CENTER">
</P>
<P>We've just successfully built an MFC application using jam. There are a couple of things that we still need
to consider:</P>
<UL>
<LI>Resource Scripts can have include files. We've not done anything about dependency checking on them.
<LI>Separate Debug/Release configurations.
<LI>Visual C++ puts the output in Debug or Release. Currently, we just dump everything in the current directory.
<LI>The AppWizard-generated project used precompiled headers. We ought to, as well. They dramatically improve compilation
speed.
</UL>
<P>I'll come back to this later and deal with some of the above points.</P>
<H1 CLASS="node"><A NAME="%23BuildJam"></A>Building Jam on Windows NT</H1>
<P><EM><SPAN CLASS="node">2004/01/09 - 10:28am | <A HREF="http://www.differentpla.net/taxonomy/page/or/16">Jam</A></SPAN></EM></P>
<P>First, you'll need to download jam version 2.3.2 from ftp.perforce.com in <A HREF="ftp://ftp.perforce.com/pub/jam/jam-2.3.2.zip">zip
format</A> or <A HREF="ftp://ftp.perforce.com/pub/jam/jam-2.3.2.tar.gz">as a tar.gz</A></P>
<P>Unpack the contents into a new directory. Edit the <CODE>Makefile</CODE>, and uncomment the relevant lines under
the comment:</P>
<PRE># NT (with Microsoft compiler)</PRE>
<P>Tell it where your VC++ libraries live. This might not work if you've installed VC++ in the default location
(spaces in the path names, see). I haven't, so I just:</P>
<PRE><FONT FACE="Courier New, Courier">set MSVCNT=P:\VStudio\VC98</FONT></PRE>
<P>Build it:</P>
<PRE><FONT FACE="Courier New, Courier">nmake -f Makefile</FONT></PRE>
<P>It's a bootstrap build process. It uses make (or nmake) to build jam, and then runs jam to build itself again.
You should have a <CODE>jam.exe</CODE> file in the <CODE>bin.ntx86</CODE> directory. Copy it somewhere sensible.</P>
<P><!-- Linker Command Line Length -->
<H1 CLASS="node">Linker Command Line Length</H1>
<P><EM CLASS="node">2004/01/09 - 10:37am | <A HREF="http://www.differentpla.net/taxonomy/page/or/16">Jam</A></EM></P>
<P>Jam imposes a hard limit of 996 characters on command lines when built on NT. This limit is higher for other
operating systems, and can actually be raised to around 10Kb on Windows 2000. However, it's still not high enough
for some link actions.</P>
<P>We'd like, therefore, to place the linker actions in a response file, and invoke the linker with that instead.
Replace the "actions Link..." clause in the NT-specific section of <CODE>Jambase</CODE> with this:</P>
<DIV CLASS="snippet">
<PRE> rule Link {
MODE on $(<) = $(EXEMODE) ;
LINKFLAGS on $(<) += $(LINKFLAGS) $(SUBDIRLINKFLAGS) ;
Chmod $(<) ;
local _i ;
StartLink $(<) : $(>) ;
for _i in $(>) {
LinkItems $(<) : $(_i) ;
}
FinishLink $(<) : $(>) ;
}
rule StartLink {
Clean clean : $(<:S=.rsp) ;
}
actions quietly Link {}
# We have to touch the file first,
# or the delete will fail, stopping the build.
actions quietly StartLink {
$(TOUCH) $(<:S=.rsp)
$(RM) $(<:S=.rsp)
}
actions together piecemeal quietly LinkItems {
ECHO $(>) >> $(<:S=.rsp)
}
actions FinishLink bind NEEDLIBS {
$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) @$(<:S=.rsp) $(NEEDLIBS) $(LINKLIBS)
}
</PRE>
</DIV>
<P>Remember to set <CODE>TOUCH</CODE> to something sensible.</P>
<P><!-- Conflicting 'lib' target -->
<H1 CLASS="node">Conflicting 'lib' target</H1>
<P><EM CLASS="node">2004/01/09 - 10:39am | <A HREF="http://www.differentpla.net/taxonomy/page/or/16">Jam</A></EM></P>
<P>empeg's source tree has a directory called <CODE>lib</CODE>, in which the core libraries used by all of our
products live. Unfortunately, this conflicts with one of the included pseudo-targets that jam uses.</P>
<P>The fix is relatively simple. You need to edit the included <CODE>Jambase</CODE> file, and rename every mention
of <CODE>lib</CODE> to something else, e.g. <CODE>libs</CODE>.</P>
<P>Lines 552-554:</P>
<DIV CLASS="before">
<PRE>DEPENDS all : shell files lib exe obj ;
DEPENDS all shell files lib exe obj : first ;
NOTFILE all first shell files lib exe obj dirs clean uninstall ;
</PRE>
</DIV>
<P>...change this to...
<DIV CLASS="after">
<PRE>DEPENDS all : shell files <B>libs</B> exe obj ;
DEPENDS all shell files <B>libs</B> exe obj : first ;
NOTFILE all first shell files <B>libs</B> exe obj dirs clean uninstall ;
</PRE>
</DIV>
<P>Lines 827-830:
<DIV CLASS="before">
<PRE> else {
DEPENDS lib : $(_l) ;
}
</PRE>
</DIV>
<P>...change this to...
<DIV CLASS="after">
<PRE> else {
DEPENDS <B>libs</B> : $(_l) ;
}
</PRE>
</DIV>
<P>You should probably also change the comments at lines 41-52 that refer to 'lib'.</P>
<P>This then requires that you use your new <CODE>Jambase</CODE> instead of the included one. You have two choices:</P>
<OL>
<LI>Use the <CODE>-f</CODE> switch to <CODE>jam</CODE> to tell jam where to find an alternate Jambase file. This
is the simplest, but requires more typing.
<LI>Recompile jam, including the new file. This is relatively simple. Copy the edited <CODE>Jambase</CODE> into
the source directory for jam, and rebuild it.
</OL>
<P><!-- Jam - Separate Release/Debug Target Directories -->
<H1 CLASS="node">Jam - Separate Release/Debug Target Directories</H1>
<P><EM CLASS="node">2002/02/01 - 12:05pm | <A HREF="http://www.differentpla.net/taxonomy/page/or/16">Jam</A></EM></P>
<P>By default, AppWizard-generated applications use separate directories for the output of the different debug
and release builds. We'd like to replicate that functionality.</P>
<P>This is controlled by the <CODE>LOCATE_TARGET</CODE> variable. It's initialised by the <CODE>SubDir</CODE> rule
to be the same as the <CODE>SEARCH_SOURCE</CODE> variable, unless <CODE>ALL_LOCATE_TARGET</CODE> is set.</P>
<P>However, if we set <CODE>ALL_LOCATE_TARGET</CODE> to, e.g. <CODE>Debug</CODE> in our <CODE>Jamrules</CODE> file,
or using the <CODE>-s</CODE> switch to jam, the object files and targets are all built in exactly the same directory.
It's not relative to the subdirectory. This could cause confusion if directories generate object files with the
same names.</P>
<P>The comments in <CODE>Jambase</CODE> state that the <CODE>LOCATE_TARGET</CODE> variable should be set after
invoking the <CODE>SubDir</CODE> rule, if required. This is a pain. We much prefer the following change to the
<CODE>SubDir</CODE> rule:</P>
<DIV CLASS="snippet">
<PRE> # directory should not hold object files, LOCATE_TARGET can
# subsequently be redefined.
<B>local path = [ FDirName $(SUBDIR) $(TARGET_PREFIX) ] ;
SEARCH_SOURCE = $(SUBDIR) ;
LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(path) ;
LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(path) ;
SOURCE_GRIST = $(path) ;</B>
# Reset per-directory ccflags, hdrs
</PRE>
</DIV>
<P>This allows us to place a rule like the following in our <CODE>Jamrules</CODE> file:</P>
<DIV CLASS="snippet">
<PRE>if ! $(DEBUG) {
ECHO Assuming DEBUG=1 ;
DEBUG = 1 ;
}
if $(DEBUG) = 0 {
TARGET_PREFIX = Release ;
} else {
TARGET_PREFIX = Debug ;
}</PRE>
</DIV>
<P><!-- Jam - Resource File Dependencies -->
<H1 CLASS="node">Jam - Resource File Dependencies</H1>
<P><EM CLASS="node">2004/01/16 - 10:14am | <A HREF="http://www.differentpla.net/taxonomy/page/or/16">Jam</A></EM></P>
<P>
<H2>Introduction</H2>
<P>Our <A HREF="http://www.differentpla.net/mfc_app/">MFC application</A> has a resource script. This resource
script suffers from a minor problem: It's not dependency-scanned. If we edit any file included by it -- for example
the <CODE>.rc2</CODE> file, it's not rebuilt properly. We need to add the following to our <CODE>Resource</CODE>
rule:</P>
<DIV CLASS="snippet">
<PRE> NEEDLIBS on $(_e) += $(_r) ;
<B># .rc files have #includes, but this limits the dependency search to
# the .rc's directory and the SubDirHdrs for this directory.
HDRS on $(_r) = $(HDRS) $(SEARCH_SOURCE) $(SUBDIRHDRS) ;
HDRRULE on $(_s) = HdrRule ;
HDRSCAN on $(_s) = $(HDRPATTERN) ;
HDRSEARCH on $(_s) = $(SEARCH_SOURCE) $(SUBDIRHDRS) ;
HDRGRIST on $(_s) = $(HDRGRIST) ;</B>
Rc $(_r) : $(_s) ;
</PRE>
</DIV>
<P>Source is <A HREF="http://www.differentpla.net/src/jam-test-20010717a.tar.gz">here</A>.</P>
<H1>Building a DLL with Jam: Introduction</H1>
<H2>Introduction</H2>
<P>empeg's code base contains several Windows DLL projects. I'm going to investigate getting these built with jam
the <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/mfc_app/">same way</A> as I did for the MFC
applications -- by building a simple project with the AppWizard, and getting the project built.</P>
<UL>
<LI>Introduction / Creating the Jamfile.
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/cpp_flags.html">Compiler Flags</A>.
<LI><A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/entry_point.html">Entry Point</A>.
</UL>
<P>You can find the resulting source code from this example <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/src/jam-test-20010712a.tar.gz">here</A>.</P>
<P>
<HR ALIGN="CENTER">
<H2>Using AppWizard to generate the DLL</H2>
<P>As I've already stated, our DLLs don't use MFC, so we'll use the AppWizard to generate a "Win32 Dynamic-Link
Library". To mirror the structure used at empeg, we'll create the DLL in the <CODE>S:\jam-test\lib\</CODE>
directory, and we'll call it <CODE>win32_dll</CODE>.</P>
<P>The AppWizard wants to know what type of DLL we're creating, so we'll create one that exports some symbols.
We'll build it with Developer Studio, just to check.</P>
<H2>Creating the Jamfile</H2>
<P>The obvious thing to start with is a Jamfile that looks like this:</P>
<DIV CLASS="snippet">
<PRE>Main win32_dll : StdAfx.cpp win32_dll.cpp ;
</PRE>
</DIV>
<P>The <CODE>Main</CODE> rule section in the <CODE>Jambase</CODE> file is geared up to create a .EXE file, so we'll
have to hack on it a little. If we just copy it to a new rule called <CODE>SharedLibrary</CODE>, we can make our
changes there. It turns out that the only change we need to make is the suffix of the generated file:
<DIV CLASS="snippet">
<PRE> _t = [ FAppendSuffix $(<) : $(SUFSHR) ] ;
</PRE>
</DIV>
<P>We have to go and define SUFSHR somewhere, though. The definition of the <CODE>SharedLibrary</CODE> rule is
<A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/shared_lib_rule.html">here</A>.</P>
<P>Once we've added the new rules to Jambase, and changed "Main" to "SharedLibrary" in our
Jamfile, we get the following:</P>
<DIV CLASS="snippet">
<PRE>win32_dll.cpp(25) : error C2491: 'nWin32_dll' : definition of dllimport data not allowed
win32_dll.cpp(29) : error C2491: 'fnWin32_dll' : definition of dllimport function not allowed
win32_dll.cpp(36) : warning C4273: 'CWin32_dll::CWin32_dll' : inconsistent dll linkage. dllexport assumed.
</PRE>
</DIV>
<P>This doesn't work, so we'll take a look at the compiler command lines.</P>
<H1>Building a static library with Jam: Introduction</H1>
<H2>Introduction</H2>
<P>Once again, we're going to do what we did with the <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/mfc_app/">MFC
application</A> and <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/">DLL</A> examples:
build the project with AppWizard, and then get it built with jam.</P>
<H2>Using AppWizard to generate the library</H2>
<P>Run up Visual C++ and generate a new "Win32 Static Library" project. Call it <CODE>win32_lib</CODE>,
and put it in the <CODE>S:\jam-test\lib\win32_lib</CODE> directory. For now we want neither "Pre-Compiled
header" nor "MFC support".</P>
<P>This gives us a project with no files in it. We'll create a C++ file, and a header file (<CODE>something.cpp</CODE>
and <CODE>something.h</CODE>), and we'll create a simple function:</P>
<DIV CLASS="snippet">
<PRE>/* something.cpp */
#include "something.h"
#include <string.h>
int something(const char *p) {
return strlen(p) + 42;
}
</PRE>
</DIV>
<DIV CLASS="snippet">
<PRE>/* something.h */
int something(const char *p);
</PRE>
</DIV>
<P>We'll add these to the Visual C++ project, and check that it builds, as we did with the other examples: for
sanity's sake.</P>
<P>
<H2>Creating the Jamfile</H2>
<P>As we did with the other examples, we'll create a simple Jamfile. We'll also add all of the SubDir stuff necessary
to integrate it into our overall build system:</P>
<DIV CLASS="snippet">
<PRE>SubDir TOP lib win32_lib ;
Library win32_lib : something.cpp ;
</PRE>
</DIV>
<P>This builds, except that Jam says "warning: lib depends on itself". This is down to the fact that
one of jam's pseudotargets is called 'lib'. The <CODE>Jamfile.html</CODE> documentation gives three different options
to resolve this conflict:
<OL>
<LI>Change the name of the conflicting file.
<LI>Modify Jambase and change the name of the pseudotarget.
<LI>Use grist on the target name.
</OL>
<P>
<P>Now, I can't change the name of the conflicting file (or, in this case, directory) -- I'm looking to use jam
to build empeg's source code. We've had a directory called lib for over two years. It's in CVS with that name,
and there's over 180KLOC in there. That rules out option one.</P>
<P>I haven't figured out the implications of using grist on the target name, and a brief look at the example suggests
that this isn't viable anyway, so that kinda leaves us with option 2 -- changing Jambase. See <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/misc/conflict_lib.html">here</A>.</P>
<H1>Linking with a Shared Library</H1>
<H2>Introduction</H2>
<P>Now we've got our <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/mfc_app/">application</A>
and <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/">DLL</A> building as part of the
same project, we really ought to persuade them to link together.</P>
<P>The first thing we ought to do is establish some kind of dependency in the code. It's test-first, but for Jamfiles.
We'll do this by attempting to use one of the symbols exported from the DLL in our MFC application. We'll add this
piece of code to <CODE>InitInstance</CODE>:</P>
<DIV CLASS="snippet">
<PRE> int j = fnWin32_dll();
</PRE>
</DIV>
<P>This requires that we include the relevant header file:
<DIV CLASS="snippet">
<PRE>#include "win32_dll/win32_dll.h"
</PRE>
</DIV>
<P>It now compiles correctly, but fails to link. We need to establish a dependency on the import library generated
as part of the DLL build process. We can add this to the <CODE>mfc_exe\Jamfile</CODE>:
<DIV CLASS="snippet">
<PRE>LinkLibraries mfc_exe : win32_lib win32_dll ;
</PRE>
</DIV>
<P>This fails because jam doesn't know how to build win32_dll.lib. We need to add some extra stuff to the <CODE>SharedLibaryFromObjects</CODE>
rule:
<DIV CLASS="snippet">
<PRE> MakeLocate $(_t) : $(LOCATE_TARGET) ;
<B># Tell jam where it can find the import library
MakeLocate $(_t:S=$(SUFLIB)) : $(LOCATE_TARGET) ;</B>
Clean clean : $(_t) ;
</PRE>
</DIV>
<P>This compiles and links. It even runs -- if we make sure that the DLL can be found when loading the EXE.</P>
<P>
<P>If we use shared libraries on Unix, we'll have to invent a <CODE>SharedLinkLibraries</CODE> rule, because <CODE>gcc</CODE>
wants the name of the <CODE>.so</CODE> file in order to establish the runtime dependency, and <CODE>LinkLibraries</CODE>
assumes <CODE>.lib</CODE>.</P>
<P>Source code is <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/src/jam-test-20010716a.tar.gz">here</A>.</P>
<P>
<HR ALIGN="CENTER">
</P>
<H1>Bringing it together with the SubDir rule</H1>
<H2>Introduction</H2>
<P>Now we've built <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/mfc_app/">an application</A>
and <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/shared_lib/">a DLL</A>, we'd like to include
them in the same build process. This is what Jam's <CODE>SubDir</CODE> rule does.</P>
<P>In the top-level directory, i.e. <CODE>jam-test</CODE>, we place a <CODE>Jamfile</CODE> looking like this:</P>
<DIV CLASS="snippet">
<PRE>SubDir TOP ;
SubInclude TOP lib ;
SubInclude TOP apps ;
</PRE>
</DIV>
<P>We also need to create an empty <CODE>Jamrules</CODE> 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.</P>
<P>We also have to create corresponding <CODE>Jamfile</CODE> files for the other directories. The one in the <CODE>apps</CODE>
directory looks like this:</P>
<DIV CLASS="snippet">
<PRE>SubDir TOP apps ;
SubInclude TOP apps mfc_exe ;
</PRE>
</DIV>
<P>And we have to add <CODE>SubDir</CODE> invocations to the top of the original Jamfiles, so that they know where
they are.</P>
<P>We run the build from the top-level directory:</P>
<DIV CLASS="snippet">
<PRE>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
<B>... etc. ...</B>
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)...
S:\jam-test>
</PRE>
</DIV>
<P>If we attempt to run the <CODE>mfc_exe</CODE> executable, we find that it's "not a valid Win32 executable".
This would appear to be down to the <CODE>/dll</CODE> switch being passed to the linker: we've managed to build
a DLL and give it a .exe extension.</P>
<P>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.</P>
<P>At this point, we do something we should have done before: Since we're invoking our <CODE>SharedLibrary</CODE>
rule to build the DLL, we should add the <CODE>/dll</CODE> switch to the linker command line at that point.</P>
<P>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:</P>
<DIV CLASS="snippet">
<PRE>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 ;
</PRE>
</DIV>
<P>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 <CODE>Jambase</CODE>:</P>
<DIV CLASS="snippet">
<PRE>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) ;
}
</PRE>
<PRE>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) ;
}
</PRE>
</DIV>
<P>
<H2>Conclusions</H2>
<P>That was easy enough. We've still got a couple of things to work out:</P>
<UL>
<LI>We're not actually attempting to link the EXE with the DLL, so there's no dependency established.
<LI>The resource compiler flags aren't being reset. We've only got one resource script so far, so it's not an issue
yet.
</UL>
<P>You can find the source resulting from this <A HREF="http://www.differentpla.net/%7Eroger/devel/jam/tutorial/src/jam-test-20010713a.tar.gz">here</A>.</P>
<P>
<H1>System Libraries</H1>
<P>Obviously, your code doesn't just link with your libraries. It also has to link with some of the system libraries.
Jam manages this by using the <CODE>LINKLIBS</CODE> variable. The simplest way to make this work is something like
the following:</P>
<DIV CLASS="before">
<PRE>LINKLIBS on emplode.exe += ws2_32.lib ;
</PRE>
</DIV>
<P>Here you can see that we're telling jam to pass <CODE>ws2_32.lib</CODE> on to the linker when it tries to link
<CODE>emplode.exe</CODE>.</P>
<P>The main problem with this approach is that it's a bit Windows-specific. If we put aside the fact that we know
we've got to link with <CODE>ws2_32.lib</CODE> for a moment, we still can't ignore the fact that the Jamfile needs
to know that the target is called <CODE>emplode.exe</CODE>. It hasn't yet had to know.</P>
<P>[Say something about the SystemLibraries rule, including the fact that it doesn't work with DLLs]
</BODY>
</HTML>