views:

575

answers:

10

I am moving a C++ project from Windows to Linux and I now need to create a build/make file. I have never created a build/make file before. I also need to include Boost libraries to make it more complicated. It must also be a makefile and I need to learn how to create makefile anyway, so CMake and SCON are out. IDEs are also out because of the use of Boost and all my IDEs (Eclipse, VS, etc.) are only on windows. I must generate a makefile from scratch.

So what are the basics of creating a Linux c++ make file and how to incorporate the Boost libraries in it to have it properly link?

So far, my makefile looks like this. I think CFLAGS and LDFLAGS are compiler and optimization options, but not totally sure.

CC = g++
CFLAGS = -wall -o3 - c
LDFLAGS = -03 -mfp-rounding-mode=n

I am offering a bounty because I am still very lost. In case anyone is feeling adventurous, I need to compile the following in linux

  • simple_ls.h
  • simple_ls.cpp
  • 2dquicksort.h
  • rawr.h
  • rawr.cpp
  • converter.cpp

The headers in simple_ls.h:

#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/lexical_cast.hpp"

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

The headers in 2dquicksort.h:

#include <stdio.h>
#include <ctype.h>
#include <iostream>

The headers in rawr.h:

#include <iostream> // not required by most systems
#include <fstream>
#include <iomanip>
#include <cstdlib> // or (stdlib.h) for exit()

#include <cmath> 
#include <vector>
#include <limits>
#include <string>

An acceptable answer would be a step-by-step explanation of how makefiles work and how to build them with Boost in Linux without an IDE.

+18  A: 

A very simple GNU makefile follows:

CPPFLAGS += -Isome_include_path
CXXFLAGS += -O3
LDFLAGS  += -Lsome_link_path -lsome_lib -lboost_filesystem

all: binary_name
binary_name: foo.o bar.o john.o
  • CPPFLAGS are flags that are applied to the C Preprocessor. Things like include paths.
  • CXXFLAGS are flags that are applied to the C++ compiler. Things like optimization levels.
  • LDFLAGS are flags that applied to the linker. Things like external libraries (boost_filesystem) and other libraries. Also, the paths to those libraries.
  • Convention says that there should be a make all rule, which is the default. In make, the first rule is the default.
  • binary_name is the name of your binary.
  • binary_name depends on 3 files: foo.o, bar.o, john.o.
  • We don't include rules for *.o because gnu make has an implicit rule for those.

To use make, you would create a file named Makefile with the contents listed above. To build, you would run make in that directory.

As an aside (and as others have mentioned), I would recommend moving away from make if possible. There are better systems out there. The main benefit of make is that it's everywhere. However, if management requires it, then management requires it.

(Note that the GNU Make += notation is not always portable to other versions of Make. However, make on Linux is GNU Make.)


Given your edit, here's an example with the files that you note. The one caution is that the line beginning with $(CXX) should begin with a TAB character!

LDFLAGS  := -lboost_filesystem
CXXFLAGS := -O3 -Wall
CPPFLAGS :=

all: program
program: simple_ls.o converter.o rawr.o
    $(CXX) -o $< $^ $(LDFLAGS)

simple_ls.o: simple_ls.cpp rawr.h simple_ls.h 2dquicksort.h
converter.o: converter.cpp rawr.h simple_ls.h 2dquicksort.h
rawr.o: rawr.cpp rawr.h simple_ls.h 2dquicksort.h
sharth
What do those fields mean?
Elpezmuerto
This is good in terms of explaining what the things are but not how to implement it
Elpezmuerto
+7  A: 

I would not recommend writing your own Makefiles. Instead, use a build system like CMake or SCons.

It is worth noting that those tools are cross-platform. So you can use the same build system on Linux and Windows.

