How to Create a G++ Makefile

Amir Kirsh
Amir Kirsh reading time: 11 minutes
February 7, 2023

C++ is still one of the most popular programming languages across industries and disciplines, and for good reason. This means that there are several available tools for it, each with their own advantages and downsides. the GNU Compiler Collection (GCC) remains one of the most popular, however, due to its wide range of features and capabilities. With its popularity, it’s worth diving into GCC and the tools it offers specifically for C++ – and especially, its standard G++ makefiles.

What is GCC?

GCC stands for The “GNU Compiler Collection,” which includes front ends for many software languages (in the compiler domain, a front end is a component that translates a software language to a common intermediate representation from which the compiler backend can create the proper runtime artifact that fits the target architecture and operating system). It was originally written as the compiler for the GNU operating system

GCC includes front ends for:

  • C and its implementation of C standard library, AKA libc.
  • C++ and its implementation of C++ standard library, AKA libstdc++.

It also works for: Objective-C, Fortran, Ada, Go, and D, as well as libraries for these languages.

The GNU system was developed to be 100% free software in the sense that it respects the user’s freedom.

What is (lower case) gcc? 

The command gcc invokes the compiler-driver of the GNU Compiler Collection. And no, it’s not a mistake. We wrote it in lower-case letters on purpose. Why? GCC is the official name of the app, but gcc is how you call the GNU Compiler Compiler collection in your code. 

When you run the gcc command on a file, it uses the file extension to determine how to handle the file. The default file extensions recognized by gcc as C++ source code are “.cpp”, “.cc”. It also assumes a “.c” file is written in C.

For example, when you run gcc -o [output_file] [input_file.c], it will treat the input file as C code and use the C compiler (cc1) to handle the compilation process. If the input file has a .cpp or .c++ extension, it will treat it as C++ code and use the C++ compiler (cc1plus) to handle the compilation process.

Why Use G++?

Most likely, whenever you compile C++ code you’ll want a C++ compiler. You can do this directly with g++. 

When running G++ it treats your source code as if it was c++, so in this aspect, ‘G++’ behaves as ‘gcc -x c++’. It is true that gcc can detect that you are compiling a C++ file and compile it as such, but again, it is reasonable to ask explicitly for the C++ compiler if you know you are compiling a C++ project.

Once the G++ driver links a C++ program, it normally automatically links against libstdc++, the standard C++ library implementation that comes with GCC (remember the difference between GCC in capital and the gcc program in small letters, right?). So in that aspect, ‘g++’ and ‘gcc -lstdc++’ are the same (more to follow).

Unlike gcc, G++ also automatically adds -shared-libgcc whenever you build a shared library or a main executable, because C++ programs typically use exceptions, so this is the right thing to do (see more details on that here). Thus, when considering this aspect, ‘g++’ and ‘gcc -shared-libgcc’ are equivalent.

How To Install GCC on Linux/Mac/Windows and other OS

GCC will usually be part of your Linux distribution, but for any other OS, or if you want to install a different version, you can download it from the GNU website directly.

To work with GCC on Windows you’ll need to have a Linux-compatible environment, with either MinGW, Cygwin, or WSL.

What is a Makefile and what is a G++ Makefile

A makefile is a file that contains instructions for the program ‘make’ which automates the building process for source code files. Make is an old tool (first version published in 1976) that is still widely used, either directly or with other tools such as CMake (you can read more about CMake in our posts cross compile with CMake, CMake generator, CMake vs. Make, exploring the CMake debate, and modern CMake tips and tricks. You can also read about another build tool, ninja, which is not relying on make, in our post on CMake and Ninja combo).

A g++ makefile specifically is a makefile that contains instructions for compiling and linking C++ source code using the g++ compiler. The makefile typically includes dependencies between source files and the target executable, as well as any compiler flags or options that need to be passed to g++. The make program reads the makefile, interprets its instructions, and builds the target executable according to the dependencies and instructions specified in the makefile.

You can read more about make in our post on what is GNU make and how to install it.

How To Create A G++ Makefile

The main syntax rule is simple: 

targets : prerequisites

        recipe

Let’s expand on the rules a little bit.

When a target is a file, such as an object file or executable, it needs to be recompiled or relinked if any of its prerequisites change.

Recipes tell the program how to update the targets.In addition, if any prerequisites are themselves targets, then they are updated first. 

The following is an example of Makefile straight from the horse’s mouth. The

commentary is ours.

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
       cc  -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h defs.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

Creating an executable named edit from a 8 object files: main.o kbd.o command.o display.o insert.o search.o files.o utils.o

All the C files include defs.h. However,  

Only those defining editing commands include command.h

Only low level files that change the editor buffer include buffer.h.

Code
Notes

Since all C files include defs.h, if we touch  defs.h we also need to:

  1. Recompile each of the eight object files
  2. Compile and link, to generate our executable

However, if we only touch  command.h we would:

  1. Only need to recompile command.o files.o
  2. Compile and link, to generate our executable

However, if we touch a single .c file, for example utils.c then only:

  1. Only need to recompile utils.o 
  2. Compile and link, to generate our executable

A key point to remember: a good dependency hierarchy and Makefile will save compile time. 

Using Macros

In Makefile, macros are used to define variables that can store text strings or lists of text strings. These variables can then be used throughout the Makefile to simplify and reduce repetition in the commands used to build a program. Macros are defined using the = operator and are referenced using the $ operator. For example, a macro named CXX could be defined to store the path to the C++ compiler, and then used in commands throughout the Makefile like $(CXX) -o program program.cpp.

