View on GitHub

Курс по C++

Базовые конспекты по программированию на C++ для групп 371ПС, 372ПС. В конспектах ОЧЕНЬ много неточностей, ошибок. Правки приветствуются =)

Этапы сборки программы

Компилятор g++

Хелп!

g++ --help

Стандарт

Сборка

Для сборки программы необходимо указать компилятору g++ файлы исходного кода, например команда g++ main.cpp скомпилирует исходный код файла main.cpp в исполняемый файл a.out (если компилятору не указать имя выходного файла то по умолчанию именем будет a.out)

Этапы сборки программы на С++

Процесс обработки текстовых файлов с кодом на языке C++ состоит из 4 этапов.

Рассмотрим подробнее упомянутые выше стадии обработки текстовых файлов на языке C++ на примере файла из статьи:

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

Препроцессинг

Препроцессинг — обработка текстовых файлов утилитой препроцессора, который производит замены текстов согласно правилам языка препроцессора C/C++. После препроцессора, тексты компилируемых файлов, обычно, значительно вырастают в размерах, но теперь в них содержится все, что потребуется компилятору для создания объектного файла. Во время препроцессинга в код вставляются все include, define, обрабатываются блоки ifndef .. define, ifdef.

g++ -E driver.cpp -o driver.ii

Ассемблирование

Ассемблирование — процесс превращения текста на языке C++ в текст на языке Ассемблера. Для компиляторов GNU используется синтаксис ассебмлера AT&T.

g++ -S driver.ii -o driver.s

Компилирование

Компилирование — процесс превращения текстов на языке Ассемблера в объектные файлы. Это файлы состоящие из кодов целевого процессора, но в которых еще не проставлены адреса объектов, которые находятся в других объектных файлах или библиотеках.

Объектный файл — это созданный ассемблером промежуточный файл, хранящий кусок машинного кода. Этот кусок машинного кода, который еще не был связан вместе с другими кусками машинного кода в конечную выполняемую программу, называется объектным кодом.

as driver.s -o driver.o

Далее возможно сохранение данного объектного кода в статические библиотеки для того, чтобы не компилировать данный код снова.

Линковка

Линковка — процесс объединения объектных файлов проекта и используемых библиотек в единую целевую сущность для целевой платформы. Это может быть исполняемая программа или библиотека статического или динамического типа.

Компоновщик (линкер) связывает все объектные файлы и статические библиотеки в единый исполняемый файл, который мы и сможем запустить в дальнейшем. Для того, чтобы понять как происходит связка, следует рассказать о таблице символов.

Таблица символов — это структура данных, создаваемая самим компилятором и хранящаяся в самих объектных файлах. Таблица символов хранит имена переменных, функций, классов, объектов и т.д., где каждому идентификатору (символу) соотносится его тип, область видимости. Также таблица символов хранит адреса ссылок на данные и процедуры в других объектных файлах. Именно с помощью таблицы символов и хранящихся в них ссылок линкер будет способен в дальнейшем построить связи между данными среди множества других объектных файлов и создать единый исполняемый файл из них.

` g++ driver.o -o driver `

Запуск

Последний этап, который предстоит пройти нашей программе — вызвать загрузчик для загрузки нашей программы в память. На данной стадии также возможна подгрузка динамических библиотек.

Запустим нашу программу: driver

CMakeLists.txt и MakeFile

MakeFile

Утилита make предназначена для работы с Makefile. Ссылка для установки тут.

Рассмотрим простейший проект, состоящий из 4 файлов с хабра.

main.cpp

#include <iostream>
#include "functions.h"

using namespace std;

int main(){
    print_hello();
    cout << endl;
    cout << "The factorial of 5 is " << factorial(5) << endl;
    return 0;
}

hello.cpp

#include <iostream>
#include "functions.h"

using namespace std;

void print_hello(){
   cout << "Hello World!";
}

factorial.cpp

#include "functions.h"

int factorial(int n){
    if(n!=1){
	return(n * factorial(n-1));
    }
    else return 1;
}

functions.h

void print_hello();
int factorial(int n);

