Skip to content
System Programming
Compilation and build process

Compilation and build process

The set of samples showcases different aspects of the compilation process in the Linux environment using various tools such as gcc, make, cmake, etc.

Single file compilation

Consider main.cpp as a source file containing the whole logic of the application:

#include <iostream>
 
int main() {
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

To compile the source code into an executable file execute the following line in the source directory:

g++ main.cpp -o app

where parameter -o app defines the name of the output file (a.out by default).

If the command completes successfully, the source directory will contain a file called app that is ready to execute as follows:

./app

Pre-processing a C++ source file

You may want to stop the compiler after the pre-processing phase when all the directives such as include, define, etc. are resolved. In that case you will need to run the pass the -E flag to the compiler as follows:

g++ main.cpp -E > preprocessed-main.cpp

The result of the command prints the result on a console by default, hence we redirected the content to be written to file “preprocessed-main.cpp” by using redirection operator (>) of shell.

Translation of the source

You may also want to stop the compiler after the translation phase to get the low-level code in assembler language. For that you need to pass -S flag to the compiler. It will generate a new file with .s extension by default:

g++ main.cpp -S

After the command is completed successfully you will get a main.s file containing your source in assembler language. The file will contain something like this:

# ... some other things
main:
.LFB1522:
    .cfi_startproc
    endbr64
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    leaq    .LC0(%rip), %rsi
    leaq    _ZSt4cout(%rip), %rdi
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
    movq    %rax, %rdx
    movq    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rax
    movq    %rax, %rsi
    movq    %rdx, %rdi
    call    _ZNSolsEPFRSoS_E@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
# ... some other things

Assembling the compiled file

Most commonly, you will need to compile your source file to a binary object file containing your source code in a binary format, but not ready to execute. Object files are generated by passing -c to the compiler. By default a file with “.o” extension will be generated:

g++ main.cpp -c 

After this command, a file named main.o will be generated.

Linking object files

In most of the cases, you will have many source files compiled into object files separately and after all object files are ready you will link those together and add necessary libraries. For example, if you have files main.o, module1.o and module2.o you will need to link those together into an executable file (or a library):

g++ main.o module1.o module2.o

After this command, a file named “a.out” will be generated and will be ready to run.

Also, you may want to see the libraries your executable is linked to using the following command:

ldd a.out

In this basic example, your executable will be linked with the standard libraries as follows:

linux-vdso.so.1 (0x00007ffc695fb000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f55b5264000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f55b5072000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f55b4f23000)
/lib64/ld-linux-x86-64.so.2 (0x00007f55b5456000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f55b4f08000)