There are a few techniques available to build both 32-bit and 64-bit targets.
The best approach depends on the specific needs of the project. A major
problem with building two versions of targets from one set of files is
to make sure the intermediate files, such as
.o files, do not
clobber each other so incremental updates are properly supported.
The following techniques also allow different values
CC to be used and thus different probe files, which may be
important. See Probe Considerations below for details.
The examples assume the compiler uses the
-m64 flags to target 32-bit and 64-bit output respectively but
compiler tool chains may vary.
When delivering separate 32-bit and 64-bit versions of the entire product it makes sense to build them separately in different viewpath nodes using a common node for the source code. The builds are treated as separate platform builds and do not impact each other. This approach does not require logic changes to makefiles and should be relatively easy to set up.
The current build directory can be used to determine the build type and enable the appropriate compiler options. Alternatively a variable can be used to specify the build type since developers' build directories may be named differently. The following example can be used in the global makefile to enable the appropriate compiler option based on the directory or the BITS variable. Note that the directory name takes precedence over the BITS variable so using the wrong BITS value in a known build directory won't pollute the build. Otherwise setting BITS=64 on the nmake command line enables the 64-bit build.
CC = cc if "$(PWD:N=*/platform/*64*)" CCFLAGS += -m64 elif "$(PWD:N=*/platform/*32*)" CCFLAGS += -m32 elif "$(BITS:N=64)" CCFLAGS += -m64 else CCFLAGS += -m32 end
Run the 32-bit build in its build directory.
$ echo $VPATH /builds/proj1.0/platform/sol32:/builds/proj1.0/source $ cd /builds/proj1.0/platform/sol32/src $ nmake install + cc -O -m32 -I- -c /builds/proj1.0/source/src/a.c + cc -O -m32 -I- -c /builds/proj1.0/source/src/b.c + cc -O -m32 -I- -c /builds/proj1.0/source/src/c.c + cc -O -m32 -o targ a.o b.o c.o + cp targ ../bin/targ
Run the 64-bit build in its build directory.
$ echo $VPATH /builds/proj1.0/platform/sol64:/builds/proj1.0/source $ cd /builds/proj1.0/platform/sol64/src $ nmake install + cc -O -m64 -I- -c /builds/proj1.0/source/src/a.c + cc -O -m64 -I- -c /builds/proj1.0/source/src/b.c + cc -O -m64 -I- -c /builds/proj1.0/source/src/c.c + cc -O -m64 -o targ a.o b.o c.o + cp targ ../bin/targ
When building in some other directory specify BITS=64 to build the 64-bit targets, otherwise the 32-bit targets are built by default.
$ echo $VPATH /home/richb/proj1.0:/builds/proj1.0/source $ cd /home/richb/proj1.0/src $ nmake BITS=64 install + cc -O -m64 -I- -c /builds/proj1.0/source/src/a.c + cc -O -m64 -I- -c /builds/proj1.0/source/src/b.c + cc -O -m64 -I- -c /builds/proj1.0/source/src/c.c + cc -O -m64 -o targ a.o b.o c.o + cp targ ../bin/targ
When some components needs to be delivered with the product as both 32-bit and 64-bit then it may make sense to build both versions together. One approach uses separate directories for the 32-bit and 64-bit compiles. In the following example the source code is in a parent directory. The Makefile in the parent recurses to the sub-directories for each target version. The Makefiles in the sub-directories just include a common makefile from the parent for building the code which keys off the directory name to build the 32-bit or 64-bit versions.
$ ls -l total 16 drwxr-xr-x 2 richb richb 4096 Oct 18 17:14 32bit drwxr-xr-x 2 richb richb 4096 Oct 18 17:14 64bit -rw-r--r-- 1 richb richb 19 Oct 18 16:43 Makefile -rw-r--r-- 1 richb richb 59 Oct 18 16:41 a.c -rw-r--r-- 1 richb richb 58 Oct 18 16:41 b.c -rw-r--r-- 1 richb richb 74 Oct 18 16:41 c.c -rw-r--r-- 1 richb richb 99 Oct 18 17:12 cmd.mk $ cat Makefile :MAKE: 32bit 64bit $ cat cmd.mk CC = cc BIT := $(PWD:B:C/bit//) CCFLAGS += -m$(BIT) .SOURCE: .. :ALL: targ$(BIT) :: a.c b.c c.c $ cat 32bit/Makefile include ../cmd.mk $ cat 64bit/Makefile include ../cmd.mk $ nmake 32bit: + cc -O -m32 -I- -c ../a.c + cc -O -m32 -I- -c ../b.c + cc -O -m32 -I- -c ../c.c + cc -O -m32 -o targ32 a.o b.o c.o 64bit: + cc -O -m64 -I- -c ../a.c + cc -O -m64 -I- -c ../b.c + cc -O -m64 -I- -c ../c.c + cc -O -m64 -o targ64 a.o b.o c.o $ vi b.c $ nmake 32bit: + cc -O -m32 -I- -c ../b.c + cc -O -m32 -o targ32 a.o b.o c.o 64bit: + cc -O -m64 -I- -c ../b.c + cc -O -m64 -o targ64 a.o b.o c.o
Another approach is to recurse the same directory multiple times using different makefiles to build each target version. To make maintenance easier all the rules to build both versions are in the first makefile. It uses the current makefile name to determine which version to build. The second makefile just includes the first one. A custom metarule is used to prevent object files from clobbering each other.
$ ls -l total 8 -rw-r--r-- 1 richb richb 27 Oct 20 15:05 Makefile -rw-r--r-- 1 richb richb 59 Oct 20 14:58 a.c -rw-r--r-- 1 richb richb 58 Oct 20 14:58 b.c -rw-r--r-- 1 richb richb 74 Oct 20 14:58 c.c -rw-r--r-- 1 richb richb 244 Oct 20 15:11 cmd32.mk -rw-r--r-- 1 richb richb 34 Oct 20 15:10 cmd64.mk $ cat Makefile :MAKE: cmd32.mk cmd64.mk $ cat cmd32.mk CC = cc FILES = a.c b.c c.c OBJ32 = $(FILES:B:S=.o) OBJ64 = $(FILES:B:S=64.o) %64.o : %.c (CC) (CCFLAGS) $(CC) $(CCFLAGS) -o $(<) -c $(>) :ALL: if "$(MAKEFILE:N=*64.mk)" CCFLAGS += -m64 targ64 :: $(OBJ64) else CCFLAGS += -m32 targ32 :: $(OBJ32) end $ cat cmd64.mk include cmd32.mk $ nmake cmd32.mk: + cc -O -m32 -I- -c a.c + cc -O -m32 -I- -c b.c + cc -O -m32 -I- -c c.c + cc -O -m32 -o targ32 a.o b.o c.o cmd64.mk: + cc -O -m64 -I- -o a64.o -c a.c + cc -O -m64 -I- -o b64.o -c b.c + cc -O -m64 -I- -o c64.o -c c.c + cc -O -m64 -o targ64 a64.o b64.o c64.o $ vi b.c $ nmake cmd32.mk: + cc -O -m32 -I- -c b.c + cc -O -m32 -o targ32 a.o b.o c.o cmd64.mk: + cc -O -m64 -I- -o b64.o -c b.c + cc -O -m64 -o targ64 a64.o b64.o c64.o
The nmake probe tool configures nmake for the compiler tool chain and platform environment. See the probe FAQ for details. In cases where the compiler searches different include and/or library directories based on the 32/64-bit options, the compiler should be probed with the option so nmake is aware of the proper search directories and settings. When not using the proper probe configuration nmake will reference headers and libraries for the compiler's default bit model rather than that specified by the compiler flag which could be prone to build problems.
For such compilers it is preferred to include the compiler flag in the
CC variable instead of
CCFLAGS so nmake will probe
the compiler for the specific configuration
CC = cc -m64).
Such compiler tool chains include versions of GCC, Solaris Studio and possibly
others. The methods described above can be easily adapted to
CC and thus use different probe configurations for the
different types of compiles.
As of nmake 13 probing the compiler with an alternate bit model does not
work properly and results in wrong settings related to shared libraries.
For example, if the compiler defaults to 32-bit then using
CC = cc -m64 sets some probe
variables wrong. This was fixed in nmake 14, see the
For more general techniques to build the same source files with different compiler options see the FAQ, How to compile the same code with different compiler options?