Click here to Skip to main content
6,629,885 members and growing! (25,163 online)
Email Password   helpLost your password?
Languages » C / C++ Language » Howto     Beginner License: The Code Project Open License (CPOL)

Make Quick Look

By metcarob

A quick look at a make file and how to create more advanced make files
C++, Windows, Win32, Dev
Version:2 (See All)
Posted:5 Jul 2009
Views:2,469
Bookmarked:16 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 3.59 Rating: 4.62 out of 5

1

2

3
3 votes, 50.0%
4
3 votes, 50.0%
5

Introduction

In my other articles I have used manually created make files. Make is a program that runs a set of steps to produce an output. In most cases the set of steps involve calling compilers to compile source code. Make automatically checks if the source files have been updated before it takes the steps to create the output files. This means that only changed parts of a program are recompiled rather than the entire program.

I have previously stuck to simple make files but with the make files I have created so far there have been a couple of problems/missing features.

  • Produced files are mixed in with source files
  • There is no Debug/Release functionality

In this article I will explain the make structure I currently have and show how I extend it to include the above features.

Background

Some people have told me that they believe hand coding make is a waste of time. People have suggested using compiler tools as well as automake etc. My thinking behind using make is as follows:

Pros

  • Make files in my article simply describe the build process
  • I understand and can see all the steps in my build process
  • I don't have to figure out what compilers or automake processes are actually doing. With Make I know exactly the commands it is running.
  • People following the articles can follow using different compilers. - People reading the articles can see the make file I use then work out all their own compiler specific things
  • KIS - Keep it simple. This is a golden rule I always like to follow and making make files is simple compared with trying to get an IDE to produce the make files.

Cons

  • I don't currently have in editor debugging capability - When an error is reported I have to manually search for the line number that the error occurred on.
  • I have to put the time aside to create the make file - I think this is acceptable. I don't change the build process that often in development and I have made a pattern which I always follow (and describe here).

Prerequisites for following this tutorial

I have created a Codeproject article that completely shows how I set up the tool chain I use for compiling C++ programs. It is at opensource_tool.aspx

I will go through changing a make file I have used in another article. You view it at starting_sqlite_in_cpp.aspx. You should follow this before following this article to get the right files. If you want to skip this I have included a zip file with this article. You should unzip all the files to C:\code\sqlite_hello_world

Very Simple Make File Example

A make file has a 'top bit' and then any number of sections. The top bit is generally used to setup variables etc. and each section is used to supply instructions telling make how to create a file. Follow these steps to setup out project directory and create a simple little make file. You can use Shift + Ins in MINGW to paste each command to avoid typing errors.

  • Run MINGW (Click on the Blue M)
  • cd /c/code/
  • mkdir make_messing
  • "/c/program files/notepad++/notepad++.exe" /c/code/make_messing/makefile
  • Paste in the following code, then save and close notepad++
$(warning Starting Makefile)
CXX=g++

fin.txt: 
	$(warning Making fin.txt)
	echo Messing about with Make > fin.txt

This is a simple make file with the 'top bit' and one section. The first line contains a warning statement which is just a way of printing a message to the screen during build. The second line sets up a variable. The Line fin.txt: denotes the start of the first section. The layout of the section is:

<Output file>:<prerequisites ...>
	command 1
	command 2
	...
	

Output file is the name of the file to output, prerequisites is the files that must be present in order to create the output file. Command 1, 2 etc. are the shell commands used to create the file. Each command line starts with a tab character and there is a blank line separating each section.

In out example the file we are creating is fin.txt and we don't need any files to make it. There are two commands to make it, the first just tells us what make is doing and the second is just an echo command piped into a file called fin.txt. To run this launch MINGW and follow these steps:

  • cd /c/code/make_messing/makefile
  • make

You can see that the file is produced

  • ls

As you can see fin.txt now exists.

  • make

