GNU Make Utility

Makefiles and Visual Studio

An introduction to Makefiles for Visual Studio developers

Continuing from the preface Makefiles for Visual Studio Developers

Visual Studio provides features that are taken for granted until you have to read/create a classic Makefile. For example, Visual Studio auto-magically does the following.

  • Compiles all the sources in the project file
  • Create an output directory and puts all the intermediate object files in it
  • Links the object files and external dependent libraries to create binaries
  • Manages dependencies between the source and object files
  • Manages dependencies between the object files and binaries

All of the above have to be explicitly specified in a Makefile. The make utility in some ways is the equivalent of Visual Studio devenv.exe (without the fancy GUI).

Under the hood of Visual Studio
GNU/Linux Equivalent
Visual Studio to Make Utility mapping
Example
    Source Structure
    Build
    Run
Makefile Details
    Targets
    Dependencies
    Contents
NMake
Conclusion
External Links

Under the hood of Visual Studio

Visual Studio is essentially a GUI over the compilation and link process. It utilizes an underlying command line compiler and linker. Additionally, it provides a source code editor, debugger and other development tools.

c:\tmp> cl.exe
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

usage: cl [ option... ] filename... [ /link linkoption... ]
c:\tmp> link.exe
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

 usage: LINK [options] [files] [@commandfile]

A simple win32 console application project in Visual Studio is shown below.

Test solution

Invoking a build on the solution in Visual Studio calls something like the following under the hood.

C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\CL.exe /c /ZI /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fo"Debug\\" /Fd"Debug\vc100.pdb" /Gd /TP /analyze- /errorReport:prompt Test.cpp

C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\link.exe /ERRORREPORT:PROMPT /OUT:"c:\tmp\Test\Debug\Test.exe" /INCREMENTAL /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /ManifestFile:"Debug\Test.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"c:\tmp\Test\Debug\Test.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"c:\tmp\Test\Debug\Test.lib" /MACHINE:X86 Debug\Test.exe.embed.manifest.res Debug\Test.obj

Yes, it looks ugly! But that is the the project properties translated to compiler/linker flags and options.

GNU/Linux Equivalent

It is very similar in GNU/Linux. The equivalent of a compiler and linker is gcc, the GNU project C and C++ compiler. It does the preprocessing, compilation, assembly and linking.

$ gcc --help
Usage: gcc [options] file...

Shown below is a very simple Makefile which can be accessed from GitHub https://github.com/cognitivewaves/Simple-Makefile.

# Specify compiler
CC=gcc

# Specify linker
LINK=gcc

.PHONY : all
all : app

# Link the object files into a binary
app : main.o
	$(LINK) -o app main.o -lstdc++

# Compile the source files into object files
main.o : main.cpp
	$(CC) -c main.cpp -o main.o

# Clean target
.PHONY : clean
clean :
	rm main.o app 

Invoking the make command to build will output the following.

~/src/Simple-Makefile $ make
gcc -c main.cpp -o main.o
gcc -o app main.o -lstdc++

Visual Studio to Make Utility mapping

Below is a table relating Visual Studio aspects to Makefiles.

Visual Studio make Utility
Command devenv.exe make
Source files list Project (.vcproj) Makefile
Source structure Solution (.sln) has project files which could be in sub-directories Starting at the root, each Makefile can indicate where other Makefiles (typically sub-directories) exist
Source to Object dependency Project (.vcproj) Makefile
Library build dependency Solution (.sln) has projects and build order Makefile
Compile and Link options Project (.vcproj) Makefile
Compiler cl.exe gcc, g++, c++ (or any other compiler, even cl.exe)
Linker link.exe gcc, ld (or any other linker)

Example

Download the example sources from GitHub at https://github.com/cognitivewaves/Makefile-Example.
Note that very basic Makefile constructs are used because the focus is on the concept and not the capabilities of make itself.

Source Structure

Makefile-Example
   |-- app
   |   |-- Makefile
   |   |-- main.cxx
   |-- math
   |   |-- advanced
   |   |   |-- AdvancedFunctions.cxx
   |   |   |-- AdvancedFunctions.h
   |   |-- simple
   |   |   |-- SimpleFunctions.cxx
   |   |   |-- SimpleFunctions.h
   |   |-- Makefile
   |-- Makefile

Build

Visual Studio make Utility
Building in Visual Studio is via a menu item in the IDE or invoking devenv.exe on the .sln file at the command prompt. This will automatically create the necessary directories and build only the files modified after the last build. Initiating a build with makefiles is to invoke the make command at the shell prompt. Creating output directories has to be explicitly done either in the Makefile or externally.

In this example, to keep the makefiles simple, the directories are created at the shell prompt.

~/src/Makefile-Example $ mkdir -p app/bin app/obj math/bin math/obj

The make utility syntax is shown below. See make manual pages for details.

make [ -f makefile ] [ options ] ... [ targets ]

Execute the command make and specify the “root” Makefile. However, it is more common to change to directory where the “root” Makefile exists and call make. This will read the file named Makefile in the current directory and call the target all by default.
Notice how make enters sub-directories to build. This is because of nested Makefiles which is explained later in the Makefiles Details section.

