In this article, I want to discuss how to create a cross-platform C++ project from your Mac or Linux Box using Autotools.

Wait, this is not about iOS?

Well, not necessarily. However, a good understanding of C and C++ will come in really handy for you if you develop iOS or macOS applications.

Even though Apple is trying to remove any C or C++ resemblance from Swift, there’s still a lot of libraries (like the Security framework and the crypto library) heavily relying on it.

Thus, learning and being proficient in C and C++ can make you a better, more valuable iOS and macOS developer. Plus, it can help you find other interesting job opportunities too.


C++ is one of the most professionally used languages out there, from big CRM and ERP software vendors to the Bitcoin core client. It’s everywhere.

However, it’s actually not that easy to write a portable, cross-platform C++ project. Different compilers from different systems, and the presence of a myriad of different libraries, make it really difficult.

Some initiatives exist out there trying to help setting a compiling and linking environment that works on most systems. Some of them include GNU’s Autotools and CMake.

Nevertheless, and perhaps ironically, these platforms are really hard to understand and master. As a result, it’s sometimes easier for developers to generate their own Makefile structure than understanding and using them.

In this post, I want to help you getting started by creating a very simple Hello World project including a binary and a dynamic library that can be ported to any GNU-compliant environment to be compiled and installed.

As always, you can download the code from my Github repository.

Project structure

Our project will build a very simple “hello world” program that will just show a message on screen. It contains a shared library:, and a main program that calls this shared library.

Additionally, there will be some extra scripts (to be installed but not compiled), a man page for the main application, some documentation, examples and sample data.

The project should be structured in the following way:

  • sources in src/
  • documentation in doc/
  • man pages in man/
  • some scripts in scripts/ (in this directory, we will add files that need to be executed but not compiled)
  • examples in examples/

Our src/ directory will contain just three source files:


