Sunday, September 9, 2012

A Makefile practical example


It's not uncommon in a project to have a standard rule for compiling files (perhaps even using the built-in GNU Make rule) and to need to have a slightly different version of that rule for a specific file, or set of files, that otherwise use the same command.

For example, here's a Makefile that builds all the C files in two subdirectories (lib1 and lib2) using a pattern rule.

lib1_SRCS := $(wildcard lib1/*.c)
lib2_SRCS := $(wildcard lib2/*.c)

lib1_OBJS := $(lib1_SRCS:.c=.o)
lib2_OBJS := $(lib2_SRCS:.c=.o)

.PHONY: all
all: $(lib1_OBJS) $(lib2_OBJS)

%.o: %.c
   @$(COMPILE.C) -o $@ $<

First the Makefile gets the list of all C files in lib1 in the variable lib1_SRCS and the C files in lib2 in lib2_SRCS and then it converts those to a list of object files to build using a substitution reference to change .c to .o creating lib1_OBJS and lib2_OBJS.

The pattern rule uses the GNU Make built-in variable COMPILE.C to run a compiler that compiles a .c into a .o.   The Makefile knows to build all the objects in lib1_OBJS and lib2_OBJS because they are prerequisites of all.

This works fine if all the .c files have the same compilation options.

Now suppose that the C file lib1/special.c requires the -Wcomment option to prevent the compiler from warning about an oddly written comment.   Obviously is would be possible to change the value of CPPFLAGS globally by adding

CPPFLAGS += -Wcomment

in the Makefile.  But that change affects every compilation and is probably undesirable.

Luckily, a target-specific variable can be used to just alter the value of CPPFLAGS for that single file.  By writing

lib1/special.o: CPPFLAGS += -Wcomment

the value of CPPFLAGS will be altered just for the creation of lib1/special.o.

Now suppose that an entire subdirectory requires a special CPPFLAGS option to maximize optimization for speed (the -fast option to gcc, for example).

Here a pattern-specific macro definition is ideal.  Adding

lib1/%.o: CPPFLAGS += -fast

does the trick.  Any .o files that are built in lib1/ will have the -fast command-line option.

Conclusion

Target-specific and pattern-specific macros are worth reading about because they are a very handy feature of GNU Make when a small change is needed for a limited set of targets.

Further reading can be found in the GNU Make manual sections 'Target-specific Variable Values' (http://www.gnu.org/software/make/manual/make.html#Target_002dspecific) and 'Pattern-specific Variable Values' (http://www.gnu.org/software/make/manual/make.html#Pattern_002dspecific).

Makefiles: pattern-specific example


Pattern-specific macros work in a similar manner to target-specific macros, but instead of being defined for a target they are defined for a pattern and then will be applied to all targets that match that pattern.

The below example shows a pattern-specific macro.  The last line says that for any target that begins with f followed by anything (that's the % wildcard) VAR has the value starts with f.

.PHONY: all foo bar baz

VAR = global scope

all: foo bar
   @echo In $@ VAR is $(VAR)

foo:
   @echo In $@ VAR is $(VAR)

bar: VAR = local scope
bar: baz
   @echo In $@ VAR is $(VAR)

baz:
   @echo In $@ VAR is $(VAR)

f%: VAR = starts with f

Now if you run make you get the following output:

In foo VAR is starts with f
In baz VAR is local scope
In bar VAR is local scope
In all VAR is global scope

This is the same as above except that in the rule for foo the value of VAR has been set to starts with f by the pattern-specific definition.

It's worth noting that this is unrelated to normal GNU Make pattern rules.  The pattern-specific macro definition can be used to change the value of a macro in a normal rule (as above).  It can also be used with a pattern rule.

For example, you could change the value of DEBUG to 1 within all %.o files (that could be built using the standard %.o: %.c pattern rule) by writing:

%.o: DEBUG = 1

Makefile: target-specific example


As always, let's start with a simple example that illustrates the difference between global and local scope in GNU Make.  The following Makefile has four targets: all, foo, bar, baz.  All four targets are phony; since I'm only interested in illustrating global and local scope I've ignored actually building any files.

all requires that foo and bar be built and bar needs baz.  The commands for each target all do the same thing.  They print the value of macro VAR using a shell echo.

.PHONY: all foo bar baz

VAR = global scope

all: foo bar
    @echo In $@ VAR is $(VAR)

foo:
    @echo In $@ VAR is $(VAR)

bar: VAR = local scope
bar: baz
    @echo In $@ VAR is $(VAR)

baz:
    @echo In $@ VAR is $(VAR)

VAR is initially defined to have the value global scope.  That's the value VAR will have anywhere in the Makefile; unless, of course, that value is overriden using a target- or pattern-specific macro.

So illustrate local scope I've redefined VAR to local scope for the rule that creates bar.

A target-specific macro definition is exactly like a normal macro definition (it uses the same =, :=, += and ?= operators) but it is preceded by the name of the target (and its colon) for which the macro should be defined.

In the Makefile above VAR is set to local scope for the bar rule by writing:

bar: VAR = local scope

If you run make on this Makefile you'll get the following output:

In foo VAR is global scope
In baz VAR is local scope
In bar VAR is local scope
In all VAR is global scope

You can clearly see that GNU Make is following its standard depth-first, left-to-right search pattern building foo (it's the first prerequisite of all), then baz (which is a prerequisite of bar which is the second prerequisite of all), then bar and finally all.

And sure enough within the rule for bar the value of VAR is local scope.  And, as you expect since there's no local definition of VAR for all and foo, VAR has the value global scope in those rules.

But what about baz.  The Makefile output shows that in baz the value of VAR is local scope, yet there was no explicit target-specific definition of VAR for baz.

baz has the same locally scoped macros as bar because baz is a prerequisite of bar.  In fact, target-specific variables are defined for a target and all prerequisites of that target and all their prerequisites and so on.    A target-specific variable's scope is the entire tree of targets starting from the target for which the variable was defined.

Saturday, September 8, 2012

Makefiles - Compile all .cpp files object .o files


Suppose I have project directory looks like this:

/project
    Makefile
    main
    /src
        main.cpp
        foo.cpp
        foo.h
        bar.cpp
        bar.h
    /obj
        main.o
        foo.o
        bar.o

What I would like my makefile to do would be to compile all .cpp files in the /src folder to .o files in the /obj folder, then link all the .o files in /obj into the output binary in the root folder /project.

So, the makefile part will be as easy as below:

Makefile

CPP_FILES := $(wildcard src/*.cpp)
OBJ_FILES := $(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o)))
LD_FLAGS := ...
CC_FLAGS := ...

main.exe: $(OBJ_FILES)
   g++ $(LD_FLAGS) -o $@ $^

obj/%.o: src/%cpp
   g++ $(CC_FLAGS) -c -o $@ $<

Sunday, August 26, 2012

Developing C++ shared libraries with Eclipse CDT

Con­sider the fol­low­ing not so uncom­mon sce­nario: you have a shared library project in Eclipse and an exe­cutable project which uses the shared library.

I found that you have to do some addi­tional things for build­ing, run­ning and debug­ging the exe­cutable project in Eclipse (note that I’m doing this on Linux using CDT 4.0.3):

Define your shared library’s project as a ref­er­ence for your appli­ca­tion. In order to do so open the project prop­er­ties of the exe­cutable project, go to project ref­er­ences and select the shared library project (where in my case “shared” is also the name of the shared library).

In the project set­tings of your exe­cutable project, add the shared library to the linker set­tings, as below:

  1. Select the project name and right click, then choose property,
  2. Select C/C++ Build > C/C++ Linker,
  3. Add the library name of your shared library,
  4. Specify the shared library path "${workspace_loc:/SharedLib/Debug}"

Reference:

http://dirkraffel.com/2008/06/27/developing-shared-libraries-with-eclipse-cdt/

Saturday, August 25, 2012

CppUnit with Eclipse CDT Tutorial

About Eclipse CDT

Eclipse C/C++ Development Toolkit (CDT) is an extension to the Eclipse platform in the form of a plug-in. This plug-in is available for download for all platforms. The open source nature of the plug-in with its user-friendliness makes it more popular not just among the Linux developers but also among C++ developers on other platforms. CDT and the Web Tools plug-ins are the two most popular Eclipse plug-ins. Nearly two out of three developers using CDT are Windows users.

CDT has subcomponents or plug-ins that are independent projects in the CDT community. The most important is the CDT primary plug-in, which provides the core CDT capabilities. CDT Debug UI provides the UI capabilities for the debug editors and views. CDT UI plug-in provides the UI-related features, views, editors, wizards, etc. CDT Debug provides core debugging capabilities. CDT Feature provides CDT Feature component. CDT core presents Core Model, CDOM, and other core components. CDT Launch provides launch mechanism for launching external executables and tools. CDT Debug MI is the application connector for MI-compatible debuggers.

CDT editors have several features that make them popular. For example, syntax highlighting and code assist make software development quick and easy. Syntax highlighting is configurable and can be personalized to your individual taste. Code assist is the code completion feature that is similar to the one in Visual Studio. Custom-defined code templates can be added to the plug-in, which can be used by code assist.
In the following sections, we will learn how to use CDT effectively to use for testing project with CppUnit.

Installing Eclipse CDT

Download http://eclipse.org/downloads/ - i.e. eclipse-cpp-XXX-linux-gtk-x86_64.tar.gz 
Eclipse also requires the Java installation.

1. Download the Cpp``Unit distribution file Cpp``Unit-1.10.2.tar.gz and unpack it in a directory of your choice. From now on we will refer to this directory as $Cpp``Unit .
2. Run MSys to open a command line shell, then cd to the directory where you extracted Cpp``Unit and do:
./configure

There's no need to go any further than this with make, make install, etc. All we will need is the header $Cpp``Unit/include/Cpp``Unit/config-auto.h which has been generated in the step above.
Go to Eclipse and create a Managed Make C++ Project to generate a library (either static or shared).
Import all .h and .cpp files in $Cpp``Unit/src/Cpp``Unit to the project.
Open the project properties configuration dialog (Alt + Enter) and add $Cpp``Unit/include to the include path.


Thursday, August 23, 2012

Creating a local yum repository in CentOS


Sometimes it can be handy to set up your own repository to prevent from downloading the remote repository over and over again. This tutorial shows how to create a CentOS mirror for your local network. If you have to install multiple systems in your local network then all needed packages can be downloaded over the fast LAN connection, thus saving your internet bandwidth.

Create the Directories

mkdir -pv /var/www/html/centos/VER/{os,updates}/i386

Replacing VER and i386 with your major version and architecture.
Additionally you'll need some deeper directories. This is the correct location to copy the CD/DVD rpms.

Quote:
mkdir -pv /var/www/html/centos/VER/os/i386/CentOS/RPMS/

The Base Repository
Copy the RPMs from the CDs/DVD to /var/www/html/centos/base.

Create the base repository headers:

createrepo /var/www/html/centos/VER/os/i386/

The Updates Repository

Select an rsync mirror for updates: check out this list of aviable mirrors: Centos OS Mirror list and these are identified with rsync.

For example: rsync://ftp.belnet.be/packages/centos/

The mirrors share a common structure for updates. Simply append /updates/<d version>/<base arch>.

Rsync to create the updates-released repository:

/usr/bin/rsync -avrt rsync://ftp.riken.jp/centos/4/updates/i386 --exclude=debug/ /var/www/html/centos/4/updates/

This will create a complete update repository at /var/www/html/centos/4/updates/i386. The repodata directory will be created with all of the headers.

You can additionally pipe this into mail to receive an email when updates are available.

/usr/bin/rsync -avrt rsync://ftp.riken.jp/centos/4/updates/i386 --exclude=debug /var/www/html/centos/4/updates/ | /bin/mail you@example.com -s "New Repo Updates"

Next I would advise to setup a cron job to run the rsync (above). In this manner your repository is kept updated and only new updates and headers will be downloaded to your repository.

Client Site Configuration

Yum Configuration

Edit yum.conf:
vi /etc/yum.repos.d/CentOS-Base.repo

http://localhost/res/centos/

[base]
name=CentOS-$releasever - Base
baseurl=http://192.168.*.*/centos/$releasever/os/$basearch/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-5
#released updates
[update]
name=CentOS-$releasever - Updates
baseurl=http://192.168.*.*/centos/$releasever/updates/$basearch/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-5