~/src/Makefile-Example $ make
make -C math make[1]: Entering directory `/home/user/src/Makefile-Example/math' g++ -fPIC -I. -c -o obj/AdvancedFunctions.o advanced/AdvancedFunctions.cxx g++ -fPIC -I. -c -o obj/SimpleFunctions.o simple/SimpleFunctions.cxx g++ -fPIC -shared -o bin/libmath.so obj/AdvancedFunctions.o obj/SimpleFunctions.o make[1]: Leaving directory `/home/user/src/Makefile-Example/math' make -C app make[1]: Entering directory `/home/user/src/Makefile-Example/app' g++ -I../math -c -o obj/main.o main.cxx g++ obj/main.o -L../math/bin -o bin/app.exe -lmath make[1]: Leaving directory `/home/user/src/Makefile-Example/app'

Run

Once the code is built, run the executable. This is nothing specific to makefiles but has been elaborated in case you are not familiar with Linux as you will notice that by default is will fail to run with an error message.

~/src/Makefile-Example $ app/bin/app.exe 4
app/bin/app.exe: error while loading shared libraries: libmath.so: cannot open shared object file: No such file or directory

This is because the executable app.exe requires the shared object libmath.so which is in a different directory and is not in the system path. Set LD_LIBRARY_PATH to specify the path to it.

~/src/Makefile-Example $ export LD_LIBRARY_PATH=~/src/Makefile-Example/math/bin/

~/src/Makefile-Example $ app/bin/app.exe 4
The sum of 4 and 4 is 8 The square root of 4 is 2

Makefile Details

The basis of a Makefile has a very simple structure.

target: dependencies
[tab] system commands

The [tab] separator is very important! Spaces instead of a [tab] is not the same. You will see rather obscure error messages as shown below.
Makefile:12: *** missing separator. Stop.

Targets

Here target is a physical file on disk. When the target is more of a label, then it has to be tagged as .PHONY to indicate that the target is not an actual file.

Visual Studio make Utility
Visual Studio by default provides options to clean and rebuild a project or solution. Clean and rebuild have to be explicitly written in a makefile as targets which can then be invoked.

A typical case would be to clean before rebuilding.

~/src/Makefile-Example $ make clean
make -C math clean make[1]: Entering directory `/home/api/src/Makefile-Example/math' rm -fv bin/* obj/* removed `bin/libmath.so' removed `obj/AdvancedFunctions.o' removed `obj/SimpleFunctions.o' make[1]: Leaving directory `/home/api/src/Makefile-Example/math' make -C app clean make[1]: Entering directory `/home/api/src/Makefile-Example/app' rm -fv bin/* obj/* removed `bin/app.exe' removed `obj/main.o' make[1]: Leaving directory `/home/api/src/Makefile-Example/app'

Dependencies

Dependencies can be files on disk or other targets (including phony targets).

Visual Studio make Utility
Visual Studio by default supports implicit dependencies (source to object files) within a project. Library(project) dependencies have to specified in the solution file Every dependency has to be explicitly defined in makefiles

For example, the target all, depends on app.exe which in turn depends on libmath.so. If you remove app.exe, make is capable of recognizing that libmath.so need not be built again.

~/src/Makefile-Example $ rm -v app/bin/*
removed `app/bin/app.exe'
~/src/Makefile-Example $ make
make -C app make[1]: Entering directory `/home/api/src/Makefile-Example/app' g++ obj/main.o -L../math/bin -o bin/app.exe -lmath make[1]: Leaving directory `/home/api/src/Makefile-Example/app'

Contents

File: Makefile

# all is the default target
.PHONY : all 
all : app/bin/app.exe

# Invoke the Makefile in the sub-directory "app"
app/bin/app.exe : math/bin/libmath.so
	$(MAKE) -C app

# Invoke the Makefile in the sub-directory "math"
math/bin/libmath.so :
	$(MAKE) -C math

# Invoke the clean target in the "math" and "app" sub-directory Makefile
.PHONY : clean
clean :
	$(MAKE) -C math clean
	$(MAKE) -C app  clean

File: math/Makefile

# The shared object libmath.so depends on the object files
# The command is to link and create the .so if the dependencies are up to date
# Note the .so is put in a sub-directory 'bin' by specifying 'g++ -o' option
bin/libmath.so : obj/AdvancedFunctions.o obj/SimpleFunctions.o
	g++ -fPIC -shared -o bin/libmath.so obj/AdvancedFunctions.o obj/SimpleFunctions.o

# The object file depends on the source
# The command is to compile the .cxx to .o
obj/AdvancedFunctions.o : advanced/AdvancedFunctions.cxx
	g++ -fPIC -I. -c -o obj/AdvancedFunctions.o advanced/AdvancedFunctions.cxx

# The object file depends on the source
# The command is to compile the .cxx to .o
obj/SimpleFunctions.o : simple/SimpleFunctions.cxx
	g++ -fPIC -I. -c -o obj/SimpleFunctions.o simple/SimpleFunctions.cxx

# clean target for this sub-directory Makefile
.PHONY : clean
clean : 
	rm -fv bin/* obj/

File: app/Makefile

# The executable depends on the main.o object file
# Generate the exe if the object file dependencies are up to date
# The exe is put in a sub-directory 'bin' by specifying 'g++ -o' option
bin/app.exe : obj/main.o
	g++ obj/main.o -L../math/bin -o bin/app.exe -lmath

# The object file depends on the source
# The command is to compile the .cxx to .o
# The object file is put in sub-directory 'obj' by specifying 'g++ -o' option
obj/main.o : main.cxx
	g++ -I../math -c -o obj/main.o main.cxx

# clean target for this sub-directory Makefile
.PHONY : clean
clean : 
	rm -fv bin/* obj/

NMake

NMake is the native Windows alternative to the *nix make utility. The syntax is very similar to *nix makefiles. However, this does not mean that *nix makefiles can be executed seamlessly on Windows. See Makefiles in Windows for a discussion.

Conclusion

Makefiles are very powerful and gives a lot of control and flexibility compared to Visual Studio, but the content is not easily understandable. As an alternative, CMake has adopted similar concepts but the script is much easier and more readable. See CMake and Visual Studio.

External Links

Makefile in detail for beginners

Advertisements

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s