#include "helloWorld.h"
int main() {
   return 0;


#include <iostream>
#pragma once
namespace helloWorld {
   void printHelloWorldMessage();


#include "helloWorld.h"
namespace helloWorld {
   void printHelloWorldMessage() {
      std::cout << "Hello world, helloWorld() called in the helloWorld namespace." << std::endl;

Test It First

The first thing you should do is download the code from the Github repository to a local folder.

Then, get into the directory in a terminal, and invoke the script:

$ ./

This will generate the required links to configure and build the project, and create the Makefile structure.

Now it’s time to configure the project. Run the local configure script:

$ ./configure

The configure script will check that the environment is correct. Given that you have Xcode installed on your Mac, or the build-essentials package on most Linux distributions, you should have no problem here.

Next, time to build our project, calling make:

$ make

This will compile all the source files and generate the required dynamic libraries.

Finally, time to install and run the hello world sample app.

$ make install
$ helloWorld
Hello world, helloWorld() called in the helloWorld namespace.

You can uninstall it anytime by running make uninstall.

The project includes some cool stuff, like an associated man page and some documentation and dummy scripts:

$ man helloWorld
... (displays helloWorld app man page) ...

Hello world!

Goodbye world!

Thus, now that you know what we want to achieve, it’s time to start.


Depending on our operating system, we need to follow a different process for installing autotools.

On linux based systems (i.e: Debian), all you need to do is installing the required packages (as root):

# aptitude install autotools-dev autoconf automake libtool

On Mac OS X, we can use *brew* for this:

$ brew install autoconf automake libtool

In order to install Brew on Mac OS X, you just need to run this command:

$ /usr/bin/ruby -e "$(curl -fsSL"

More detailed instructions and information on Brew can be found here.

Generating The Project Configuration

In this step, we will generate the required configuration files and the files for the different parts of our project.

These files will set the rules to indicate autoconf and automake which files should be included and how to process them.

Run autoscan:

$ autoscan

Autoscan tries to produce a suitable file (autoconf’s driver) by performing simple analyses on the files in the package. This is enough for the moment (many people are just happy with it as permanent).

Use AutoScan file as initial Configuration file

Autoscan actually **produces a configure.scan file**, so let it have the name autoconf will look for:

$ mv configure.scan

Modify the Configuration file

Now we need to adjust the generated configuration file to add some info, like the name and version of our package:

$ vim

We will modify this initial file to adapt it to our project.

First, we will modify the *AC_INIT* property that defines the package name, version and bug reporting email address. Replace:



AC_INIT([helloWorld], [1.0], [])

Also, we will change the *AC_CONFIG_SRCDIR* to point to our main.cpp file. Replace:




Additionally, we will add some properties that will create the link between autoconf and automake. One of them will be the *AM_INIT_AUTOMAKE* variable, where you will specify the package name and version again. Also, *AC_OUTPUT*, where we will specify the Makefile files to be generated by autoconf in all of our directories.

Last, we will add some checks to make sure that the system where our project is going to be built has a proper compiling environment for C++ and can link shared libraries.

The final file should look like this:

# Process this file with autoconf to produce a configure script.
AC_INIT([helloWorld], [1.0], [])

# Checks for programs.

# Checks for libraries.

# Checks for typedefs, structures, and compiler characteristics.

# Output Makefile files.
AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile examples/Makefile man/Makefile scripts/Makefile])

Create the root

Now it’s time to create the root, the seed for our root Makefile. It will specify the directories where automake should look for Makefile files.

$ vim

Add this content:

AM_CXXFLAGS = -fPIC -Wall -Wextra
SUBDIRS = src doc examples man scripts

Apart from specifying the subfolders containing Makefiles, we are adding some commonly used settings for most projects.

Create the in the src/ directory

The src directory is where we keep all our source files (.cpp and .h). We want our application to be called helloWorld. Let’s see how the generator for our source dir looks like.

$ vim src/

Add this content:

AUTOMAKE_OPTIONS = subdir-objects

bin_PROGRAMS = helloWorld

helloWorld_SOURCES = main.cpp
helloWorld_LDADD =

libHelloWorld_la_LDFLAGS = -version-info 0:0:0
libHelloWorld_la_SOURCES = helloWorld.cpp helloWorld.h

As you can see, after defining some global options, we set the initial binary for the project, *helloWorld*, with the property *bin_PROGRAMS*.

Next, we define the sources and the libraries that should be included in this *helloWorld* object, with the *_SOURCES* and *_LDADD* properties respectively. Note how we use the extension .la instead of .so for the shared library. This is a convention.

Then, we define our small library (*lib_LTLIBRARIES*), set some flags (*libHelloWorld_la_LDFLAGS*) and specify its source files. That’s all we need. If you were to add additional source files, you will add them to *helloWorld_SOURCES*, and repeat the same process for libHelloWorld in order to add more libraries.

Create the in the man/ directory

Next, we’ll add the man page for our helloWorld executable.

We will create a very simple man page with this content:

.\" Manpage for helloWorld.
.\" Contact for comments or help.
.TH man 1 "30 Nov 2017" "1.0" "helloWorld man page"
helloWorld \- simple hello world example application
helloWorld is a simple test application to show how to build a C++ example project with Autotools for multiplatform compilation and use.
helloWorld does not take any arguments.
No known bugs.
Ignacio Nieto Carvajal (

Write that down in a file called helloWorld.1 (The man page number 1 is a convention, because our App is considered a “User command”).

Now it’s time to write our man/

$ vim man/

Add this content:

man_MANS = helloWorld.1

Create the in the scripts/ directory

Next, we will create a makefile.a, for our scripts directory.

$ vim scripts/

Add this content


These scripts are just dummy scripts we will be using for this sample project. Thus, we will create two files,…

echo "Hello World!"


echo "Goodbye world!"

Create the in the examples/ directory

In the examples directory, we can add configuration sample files, data examples, or any other kind of resource we might want to add.

In this example, we will add a dummy sample configuration file called

version = 1.0
author = Ignacio Nieto Carvajal <>

Then, we will generate the

$ vim examples/

With this content:

exampledir = $(datarootdir)/doc/@PACKAGE@
example_DATA =

Indeed, we can add as many files as we want (separated by whitespaces) in *example_DATA*.

Create the in the doc/ directory

Finally, in order to set a doc directory, we need to specify a variable that will depend on the name of the project specified in, `@PACKAGE@`.

Then, we add a doc_DATA variable with the files we want to include. We will use two dummy files: and changelog.

$ vim doc/

Add this content to the file:

docdir = $(datadir)/doc/@PACKAGE@
doc_DATA = changelog

That’s all! We have finished generating all configuration files. Now, let’s create a script to generate the proper configuration and make scripts.

Integrating Autoconf and Automake

Last, we need to build our configure file. This file, called, will be called in order to setup all configuration and makefile scripts needed to configure and build the project.

In order to do that, we need to follow a series of steps. First, we need to call libtoolize to generate the linking helper script, that will assist the linked when generating the shared libraries. Unfortunately, in Mac OS X it’s called glibtoolize, whereas in linux it’s called libtoolize. As a result, we need to address both cases in our script.

Next, we need to call aclocal. This command will generate the aclocal.m4 that contains all necessary information (including macros) needed for automake.

Then, we need to run autoheader to generate the configuration headers. After that, we can call autoconf to generate the configure command and automake to go through all our directory structure and generate, for every file, a corresponding file.

Here’s how the final script looks like:


case `uname` in Darwin*) glibtoolize --copy ;;
 *) libtoolize --copy ;; esac

aclocal && autoheader && automake --add-missing && autoconf

Voila! 👏🏻 Our multiplatform configure file is generated and ready to use in any GNU-compliant environment. You went through the whole process before, but let’s see how it works.

Invoking Configure And Make

First of all, we call As we saw, this script will call libtool and autotools to generate the configure script and the Makefile structure in our project:

❤️ Enjoying this post so far?

If you find this content useful, consider showing your appreciation by buying me a coffee using the button below 👇.

Buy me a coffeeBuy me a coffee
$ ./

After that, we call the generated configuration script.

$ ./configure

It will read our file and scan all the required libraries and dependencies. If a dependency or library is missing, the proper error message will be delivered.

For every we generated previously, as specified in AC_OUTPUT, it will generate the plaform-dependent Makefile file.

Now, we just can run make to build our project:

$ make

Makes reverses all the folder structure specified in the root and call make on that directory. If everything goes well, it will generate all the libraries and executable files. Now we can install our project to be available for the entire system (as superuser).

$ sudo make install

This command will typically copy the binaries in /usr/local/bin, the libraries in /usr/local/lib, the man pages in /usr/local/man and the docs in our @PACKAGE@ defined directory: /usr/local/share/helloWorld.

You can also uninstall everything by calling *make uninstall* (as superuser). This will do the reverse process, removing the files in the installation directory.

$ sudo make uninstall

Congratulations! You have built a cross-platform C++ project 😎.


In this post I discussed how to build a cross-platorm C++ project from your Mac or Linux box using Autotools. C++ is a very interesting language to learn and master for iOS and macOS developers. However, building cross-platform projects that can be ported to different environments (like macOS, Linux, Unix, etc) can be painful. As a result, I wanted to share a sample project with detailed instructions on how to do that to easily get you started.

I hope you enjoyed this post. If you have any questions or comments, don’t hesitate to share them with us in the comments below!