Job
premake isn't bad either
ergosys
bakefile is a possibility too (http://www.bakefile.org/)
+4  A: 

Of course you should Read The Fine Manual (specifically gcc and make). However, here are the basics for gcc:

To compile a source file:

g++ -c file.cpp

This will create file.o. Then you'll probably want to link them:

g++ -o app_name file.o main.o other_file.o

This will create an executable called *app_name*. But since you are using boost and might have header files located all over the place, you'll probably need additional options. Use -I during the compilation to add a directory to the include path:

g++ -I/usr/local/more_includes/ -c file.cpp

You'll probably also need to link to some libraries. During linking:

g++ -L/usr/local/more_libraries/ file.o main.o other_file.o -lsome_library

Now onto makefiles. The basics of a makefile are:

target: dependencies
    command

For example:

my_app: file.o
    g++ -o my_app file.o

file.o: file.cpp file.h
    g++ -o file.cpp

clean:
    rm file.o my_app

If you type 'make' it will be default try to create the first target. my_app depends on the target file.o, so it will check to see if file.o has been modified since the last time my_app has been modified. If so, it will relink. In checking file.o, it notices that file.o depends on file.cpp and file.h. If either of those files have been modified since the last time file.o was created, it will recompile that file.

Targets don't always have to be actual files either. The last one is called clean, it doesn't depend on anything and just deletes file.o and my_app. If you type 'make clean' it will run the command.

There are of course a ton of other options, but that should get you started.

Niki Yoshiuchi
Its worth noticing, that 'command' line must start with separator and it must be tab (all versions of 'make' ? Not sure). Causes lot of headaches "Makefile:4: *** missing separator. Stop." is most common. Easy to omit if you are coping examples from webpage.
greg
Good call! I didn't even think of that since I use tabs for indenting in my editor and it's all automagic for me.
Niki Yoshiuchi
+2  A: 

It's not exactly what you were requesting but I would highly recommend that you use a build system called premake. The benefit, and difference, between premake and scons and cmake is that it generates makefiles for you. This means that you can use premake as a start and then look at the files it generates to learn more.

Aside from this premake is an easy to learn and intuitive tool and it has the added benefit of being able to generate visual studio project files from the same configuration.

My advice would be to use premake if you absolutely have to have makefiles, it will help you learn and is ultimately a flexible replacement for directly writing your own makefiles (which is a major pain is the %*$).

radman
CMake also generates Makefiles, VisualStudio/Eclipse/Xcode project files.
MKroehnert
+1  A: 

Using @Job 's idea, I recommend you to leave it for some IDE to do. For example, Build a project in Eclipse CDT and use it's auto-generated make file. Then, you can kearn it and by the time change it to fit exactly your needs.

rursw1
That can't work, because I only have Eclipse on my Windows machine
Elpezmuerto
@Elpezmuerto, so install Eclipse CDT also on your Linux machine...
rursw1
@Rursw1, thats not an option
Elpezmuerto
+2  A: 

There is a very good tutorial @ ALP.

Prabhu Jayaraman
+1  A: 

First off, I am not an expert on Makefiles. I know the basics, but the Makefile in this answer will almost certainly have room for improvement. There are a lot of tricks and shortcuts that will allow you to handle dependencies and adding files better than hardcoding everything. However I think this example will be sufficient for your purposes and will possibly be more educational.

Second, I want to make sure you know the basic stages of building a project. You may already know this, but if you don't the following will be a bit confusing at first. There are essentially two steps: compiling and linking. Compiling converts your code to object code - it will convert a .cpp file to a .o file if successful. The next step is linking. This is where it sticks all the object code together to create an executable and links the function calls from one file to another (so if file1.cpp calls a function defined in file2.cpp, it's at this step that the file1.cpp figures out where the function actually is). There is more to it but that should be enough to make the following clear.

At this point you can just use g++ to compile and link your project (you can even do it in "one" step). This is quite cumbersome however, especially for any non-trivial project. It also makes it difficult to track files that have changed since you last compiled.

This is where Makefiles come in. A Makefile is a list of rules in the form:

target: dependencies
    command

(Make sure to use tabs not spaces, make probably won't work if you use spaces). If you run the command:

make some_target

Then make will look for the rule with some_target. If the target is a file, it will check the 'last modified' time stamp of the file, and it will check the time stamps of all the dependencies you have listed. If any of the dependencies have a later time stamp, it will run the command.

I'm going to have to make some assumptions about your project (namely, which files depend on which files) so you will probably have to modify the following, but here is a basic Makefile for your project (and remember, tabs not spaces. If you copy and paste this it won't work):