Мэйкфайл - один из самых простых способов как-то отрегулировать происходящее на этапе сборки. Многие из вас уже наверняка сталкивались с проблемой, что из-за неправильного порядка сборки файлов проекта ваш проект не хотел линковаться/возникали еще какие-то проблемы. Сейчас мы научимся это чинить.

Слева в makefile указаны цели:

цель all - главная цель в makefile, если не указано иное. По умолчанию при запуске makefile собирается именно она. Справа от нее указаны зависимости - те цели, которые должны быть выполнены к моменту начала отработки данной цели. Таким образом, цели вызываются рекурсивно друг от друга. После этого идет строчка с командой, которую необходимо выполнить для сборки текущей цели.

Пример простейшего MakeFile для сборки программы:

all: hello

hello: main.o factorial.o hello.o
	g++ main.o factorial.o hello.o -o hello

main.o: main.cpp
	g++ -c main.cpp

factorial.o: factorial.cpp
	g++ -c factorial.cpp

hello.o: hello.cpp
	g++ -c hello.cpp

clean:
	rm -rf *.o hello

Таким образом для сборки all необходимо, чтобы была собрана цель hello, причем никаких дополнительных действий не нужно. Для сборки цели hello, в свою очередь, необходимо выполнение целей main.o factorial.o hello.o, после чего выполнится действие g++ main.o factorial.o hello.o -o hello.

Еще добавилась новая цель clean. Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: make -f Makefile clean

В MakeFile также есть возможность использования переменных:

# Это комментарий, который говорит, что переменная CC указывает компилятор
CC=g++  
CFLAGS=-c -Wall 
all: hello 
hello: main.o factorial.o hello.o 
	$(CC) main.o factorial.o hello.o -o hello 
main.o: main.cpp 
	$(CC) $(CFLAGS) main.cpp 
factorial.o: factorial.cpp 
	$(CC) $(CFLAGS) factorial.cpp 
hello.o: hello.cpp 
	$(CC) $(CFLAGS) hello.cpp 
clean: rm -rf *.o hello

CMakeLists.txt

Утилита cmake предназначена для работы с CMakeLists.txt. Именно cmake является встроенной в CLion утилитой для сборки ваших проектов.

В отличие от make он является “более” платформонезависимым, что позволяет его использовать для разных компиляторов, систем сборки, платформ.

Пример кода, который статически линкует SFML

cmake_minimum_required(VERSION 3.7)
set(PROJECT_NAME SFML_linking)

project(${PROJECT_NAME})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -static")

set(CMAKE_CXX_STANDARD 14)

set(SOURCE_FILES sandbox/main.cpp sandbox/Field.cpp sandbox/Field.h)

if (CMAKE_BUILD_TYPE STREQUAL "Release")
    set(SFML_ROOT C:/Users/fools/Documents/Libs/SFML-2.5.1V)
    add_executable(${PROJECT_NAME} WIN32 ${SOURCE_FILES})
else ()
    set(SFML_ROOT C:/Users/fools/Documents/Libs/SFML-2.5.1V64)
    add_executable(${PROJECT_NAME} ${SOURCE_FILES})
endif ()

set(SFML_STATIC_LIBRARIES True)

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(SFML 2 REQUIRED COMPONENTS window graphics audio network system)
if (SFML_FOUND)
    include_directories(${SFML_INCLUDE_DIR})
    target_link_libraries(${PROJECT_NAME} ${SFML_LIBRARIES} ${SFML_DEPENDENCIES})
endif ()

Позволяет делать if-ветвления, создавать переменные и многое другое. В данном примере библиотека линкуется в зависимости от выбранной версии сборки: release - 32бита, debug - 64 бита.

Другие компиляторы

Кроме minGW, про который сейчас шла речь существует достаточно большое количество других компиляторов под Windows. Рассмотрим компилятор clang. Самое большое отличие от minGW состоит в том, что он собирает ваши файлы не в машинный код, который исполняется прямо на процессоре, а в байт-код, который будет исполняться на специальной виртуальной машине LLVM.