Zie ook labo 1: (herhaaldelijk) compileren
Een greep uit de mogelijkheden, schematisch samengevat:
We gebruiken de UNIX GNU gcc compiler om C soure files om te zetten in binaries. Het simpelste om iets te compileren is:
g++ [bestand.cpp] -o [output.bin]
Het programma uitvoeren doe je dan met ./output.bin
. Normaal gezien kent het OS de execute flag automatisch toe, anders moet je nog chmod +x output.bin
uitvoeren.
Dit kan je makkelijk automatiseren met een shell script door de compilatie en de executie in twee regels onder elkaar te plaatsen. De C(++) wereld voorziet echter flexibelere tools die dit voor je kunnen vergemakkelijken.
Gebruik daarvoor Makefile
bestanden. Een Makefile is platte tekst die informatie bevat over je bronbestanden, libraries, binaries, … zodat je met één enkel commando:
make
Een aantal instructies kan ontketenen. Een Makefile is opgesplitst in verschillende blokken. Elke blok bevat één keyword gevolgd door :
, met op de volgende regels (voorafgaand met tab!) de uit te voeren instructies. Bijvoorbeeld:
.DEFAULT_GOAL := all
CC=gcc
clean:
rm -rf *.o
rm -rf *.out
compile:
$(CC) -c main.c -o main.o
link:
$(CC) -o main.out main.o
all: clean compile link
Bovenstaande Makefile bevat de volgende blokken:
Merk op dat als géén blok als argument op make
voorzien is, dat dan de “all” block uitgevoerd wordt (.DEFAULT_GOAL
, eerste regel). Op die manier kan je met het commando make compile
de compilatiestap uitvoeren, maar met make
of make all
een hele reeks aan stappen.
Makefiles verzorgen dus het uitvoeren van de compilatie, zodat wij nooit meer rechtstreeks g++
of gcc
moeten uitvoeren.
Deze configuratiebestanden komen niet zonder nadelen:
Om tekortkomingen van de archaïsche Makefile bestanden goed te maken zijn er nieuwe tools ontwikkeld die Makefiles genereren. Er zijn twee grote varianten voorzien: CMake en QMake.
CMake genereert Makefile
bestanden door middel van CMakeLists.txt
configuratie bestanden. Hierin beschrijf je je project, met een eenvoudigere syntax, waarna CMake het genereren van de Makefile uit handen neemt. Dit wil zeggen dat je nog steeds 2 stappen dient uit te voeren: het genereren, én het uiteindelijk uitvoeren van de Makefile zelf. Make blijft dus je comipler aanroepen!.
Een typisch CMakeLists.txt
bestand ziet er als volgt uit:
cmake_minimum_required(VERSION 3.10)
project(mijn_project)
SET(CMAKE_CXX_STANDARD 11)
add_executable(mijn_binary main.cpp bla.h bla.cpp)
De volgende eigenschappen zijn beschreven:
Extra bestanden compileren is een kwestie van bestanden toe te voegen aan het lijstje.
CMake is de standaard build tool in CLion. Je kan met CMake ook eenvoudig subprojecten maken, bijvoorbeeld een productie stukje in /src en een test stukje in /test. Download een CMake voorbeeld project met unittesten hier.
Qt heeft zijn eigen Makefile generatie systeem dat QMake
heet. Dit voornamelijk om de tussencompilatie stap te voorzien omdat Qt eigen keywords bij “verzonnen” heeft in de C++ standaard, zoals signals:
en slots:
. Omdat QMake deel is van een product is het nooit een goed idee om hier voor te kiezen als je geen Qt project gaat aanmaken. Uiteraard is QMake de standaard build tool in Qt Designer/Creator.
QMake gebruikt projectfiles om de configuratie in op te slaan. Een voorbeeld projectfile (gegenereerd):
#-------------------------------------------------
#
# Project created by QtCreator 2018-08-22T11:10:02
#
#-------------------------------------------------
QT += core gui widgets
TARGET = qt-labo-11-player
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp \
playercanvas.cpp
HEADERS += \
mainwindow.h \
playercanvas.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
Deze bestanden worden beheerd door de IDE, niet door jezelf. Dat betekent dat toevoegingen niet manueel dienen te gebeuren.
Meer info over qmake project files.
QMake en CMake bouwen nog steeds verder op het Makefile
model. Er zijn echter buildsystemen die hier volledig los van staan, waarvan de populairste SCons is.
In SCons beschrijf je je project met python! Er is gekozen voor een bestaande programmeertaal, in plaats van weeral een configuratie taaltje te verzinnen. Dit maakt SCons bijzonder flexibel: loopen over files in het systeem om ze te pipen naar een compiler, of een zipfile maken als distributiestap, zijn simpele zaken die met bovenstaande tools moeilijker te realiseren zijn.
SCons verwacht één SConstruct
bestand. Een voorbeeld SConstruct
file:
env = Environment(CXX = 'g++')
gtest = env.SConscript('lib/gtest/SConscript', 'env')
src = env.SConscript('src/SConscript', 'env')
out = env.SConscript('test/SConscript', 'env gtest src')
# output is an array with path to built binaries. We only built one file - run it (includes gtest_main).
test = Command( target = "testoutput",
source = str(out[0]),
action = str(out[0]) )
AlwaysBuild(test)
SCons vereist een python interpreter (en wat python kennis), en valt daarom buiten de scope van deze cursus. Voor de geïnteresseerden: lees ook SCons building in practice.