Here is an example of a Makefile that uses macros:

# Makefile for C++ program 

# Compiler and flags 

CXX = g++
CXXFLAGS = -std=c++11 -Wall -O2 
# Target and dependencies 
TARGET = myprogram 
OBJS = main.o foo.o bar.o 
# Build and run 
all: $(TARGET)
    ./$(TARGET)

$(TARGET): $(OBJS)
    $(CXX) $(CXXFLAGS) -o $@ $^

%.o: %.cpp
    $(CXX) $(CXXFLAGS) -c -o $@ $< 
# Cleanup 
clean:
    rm -f $(TARGET) $(OBJS)

This Makefile uses g++ as the compiler (CXX = g++) and sets some flags for the compiler (CXXFLAGS = -std=c++20 -Wall -O2). The TARGET variable is set to the name of the final executable file, and the OBJS variable is a list of object files that are needed to build the program.

The all target is the default target built when you run make without any arguments. It depends on the TARGET and runs the program after building it.

The $(TARGET) target depends on $(OBJS) and links them together to create the final executable. The %.o: %.cpp rule is a pattern rule for building object files from C++ source files, and the clean target is used to remove object files and the final executable.

To use this Makefile, save it in a file named “Makefile” (or “makefile” on some systems) and run make in the same directory. This will build the program and run it. To clean up object files and the final executable, run make clean.

How run make 

Simply type: make or include target names. It will see the Makefile and follow through.

Tips when creating a g++ makefile

Use macros such as to hold compiler version

In the above example, $(CXX) was expanded/replaced by g++. Using such a variable lets you control which compiler will be used. We recommend using more definitions to set compilation flags and linkage flags, and support your project structure by setting variables that contain directories to hold: source files, object files and binary files.

Aim to compile only the necessary 

Makefile or build systems like CMake can be used to manage the compilation process. They can help identify the dependencies between the source files and only recompile the necessary files when a change is made.

How To Speed Up Your G++ Makefile To Save Time + Resources

Make sure you have a good dependency hierarchy and your makefile reflects it. Make sure you compile the minimum necessary whenever the code is changed. In this matter, it is interesting to mention the C++ modules should be a game changer here. C++ modules is a feature that allows for faster compilation times by precompiling and caching the interfaces of modules. Instead of re-compiling and linking the entire codebase every time a small change is made, only the changed module and modules that depend on it need to be recompiled. This can significantly reduce build times, especially in large codebases with many dependencies. Additionally, C++ modules can help reduce the amount of code that needs to be recompiled by resolving dependencies at compile time and enabling more fine-grained control over what parts of a codebase need to be rebuilt. 

Frequently Asked Questions

1. What Is GNU

GNU (pronounced “guh-noo”) is a recursive acronym for “GNU’s Not Unix!” 

It is a widely used, free and open-source operating system and software development environment. GNU was created in 1983 by Richard Stallman as a response to the proprietary nature of the Unix operating system, which was widely used at the time. The goal of the GNU project was to create a free, open-source version of Unix that would be available to everyone.

The GNU project has developed a wide range of software tools and utilities, including the GCC (GNU Compiler Collection) which includes compilers for C, C++, Objective-C, Fortran, Ada, and other languages. The most well-known GNU project is the GNU/Linux operating system, which is a Unix-like operating system that is based on the Linux kernel and the GNU userland utilities.

GNU’s philosophy is based on the four fundamental software freedoms: the freedom to run the program as you wish, the freedom to study and modify the program, the freedom to distribute copies, freedom to distribute copies of your modified versions.

See more at: https://www.gnu.org/

2. Is it possible to compile C++ without make?

Yes, it is possible to compile C++ code without the use of a build tool such as make. One common way of doing this is by using the command-line compiler g++, which is part of the GNU Compiler Collection (GCC). The basic syntax for compiling a C++ program using g++ is as follows:

g++ -o [executable_name] [source_file_name].cpp

For example, if you have a C++ source file named “main.cpp” and you want to create an executable file named “program”, you would use the following command:

g++ -o program main.cpp

This command will create an executable file named “program” that you can run on your system. Note that the ‘-o’ flag is used to specify the name of the output file.

In addition to g++, other compilers such as clang++ and MSVC can also be used to compile C++ code without the use of make.

3. Can Make be used without g++?

Yes, it is possible to use Make without g++. Make is a build automation tool that is used to build and compile software programs. It is not specific to any particular programming language or compiler. Make uses a Makefile, which contains a set of rules and dependencies that describe how to build the software. The Makefile specifies the commands that need to be executed to build the program, and Make takes care of executing them in the correct order.

So, you can use Make with any compiler you want, such as clang, MSVC, icc, or any other compiler. In the makefile, you just need to specify the compiler you want to use and the rules to build your program.

For example, if you want to use clang to build your program, you can specify the following in your makefile:

CC=clang
CXX=clang++

and then use $(CC) or $(CXX) to call the compiler in your make recipes.

In summary, while Make and g++ are often used together, they are not dependent on each other and you can use Make with any compiler.

Amir Kirsh
Amir Kirsh reading time: 11 minutes minutes February 7, 2023
February 7, 2023

Table of Contents

Related Posts

11 minutes What Level of Build Observability Is Right for You?

Read More  

11 minutes 4 Challenges for Quant Devs in Financial Services

Read More  

11 minutes CPU vs GPU: Know the Difference

Read More