You can now see the message `fin.txt' is up to date which shows us that make is doing it's job. It hasn't processed the command to make the file that is already there. Now lets enhance the makefile a bit:

  • Run MINGW (Click on the Blue M)
  • "/c/program files/notepad++/notepad++.exe" /c/code/make_messing/makefile
  • Paste in the following code, then save and close notepad++
$(warning Starting Makefile)
CXX=g++
.PHONY: clean

fin.txt: 
	$(warning Making fin.txt)
	echo Messing about with Make > fin.txt

clean:
	$(warning Deleting all txt files)
	rm *.txt
	

The first thing to note is that the file now has two sections. How does make know which one to process? Well when it is run with no parameters make will always run the first section. In this case fin.txt is processed. If you want to make a different output file you can specify it as the first parameter, e.g. make fin.txt or make clean, etc.

The Clean section

Another thing to note is that the clean section is not a real section. The third line declares it as phoney. This tells make that there is no file called clean created but the commands are run when requested. Clean is a standard section put into a lot of make files which deletes all the files that make produced. This is useful to force a rebuild or if you are working on changes to the makefile. You can run make clean before backing up your code to save space. Experiment with this file a bit and note how the txt file appears and disappears and how make will not rebuild it if it is already there.

Now alter the makefile so it shows the following:

$(warning Starting Makefile)
CXX=g++
.PHONY: clean

fin1.txt: fin2.txt
	$(warning Making fin.txt)
	cat fin2.txt > fin1.txt
	echo done with 2 >> fin1.txt

fin2.txt: fin3.txt
	$(warning Making fin.txt)
	cat fin3.txt > fin2.txt
	echo done with 3 >> fin2.txt

fin3.txt: fin4.txt
	$(warning Making fin.txt)
	cat fin4.txt > fin3.txt
	echo done with 4 >> fin3.txt
	
fin4.txt: 
	$(warning Making fin.txt)
	echo We are still messing about with Make > fin4.txt

clean:
	$(warning Deleting all txt files)
	rm *.txt
	

You can see that this makefile has five sections. The clean section we talked about earlier. You can see that fin1.txt depends on fin2 which depends on fin3 which depends on fin4. You can use make to make the whole lot and make will work out which files needs to be recreated. Make also looks at the date stamps of the files, so once you have made all the files, if you change fin3.txt you will note that make will see this and recreate fin2.txt and fin1.txt. This is useful in programming. You make objects dependant on their headers and make will rebuild objects that need to. This saves time as objects that haven’t changed aren’t rebuilt.

You can also add a section to make a final file which depends on them all:

fin.txt:fin1.txt fin2.txt fin3.txt fin4.txt 
	$(warning putting it all together)
	cat fin1.txt >> fin.txt
	cat fin2.txt >> fin.txt
	cat fin3.txt >> fin.txt
	cat fin4.txt >> fin.txt

Remember make by default always builds the first section it sees so place this at the top of your make file. As this file depends on all the other files make will create all the other files first. Also all of this has lots of warnings which tell us what make is doing here but I wouldn't put in a real makefile.

For a laugh we can try and confuse make with something like the following:

aa.txt:bb.txt
	echo aa > aa.txt
	
bb.txt:aa.txt
	echo bb > bb.txt

This can't be built and make warns us and tries its best.

Start using variables

Have a look at the following make file:

$(warning Starting Makefile)
CXX=g++
.PHONY: clean

fin.txt:
	echo Make variable test > fin.txt
	echo CXX=${CXX} >> fin.txt
	echo CFG=${CFG} >> fin.txt

clean:
	rm *.txt
	

This file uses a built in variable and an external variable. You can run it by using make, or you can pass a variable to it by running a command like: make -e CFG=Test_other_var As you can see this will allow us change what we do based on command line parameters. We can also add some code to give the external variables default values, and check if it is the correct value:

$(warning Starting Makefile)
CXX=g++
.PHONY: clean

ifndef CFG
CFG=DEBUG
$(warning Defaulting to $(CFG))
endif

ifneq ($(CFG),DEBUG)
ifneq ($(CFG),RELEASE)
$(error error is CFG must be DEBUG or RELEASE)
endif
endif

fin.txt:
	echo Make variable test > fin.txt
	echo CXX=${CXX} >> fin.txt
	echo CFG=${CFG} >> fin.txt

clean:
	rm *.txt
	

The Error command is different to the Warning command because Error will exit the make process. You can see here that the variable CFG is defaulted to DEBUG but the user can enter a value. If the value is not DEBUG or RELEASE the build process errors. You can test this make file with the commands:

make
make -e CFG=ddd
make -e CFG=RELEASE
Note: Remember to use make clean in between tests.

Putting what we know to use

Ok, now we need to use this to allow us to make debug and release builds and store the created files somewhere other than our code directory. I will do this using the sqlite example project I created. Follow these instructions:

  • Run MINGW
  • "/c/program files/notepad++/notepad++.exe" /c/code/sqlite_hello_world/makefile
  • Paste in the following code, then save and close notepad++
$(warning Starting Makefile)
CXX=g++
ifndef CFG
CFG=DEBUG
$(warning Defaulting to $(CFG))
endif

ifneq ($(CFG),DEBUG)
ifneq ($(CFG),RELEASE)
$(error error is CFG must be DEBUG or RELEASE)
endif
endif

.PHONY: clean

${CFG}/main.exe: ${CFG}/dir_exists.obj ${CFG}/sqlite_demo.lib main.cpp
	$(CXX) -s main.cpp -o ${CFG}/main.exe -Wl,${CFG}/sqlite_demo.lib

${CFG}/sqlite_demo.lib: ${CFG}/dir_exists.obj ${CFG}/sqlite3.obj ${CFG}/RJM_SQLite_Resultset.obj ${CFG}/RJMFTime.obj
	ar cq $@ ${CFG}/RJMFTime.obj
	ar cq $@ ${CFG}/RJM_SQLite_Resultset.obj
	ar cq $@ ${CFG}/sqlite3.obj
	
${CFG}/sqlite3.obj: ${CFG}/dir_exists.obj sqlite3.c
	gcc -c sqlite3.c -o ${CFG}/sqlite3.obj -DTHREADSAFE=1
	
${CFG}/RJM_SQLite_Resultset.obj: ${CFG}/dir_exists.obj RJM_SQLite_Resultset.cpp RJM_SQLite_Resultset.h Glob_Defs.h RJMFTime.h sqlite3.h
	$(CXX) -c RJM_SQLite_Resultset.cpp -o ${CFG}/RJM_SQLite_Resultset.obj

${CFG}/RJMFTime.obj: ${CFG}/dir_exists.obj RJMFTime.cpp RJMFTime.h
	$(CXX) -c RJMFTime.cpp -o ${CFG}/RJMFTime.obj

${CFG}/dir_exists.obj:
	-mkdir ${CFG}
	-echo dir exists > ${CFG}/dir_exists.obj
	
clean: 
	-rm DEBUG/*.*
	-rm RELEASE/*.*
	-rmdir DEBUG
	-rmdir RELEASE

Notes

  • The Clean section will delete all files from DEBUG and RELEASE and deletes the directories themselves. It does all the files which is useful for cleaning up directories before back ups.
  • If you didn't do a make clean before you changed the makefile you may have to delete the .obj, .lib and .exe files from /c/code/sqlite_hello_world/ (You might also want to delete datafile.sqlite)
  • There is a fake object file dir_exists.obj. This allows make to create the directory if it doesn’t exist.

Explanation

Basically what I have done is added ${CFG} in front of the object files. This means that the files are created in the relevant directories. I also made all the files dependant on ${CFG}/dir_exists.obj. This means that the directory will be created if it doesn’t exist. You can also see an enhanced clean section to deal with the sub directories.

Fixing notepad++ bat files to work with this

When I set up the tool chain I added menu items into notepad++ menu. This automatically launched the program. This won't now work as the program is in a different place. To fix this follow these instructions:

  • "/c/program files/notepad++/notepad++.exe" /c/code/run.bat
  • Paste in the following code, then save and close notepad++
:##BATCH to run fron notepad++
c:
cd\
cd %1
make
pause
cd DEBUG
main.exe
pause

This default menu option will automatically run the DEBUG version of the code. You should note that the data files used in this example are also stored in the DEBUG directory and are always recreated when the program is rebuilt. This behaviour is program specific and if you wish data files to be kept you will need to locate them elsewhere.

Finally I create a new menu to produce the release build

  • "/c/program files/notepad++/notepad++.exe" /c/code/run_release.bat
  • Paste in the following code, then save and close notepad++
:##BATCH to run fron notepad++
c:
cd\
cd %1
make -e CFG=RELEASE
pause
cd RELEASE
main.exe
pause
  • Load Notepad++
  • Press F5
  • enter c:\code\run_release.bat $(CURRENT_DIRECTORY)
  • Save the command as RUN RELEASE CODE

Clean cleans both RELEASE and DEBUG so there is no reason to add it here.

Rolling your own

We now have a much simpler and cleaner way to build our programs separating the source files from the created files. You now should be able to create your own projects that follow this system. Things you need to remember when building the makefile are:

  • Include the basic start section setting up the ${CFG} and ${CXX} vars
  • Make all objects dependent on ${CFG}/dir_exists.obj
  • Include instructions to build ${CFG}/dir_exists.obj
  • Make the clean section deal with the sub-dirs

If you follow these guides you can easily create your own projects following this system.

Final Comments

Make is very powerful and using just the features I have described you can produce complex build processes. One thing I have done before is to use make to build a program, then run it. The program I wrote produced source code which I then used make to build into my main project. This way I achieved auto-generated code being directly included in the build process.

Some people say make is complicated and time sucking. I do not think what I have explained here is too complicated especially since people here will be programmers anyway and variables etc. should be second nature. I personally like the idea of having control over the build process and manually setting up the dependencies, rather than allowing the IDE to do it. It is also useful as it exposes me to common compiler directives used in libraries I use and I can see what is going on. Of course there are other methods available and you might decide something else is better. Even if you move on to something else you might be glad that you spent this time studying make and the build process at a detailed level.

Link

You can view the make manual at http://www.gnu.org/software/make/manual/.

History

  • 04-Jul-2009 - First version

The Author

I have just signed up to twitter and I am hoping make contacts with other developers who work with C++ to help expand what I know. My username is @metcarob. I usually post when I update articles, and other development stuff I do.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

metcarob


Member

Occupation: Web Developer
Location: United Kingdom United Kingdom

Other popular C / C++ Language articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 5 Jul 2009
Editor:
Copyright 2009 by metcarob
Everything else Copyright © CodeProject, 1999-2009
Web19 | Advertise on the Code Project