Компиляторы, флаги и сборка проекта
Чтобы код жил долго, его нужно собирать предсказуемо: компиляторы, флаги, многофайловые проекты и инструменты сборки.
Компиляция — преобразование исходного текста на Fortran в объектный и затем исполняемый код заранее, до запуска; в отличие от интерпретации, она происходит один раз и даёт быстрый бинарник.
Программа на Fortran бесполезна, пока не превратилась в исполняемый файл. В предыдущих уроках мы вызывали gfortran одной командой, но реальные проекты состоят из десятков файлов, требуют флагов оптимизации и контроля ошибок. Этот урок — практическое руководство по сборке: какие компиляторы выбрать, какие флаги включать в учебном и боевом режиме, как собирать многофайловый проект и какие инструменты автоматизируют рутину. Без этого фундамента любой содержательный код останется текстом, который негде запустить.
Выбор компилятора
Fortran — стандартизованный язык, но воплощают стандарт разные компиляторы. Знать их сильные стороны полезно: учебный код можно собрать чем угодно, а в реальном проекте выбор влияет на скорость и диагностику.
| Компилятор | Кто делает | Особенности |
| gfortran | проект GNU (GCC) | Бесплатный, кросс-платформенный, отличная диагностика, наш основной |
| ifx | Intel | Бесплатный, на основе LLVM, сильная оптимизация под x86 |
| nvfortran | NVIDIA | Для GPU-ускорения (OpenACC, CUDA Fortran) |
| LFortran / Flang | сообщество / LLVM | Новые компиляторы, активно развиваются |
Для всего курса достаточно gfortran: он ставится из пакетного менеджера (apt install gfortran в Debian/Ubuntu, через Homebrew на macOS, через MSYS2 или пакет на Windows) и даёт понятные сообщения об ошибках. Когда вы дорастёте до выжимания производительности на конкретном железе, имеет смысл сравнить с ifx.
Флаги компиляции, которые надо знать
Голый вызов gfortran file.f90 работает, но в серьёзной работе всегда добавляют флаги. Они делятся на две группы: для разработки (максимум проверок) и для релиза (максимум скорости).
# Учебная/отладочная сборка: ловим всё
gfortran -O0 -g -Wall -Wextra -fcheck=all -fbacktrace prog.f90 -o prog
# Боевая сборка: оптимизация
gfortran -O2 -march=native prog.f90 -o prog
Разберём ключевые флаги. -O0…-O3 задают уровень оптимизации (от никакой до агрессивной); -g добавляет отладочную информацию для отладчика. -Wall и -Wextra включают предупреждения — относитесь к ним как к ошибкам. -fcheck=all вставляет проверки времени выполнения, главная из которых — выход за границы массива; без неё такая ошибка молча портит память, с ней — даёт внятное сообщение. -fbacktrace печатает стек вызовов при аварии. В релизе всё это убирают ради скорости, а -march=native разрешает использовать инструкции именно вашего процессора.
Многофайловый проект
Настоящие программы разбиты на файлы: модуль с типами, модуль с процедурами, главная программа. Сборка проходит в два этапа — компиляция каждого файла в объектный (.o) и линковка объектных файлов в исполняемый. Рассмотрим минимальный пример из двух файлов. Сначала модуль:
! файл geometry.f90
module geometry
implicit none
real, parameter :: pi = 3.14159265358979_8
contains
pure function circle_area(r) result(a)
real, intent(in) :: r
real :: a
a = pi * r**2
end function circle_area
end module geometry
Затем главная программа, которая использует модуль через use:
! файл main.f90
program main
use geometry, only: circle_area
implicit none
print *, "Площадь:", circle_area(2.0)
end program main
Порядок сборки важен: модуль должен компилироваться раньше того, кто его использует, потому что компилятор создаёт файл .mod с описанием интерфейса.
gfortran -c geometry.f90 # -c: только компиляция, без линковки -> geometry.o + geometry.mod
gfortran -c main.f90 # видит geometry.mod
gfortran geometry.o main.o -o app # линковка
./app
Вывод:
Площадь: 12.5663710
Инструменты сборки
Собирать руками две команды терпимо, но проект из пятидесяти файлов так не построишь. Существуют инструменты, которые отслеживают зависимости и пересобирают только изменившееся.
- make — классический инструмент: вы описываете правила в
Makefile, и он сам решает, что пересобрать. Универсален, но требует ручного описания зависимостей между модулями. - CMake — генератор сборочных систем, понимает Fortran-модули, удобен для кросс-платформенных проектов.
- fpm (Fortran Package Manager) — современный официальный менеджер пакетов и сборщик. Создаёт проект командой
fpm new, собираетfpm build, запускаетfpm run, тянет зависимости из реестра. Для новых проектов это самый простой путь.
Минимальный fpm-проект задаётся файлом fpm.toml, и вся возня с порядком компиляции модулей ложится на инструмент.
name = "myproject"
version = "0.1.0"
[build]
auto-executables = true
Стадии компиляции и линковки подробнее
Путь от исходника до программы стоит представлять как конвейер из нескольких стадий, потому что ошибки на каждой стадии выглядят по-разному. Сначала препроцессинг (если используется, например, для #include в файлах .F90 с заглавной F) — текстовая подстановка. Затем разбор и семантический анализ: компилятор строит дерево программы, проверяет типы, опирается на .mod-файлы для внешних имён. Здесь ловятся «синтаксические» и «типовые» ошибки — необъявленная переменная, неверный аргумент. Далее идёт оптимизация внутреннего представления и генерация объектного кода — получается .o-файл с машинными инструкциями, но с ещё не разрешёнными ссылками на внешние процедуры. Наконец, линковщик сшивает все .o-файлы и библиотеки, разрешая символы: вызов circle_area в одном файле связывается с её телом в другом. Ошибки линковки («undefined reference») принципиально иные, чем ошибки компиляции: они означают, что символ нигде не найден или объявлен, но не определён, — частый признак того, что вы забыли добавить нужный .o-файл или библиотеку в команду.
Понимание этого конвейера экономит часы отладки. Если gfortran ругается на «Cannot open module file» — это стадия разбора, не нашёлся .mod; решение в порядке сборки. Если жалуется линковщик — недостаёт объектного файла. А если код собрался, но падает с «Segmentation fault» — это уже рантайм, и тут спасает пересборка с -fcheck=all -g -fbacktrace, превращающая немой крах в осмысленное сообщение с номером строки.
Внешние библиотеки: BLAS и LAPACK
Реальный численный код почти никогда не пишут «с нуля» — он опирается на проверенные библиотеки, и умение их подключать так же важно, как знание синтаксиса. Две легендарные библиотеки — BLAS (базовые операции линейной алгебры: умножение матриц, скалярные произведения) и LAPACK (решение систем уравнений, собственные значения, разложения). Они написаны и вылизаны десятилетиями, существуют в оптимизированных под конкретное железо вариантах (OpenBLAS, Intel MKL) и стоят в основе численного мира, включая, как ни странно, Python: NumPy и SciPy вызывают именно их. Подключение к фортрановской программе сводится к указанию библиотеки линковщику флагом -l, например -llapack -lblas, и при необходимости пути к ней через -L. Это превращает сборку в нечто вроде «скомпилировать мой код и связать его с этими готовыми числодробилками», что и есть типичный рабочий процесс инженера: своя логика плюс опора на индустриальные библиотеки, а не велосипеды.
Как работает под капотом
Почему модуль нужно компилировать первым? Когда gfortran обрабатывает module geometry, он порождает два артефакта: объектный файл geometry.o с машинным кодом процедур и текстово-бинарный файл geometry.mod с описанием интерфейсов — какие в модуле есть процедуры, какие у них типы аргументов, какие константы. Когда позже компилируется main.f90 со строкой use geometry, компилятор читает geometry.mod и проверяет, что вы вызываете circle_area правильно — с одним аргументом типа real. Если .mod-файла ещё нет, компиляция main.f90 провалится. Этот механизм даёт Fortran строгую проверку типов между файлами — то, чего в старом C достигали ненадёжными заголовками.
Линковщик на финальном шаге сшивает объектные файлы: он находит, что вызов circle_area в main.o ссылается на тело функции в geometry.o, и связывает их по символьным именам. Поэтому в команде линковки перечисляют все .o-файлы.
Стоит понимать и экономический смысл раздельной компиляции. В проекте из сотен файлов изменение одного исходника не должно тянуть за собой пересборку всех остальных — это были бы часы ожидания. Раздельная компиляция позволяет пересобрать только затронутый .o и заново слинковать; именно эту логику зависимостей и автоматизируют make, CMake и fpm, сравнивая даты изменения файлов. Но у Fortran есть коварство, которого нет в C: если вы поменяли интерфейс модуля (добавили аргумент в процедуру), пересобрать нужно не только сам модуль, но и всех его пользователей, ведь их .mod-зависимость устарела. Инструменты сборки, понимающие Fortran-модули, отслеживают это автоматически; самодельный Makefile без описания модульных зависимостей здесь подведёт — отсюда популярность fpm и CMake, которые знают про .mod из коробки.
Частые ошибки
- Неправильный порядок компиляции. Компиляция
main.f90раньше модуля даёт ошибку «Cannot open module file geometry.mod». Решение — собрать модуль первым или поручить порядок инструменту (make/fpm). - Отсутствие
-fcheck=allпри отладке. Без него выход за границу массива не диагностируется и приводит к случайным сбоям; в учебной сборке этот флаг почти обязателен. - Перенос предупреждений «на потом». Предупреждения
-Wallчасто указывают на реальные баги (необъявленная переменная, несоответствие типов аргументов). Игнорировать их опасно. - Релизные флаги при отладке. С
-O2отладчик показывает «оптимизированные» строки не по порядку; для отладки нужен-O0 -g. - Несовместимые
.mod-файлы. Файлы.modпривязаны к версии компилятора; при смене gfortran их нужно пересоздать.
Итоги
- Fortran компилируется заранее; основной бесплатный компилятор курса — gfortran (альтернатива — Intel ifx).
- Отладочная сборка:
-O0 -g -Wall -Wextra -fcheck=all -fbacktrace; релизная:-O2 -march=native. - Многофайловый проект собирают в два этапа:
-cкомпилирует каждый файл в.o, затем их линкуют. - Модуль порождает
.modс интерфейсом и обязан компилироваться раньше тех, кто его использует. - Для автоматизации применяют make, CMake или современный fpm (Fortran Package Manager).
- Флаг
-fcheck=allловит выход за границы массива — включайте его при разработке.