Makefile Introduction
1 Makefile Introduction
Windows CE’s build system heavily uses Nmake and makefiles. Most Microsoft software and driver development kits include Nmake. So it’s necessary to introduce makefiles and the Nmake tool.
1.1 Makefile Overview
For many Windows programmers, makefile might be unfamiliar. Windows IDEs (like Visual Studio and Borland C++ Builder) handle what makefiles do. Usually, just press a button in the IDE and the tool automatically compiles and links the project. Without an IDE, you need another way to manage project building.
Simply put, makefiles help developers simplify compilation, linking, and other build tasks. For simple projects with a few files, you can manually control the compiler and linker. But imagine a large project with hundreds or thousands of files — doing everything manually would be unmanageable. That’s where makefiles shine.
Makefiles define the compilation rules for the entire project. Source files are organized in directories by type, function, or module. Makefiles specify which files to compile first, which later, which need recompilation, and even more complex operations — makefiles are like automation scripts that can execute OS commands.
The biggest benefit of makefiles is “automated building.” After writing a makefile, a single command automatically compiles and links the entire project, greatly improving development efficiency.
A makefile is essentially a text file that can’t run itself. An external program interprets and executes it. NMake.exe is the tool that parses and executes makefiles.
When you type nmake, Nmake reads the makefile, parses it, determines which code to compile based on the rules, then calls the compiler and linker to compile and link the code, ultimately generating executables.
Figure: Makefile workflow
Notably, makefiles aren’t specific to Windows CE or Microsoft. They’re a universal automation build method widely used on Unix/Linux platforms. Many development tools provide NMake-like tools: Delphi’s make, Visual C++’s nmake, GNU’s make on Linux.
1.2 Makefile Rules
Makefiles consist of derivation rules (called Description Blocks in NMake). Basic syntax:
targets... : dependents...
commands...
targets — target files, can be object files, executables, or labels. Must be at the start of a line. dependents — files needed to generate the target. Separated from targets by a colon. commands — commands NMake executes. Can be any Windows command-line command.
This defines a file dependency: if any dependent file is newer than the target file, the commands execute. This is the core concept of makefiles.
1.3 A Working Makefile Example
Create a hello directory under %_WINCEROOT%\PBWorkspaces\MyPlatform, then create hello.cpp:
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MessageBox(NULL, L"Hello", L"bb", 0);
}
Create a file named makefile (no extension) in the hello directory:
#This is a demo makefile
hello.exe: hello.obj
@echo linking...
link -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -entry:WinMainCRTStartup -LIBPATH:E:\WINCE500\PBWorkspaces\MyPlatform\priv\...
hello.obj: hello.cpp
@echo compiling...
cl -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE -DUNDER_CE=500 -D_WIN32_WCE=500 -DWIN32 -DSTRICT -Dx86 -D_X86_ -DINTERNATIONAL -DL0804 -DINTLMSG_CODEPAGE=1252 hello.cpp
clean:
del hello.obj, hello.exe
Open the Windows CE console from Start → Programs → Microsoft Windows CE 5.0, cd to hello, type nmake.
1.4 Using Variables
To make makefiles easier to maintain, use variables (like C macros):
TARGETNAME = hello
SOURCES = hello.cpp
TARGETLIBS = $(TARGETNAME).obj coredll.lib corelibc.lib
CPPFLAGS = -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE ...
LFLAGS = -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -entry:WinMainCRTStartup ...
$(TARGETNAME).exe: $(TARGETNAME).obj
@echo linking...
$(LINK) $(LFLAGS) $(TARGETLIBS)
$(TARGETNAME).obj: $(SOURCES)
@echo compiling...
$(CPP) $(CPPFLAGS) $(SOURCES)
1.5 Using Preprocessing
NMake supports preprocessing for conditional processing, error messages, including other makefiles, etc.
!IFDEF EXEENTRY
! MESSAGE EXEENTRY: $(EXEENTRY)
EXEENTRYOPTION=-entry:$(EXEENTRY)
!ELSE
EXEENTRYOPTION=-entry:WinMainCRTStartup
!ENDIF
1.6 Including Other Files
Use !INCLUDE to split makefiles into reusable parts:
sources file:
TARGETNAME = hello
SOURCES = hello.cpp
EXEENTRY = WinMain
TARGETLIBS = coredll.lib corelibc.lib
makefile file:
!INCLUDE makefile.def