CC = g++
INCLUDE_DIRS = -I/path/to/boost

all: binary_file

clean:
    rm *.o
    rm binary_file

binary_file: simple_ls.o rawr.o converter.o
    $(CC) -o binary_file simple_ls.o rawr.o converter.o

rawr.o: rawr.h rawr.cpp 2dquicksort.h
    $(CC) -c rawr.cpp $(INCLUDE_DIRS)

simple_ls.o: simple_ls.h simple_ls.cpp 2dquicksort.h
    $(CC) -c simple_ls.cpp $(INC*emphasized text*LUDE_DIRS)

Running make or make all or make binary_file will cause all your files to be compiled if necessary and then linked to create an executable called binary_file. There are some improvements you can make, for example:

%.o: %.cpp %.h 2dquicksort.h
    $(CC) -c $<

Which will find all the .cpp files and compile them in to .o files. The .o files will be dependent on a .cpp file and a .h file of the same name (and 2dquicksort.h).

Niki Yoshiuchi
+3  A: 

At least try to read through the official documentation of the product you're trying to use: here. It does explain nearly all of the basics.

In particular, read chapters 2 and 3, those will get you 99% of the way to where you need to be to use gmake effectively. In addition, carefully read the Catalogue of Implicit Rules. This will tell you what most of those "special variables" are.

One hint I'd give you is to try out gcc -M *.cpp in your project directory. This will output a list of prerequisite headers for each of your .cpp files in Makefile format. In fact, for a starter makefile, you can just do:

gcc -M *.cpp > Makefile

Edit this file, more or less prepending sharth's answer to it, and you have a workable Makefile. I would probably advise you remove the large number of system headers that gcc -M is going to add to each build rule, but you don't really have to.

FWIW, if you start working on a large project (more than one source directory is a good clue), it's time to break out a modern build management tool (cmake fan here). But for small projects, raw make is pretty easy to use.

jkerian
gcc -MM limits the list of headers in the generated build rules to only non-system headers.
AFoglia
+8  A: 

What is a Makefile ? (applied to a Boost project)

The root recursive idea behind Makefile is:

To build a target we need prerequisites (other targets!) and instructions to build

Prerequisites

They are either files, folders or fake targets (usually in .PHONY). Files/folders are tested for existence and modification date.

The target needs to be rebuilt if it has no prerequisite or if older that any of the prerequisites.

Instruction

An Instruction is shell commands, starting with one tab. Each instruction line is one shell instance. The shell command can be continued on next line when the current one ends with backslash \.

Target definition

A target is either a dependency or a rule.

Dependency:

target : prerequisite1 prerequisite2 prerequisiteN

Rule:

target : prerequisite1 prerequisite2 prerequisiteN
    instructions1
    @hidden_batch1 ; \
  hidden_batch2  

With tabs in front of instruction start.

Debug

Debugging a Makefile can become a real headache. Try the following in your Makefile to show traces (with file and line location for warning):

$(info Shell: $(SHELL))
$(warning CXX: $(CXX))

This is helpful when your Makefile contains lots of nested if/else/endif and you're not sure anymore what is the current path.

Makefile Structure

The ideal makefile structure is:

  1. variable setup
  2. target/dependency declarations

The real target-instructions processing starts once the whole Makefile and its include files has been understood (stored in make internal database).

Example

Finally, apply theory to this specific example using Boost and create fake source files to illustrate.

rawr.cpp

#include "rawr.h"

simple_ls.cpp

#include "rawr.h"

converter.cpp

#include <iostream>

#include "rawr.h"
#include "simple_ls.h"
#include "2dquicksort.h"

#include <boost/array.hpp>   // Boost! 

int main(int argc, char **argv)
{
    boost::array<int,4> a = { { 1, 2, 3, 4} };
    std::cout << a[1] << std::endl;
    return 0;
}

Makefile

Don't forget to replace spaces with real Tabs if you copy Makefile source from stackoverflow :

sed -i~ -e 's/^    /\t/' Makefile

Makefile source:

## Makefile for C++ project using Boost
#
# @author Cedric "levif" Le Dillau
#
# Some notes:
# - Using ':=' instead of '=' assign the value at Makefile parsing time,
#   others are evaluated at usage time. This discards
# - Use ':set list' in Vi/Vim to show tabs (Ctrl-v-i force tab insertion)
#

# List to '.PHONY' all fake targets, those that are neither files nor folders.
# "all" and "clean" are good candidates.
.PHONY: all, clean

# Define the final program name
PROGNAME := converter

# Pre-processor flags to be used for includes (-I) and defines (-D) 
CPPFLAGS := -DUSE_BOOST

# CFLAGS is used for C compilation options.
CFLAGS := -Wall -O0

# CXXFLAGS is used for C++ compilation options.
CXXFLAGS += -Wall -O0

# LDFLAGS is used for linker (-g enables debug symbols)
LDFLAGS  += -g

# Which Boost modules to use (all)
BOOST_MODULES = \
  date_time     \
  filesystem    \
  graph         \
  iostreams     \
  math_c99      \
  system        \
  serialization \
  regex

# Boost libraries' type (a suffix)
BOOST_MODULES_TYPE := -mt

# Define library names with their type
BOOST_MODULES_LIBS := $(addsuffix $(BOOT_MODULES_TYPE),$(BOOST_MODULES))

# Define the linker argument to use the Boost libraries.
BOOST_LDFLAGS := $(addprefix -lboost_,$(BOOST_MODULES_LIBS))

# Feed compiler/linker flags with Boost's
CPPFLAGS += $(BOOST_CPPFLAGS)
LDFLAGS += $(BOOST_LDFLAGS)

# List the project' sources to compile or let the Makefile recognize
# them for you using 'wildcard' function.
#
#SOURCES = simple_ls.cpp rawr.cpp converter.cpp
SOURCES = $(wildcard *.cpp)

# List the project' headers or let the Makefile recognize
# them for you using 'wildcard' function.
#
#HEADERS = simple_ls.h 2dquicksort.h rawr.h
HEADERS = $(wildcard %.h)

# Construct the list of object files based on source files using
# simple extension substitution.
OBJECTS = $(SOURCES:%.cpp=%.o)

#
# Now declare the dependencies rules and targets
#
# Starting with 'all' make it  becomes the default target when none 
# is specified on 'make' command line.
all : $(PROGNAME)

# Declare that the final program depends on all objects and the Makfile
$(PROGNAME) : $(OBJECTS) Makefile
    $(CXX) -o $@ $(LDFLAGS) $(OBJECTS)

# Now the choice of using implicit rules or not (my choice)...
#
# Choice 1: use implicit rules and then we only need to add some dependencies
#           to each object.
#
## Tells make that each object file depends on all headers and this Makefile.
#$(OBJECTS) : $(HEADERS) Makefile
#
# Choice 2: don't use implicit rules and specify our will
%.o: %.cpp $(HEADERS) Makefile
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $<

# Simple clean-up target
# notes:
# - the '@' before 'echo' informs make to hide command invocation.
# - the '-' before 'rm' command to informs make to ignore errors.
clean :
    @echo "Clean."
    -rm -f *.o $(PROGNAME)

File list

2dquicksort.h
converter.cpp
Makefile
rawr.cpp
rawr.h
simple_ls.cpp
simple_ls.h

Compilation

make clean all
Clean.
rm -f *.o converter
g++ -Wall -O0 -DUSE_BOOST  -c -o converter.o converter.cpp
g++ -Wall -O0 -DUSE_BOOST  -c -o rawr.o rawr.cpp
g++ -Wall -O0 -DUSE_BOOST  -c -o simple_ls.o simple_ls.cpp
g++ -o converter -g -lboost_date_time -lboost_filesystem -lboost_graph -lboost_iostreams -lboost_math_c99 -lboost_system -lboost_serialization -lboost_regex converter.o rawr.o simple_ls.o

Result

And now, the result of nearly the tiniest Boost program:

./converter
2

No excuse not to use it ! Boost is really a featured C++ toolbox :)

levif
you man, need a blog!
Nazgob
+1  A: 

I know this is probably not the stackoverflow spirit but damned. RTFM ! :
http://www.gnu.org/software/make/manual/make.html#Introduction

Ugo