C: Компиляция

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

При этом распределение кода программы по множеству файлов затрудняет процесс компиляции. Обычная команда для компиляции заменяется на последовательность команд.

$ gcc hello.c hellomain.c

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

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

$ gcc -c hello.c -o hello.o
$ gcc -c hellomain.c -o hellomain.o
$ gcc hello.o hellomain.o -o hello

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

Meson

Компиляция каждого из файлов исходного кода в ручном режиме может значительно усложниться ввиду увеличения количества таких файлов в проекте. Для этой цели был разработан инструментарий Meson, позволяющий осуществлять сборку проектов в автоматическом режиме. Он использует файлы с описанием правил сборки с именами meson.build. Сама сборка осуществляется в отдельной директории силами утилиты ninja в параллельном режиме.

Рассмотрим простейший файла meson.build для сборки простой рассмотренной ранее программы. Для начала рассмотрим код самой программы.

Это содержимое файла исходного кода hello.c:

  1. #include <stdio.h>
  2. #include "config.h"
  3.  
  4. void print_hello(void)
  5. {
  6.         printf("Hello world (%s:%s)n", CONFIG_NAME, CONFIG_VERSION);
  7. }

Загрузить исходный код примера

Это — содержимое файла исходного кода hello.h:

  1. #ifndef HELLO_H
  2. #define HELLO_H
  3.  
  4. void print_hello(void);
  5.  
  6. #endif /*HELLO_H*/

Загрузить исходный код примера

А это — содержимое файла исходного кода hellomain.c:

  1. #include <stdio.h>
  2. #include "hello.h"
  3.  
  4. int main()
  5. {
  6.         print_hello();
  7.        
  8.         return 0;
  9. }

Загрузить исходный код примера

Это файл для передачи параметров конфигурации от системы сборки приложению (config.h.meson). На этапе конфигурации сборочного окружения будут установлены значения описанных констант.

  1. #ifndef __CONFIG_H__
  2. #define __CONFIG_H__
  3.  
  4. #mesondefine CONFIG_NAME
  5. #mesondefine CONFIG_VERSION
  6.  
  7. #endif // __CONFIG_H__

Загрузить исходный код примера

А это файл meson.build. Зависимость от библиотеки glib приведена для демонстрации и не имеет практического значения.

  1. project('Hello', 'c',
  2.         version: '0.0.1',
  3.         license : 'GPL3+',
  4.         meson_version: '>=0.31')
  5.  
  6. glib = dependency('glib-2.0', version: '>=2.32')
  7.  
  8. config_h = configuration_data()
  9. config_h.set('CONFIG_NAME', '"@0@"'.format(meson.project_name()))
  10. config_h.set('CONFIG_VERSION', '"@0@"'.format(meson.project_version()))
  11. configure_file(input: 'config.h.meson',
  12.                 output: 'config.h',
  13.                 configuration: config_h)
  14.  
  15. c_sources = [
  16.     'hellomain.c',
  17.     'hello.c',
  18. ]
  19.  
  20. hello = executable('hello',
  21.                     c_sources,
  22.                     install: true,
  23.                     dependencies : [glib])

Загрузить исходный код примера

В данном случае функция project() используется для задания параметров проекта, а именно, его имени ('Hello'), языка программирования ('c'), версии (version: '0.0.1'), лицензии (license: 'GPL3+') и минимальной версии Meson (meson_version: '>=0.31'). Функция dependency() используется для поиска зависимостей с помощью pkg-config и возвращает объекты найденных библиотек для последующего связывания в рамках функции executable(). Функция executable() задает параметры результирующего исполняемого файла, а именно, его имя ('hello'), список файлов исходного кода ('c_sources'), указание на необходимость установки (install: true) и список связываемых библиотек (dependencies: [glib]).  

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

$ meson build

После этого нужно перейти в созданную директорию build и выполнить команду ninja.

$ cd build
$ ninja

Установка в систему осуществляется с помощью отдельной команды.

$ ninja install