Этапы сборки программы
Компилятор g++
Хелп!
g++ --help
Стандарт
-
-std=c++98
- C++98 -
-std=c++11
- C++11 -
-std=c++14
- C++14 -
-std=c++17
- C++17 -
-std=c++2a
- C++20
Сборка
Для сборки программы необходимо указать компилятору g++ файлы исходного кода, например команда g++ main.cpp
скомпилирует исходный код файла main.cpp
в исполняемый файл a.out
(если компилятору не указать имя выходного файла то по умолчанию именем будет a.out
)
-
-o
- имя выходного файлаПример: Команда
g++ -o my.exe main.cpp
скомпилирует файлmain.cpp
в исполняемый файлmy.exe
.Можно передавать несколько исходных файлов для сборки, например
g++ -o myexe file1.cpp file2.cpp
. -
-c
- создание объектного файлаПример: Для создания объектного файла необходимо указать компилятору ключи
-c
и-o
:g++ -c -o main.o main.ppp
, данной командой компилятор g++ создает объектный файлmain.o
из файлаmain.cpp
Для сборки программы из объектных файлов необходимо указать компилятору в качестве входных параметров не файлы исходного кода а объектные файлы:
g++ -o myexe foo.o main.o bar.o
- создает программу из объектных файловfoo.o main.o bar.o
-
-I
- Указание каталога для поиска подключаемых файловПример:
g++ -o my.exe -I/my/path/to/include main.cpp
-
-L
- Указание каталога для поиска библиотек -
-l
- Указание конкретной библиотеки для линковки
Этапы сборки программы на С++
Процесс обработки текстовых файлов с кодом на языке 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.