5.1.5. Режимы работы
Поведение GHC в первую очередь определяется флагом режима. Можно указать только один из этих флагов, но он не обязательно должен быть первым параметром командной строки. Например,
$ ghc Main.hs --make -o my-application
Если флаг режима не указан, GHC перейдёт в режим --make (Использование ghc --make), если в командной строке указаны исходные файлы Haskell; в противном случае он свяжет указанные в командной строке объектные файлы, чтобы создать исполняемый файл.
Доступны следующие флаги режима:
-
--interactive -
Интерактивный режим, также доступный как ghci. Подробное описание интерактивного режима приведено в разделе Использование GHCi.
-
--run ⟨file⟩ -
Запустить точку входа main скрипта. Подобно runghc/runhaskell, по умолчанию этот режим использует интерпретатор байт-кода. Если в командной строке есть аргумент --, все следующие за ним аргументы будут переданы скрипту. Все аргументы, предшествующие --, интерпретируются как аргументы GHC.
-
--make -
В этом режиме GHC автоматически соберёт многомодульную программу на Haskell, самостоятельно определив зависимости. Если ваша программа на Haskell проста, этот способ, скорее всего, будет гораздо проще и быстрее, чем использование make. Режим make описан в разделе Использование ghc --make.
Этот режим используется по умолчанию, если в командной строке указаны исходные файлы Haskell; в этом случае параметр --make можно опустить.
-
-e ⟨expr⟩ -
Режим вычисления выражений. Он очень похож на интерактивный режим, но вместо него вычисляется одно выражение (⟨expr⟩), заданное в командной строке. Этот флаг можно указывать несколько раз; в таком случае выражения вычисляются последовательно. Подробнее см. Режим вычисления выражений.
-
-E -
Остановиться после препроцессирования (файл .hspp)
-
-C -
Остановиться после генерации C (файл .hc)
-
-S -
Остановиться после генерации ассемблерного кода (файл .s)
-
-c -
Остановиться после генерации объектного файла (.o)
Это традиционный режим пакетной компиляции, в котором GHC может компилировать исходные файлы по одному или связывать объектные файлы в исполняемый файл. См. Режим пакетного компилятора.
-
--merge-objs -
Объединить набор статических объектных файлов в библиотеку, оптимизированную для загрузки в GHCi. См. Создание библиотек GHCi.
-
-M -
Режим генерации зависимостей. В этом режиме GHC можно использовать для генерации сведений о зависимостях, подходящих для использования в Makefile. См. Генерация зависимостей.
-
--frontend ⟨module⟩ -
Запустить GHC с указанным подключаемым модулем внешнего интерфейса. Подробнее см. Подключаемые модули внешнего интерфейса.
-
-shared -
Создать разделяемый объект (или DLL в Windows). См. Создание DLL.
-
--help -
-? -
Вывести длинное сообщение со справкой по использованию в стандартный вывод, а затем завершить работу.
-
--show-iface ⟨file⟩ -
Прочитать интерфейс из ⟨file⟩ и вывести его в текстовом виде в stdout. Например, ghc --show-iface M.hi.
-
--supported-extensions -
--supported-languages -
Вывести список поддерживаемых языковых расширений.
-
--show-options -
Вывести список поддерживаемых параметров командной строки. Этот флаг можно использовать для автодополнения в оболочке.
-
--info -
Вывести сведения о компиляторе.
-
--version -
-V -
Вывести строку в одну строку с номером версии GHC.
-
--numeric-version -
Вывести только числовой номер версии GHC.
-
--print-booter-version -
Вывести числовую версию двоичного файла GHC, использованного для начальной загрузки сборки этого компилятора.
-
--print-build-platform -
Вывести целевую строку платформы сборки, на которой был собран GHC, в формате, сформированном GNU Autotools. Формат: cpu-manufacturer-operating_system-(kernel), например x86_64-unknown-linux.
-
--print-c-compiler-flags -
Вывести список флагов, переданных компилятору C при сборке GHC.
-
--print-c-compiler-link-flags -
Вывести список флагов, переданных компилятору C на этапе компоновки при сборке GHC.
-
--print-debug-on -
Вывести True, если GHC был собран с флагом -DDebug. Он включает проверки утверждений и дополнительный отладочный код. Флаг можно задать в GhcStage1HcOpts и/или GhcStage2HcOpts; он автоматически устанавливается для вариантов сборки devel1 и devel2.
-
--print-global-package-db -
Вывести путь к каталогу глобальной базы пакетов GHC. База пакетов хранит сведения об установленных пакетах в каталоге, содержащем отдельный файл для каждого пакета. Этот флаг выводит путь к глобальной базе, поставляемой с GHC; в Unix он выглядит примерно так: /usr/lib/ghc/package.conf.d. Могут существовать и другие базы пакетов, например пользовательская база пакетов. Подробнее см. Базы пакетов.
-
--print-have-interpreter -
Вывести YES, если GHC был собран со встроенным интерпретатором, и NO в противном случае. Если в эту сборку GHC не включён интерпретатор, запуск в интерактивном режиме (см. --interactive) приведёт к ошибке. Это относится только к интерактивному использованию GHC, но не к отдельным исполняемым файлам GHCi (см. Использование GHCi).
-
--print-have-native-code-generator -
Вывести YES, если генератор машинного кода поддерживает целевую платформу, и NO в противном случае. (См. Генератор машинного кода (-fasm).)
-
--print-host-platform -
Вывести целевую строку платформы хоста, то есть платформы, на которой должен работать GHC, в формате, сформированном GNU Autotools. Формат: cpu-manufacturer-operating_system-(kernel), например x86_64-unknown-linux.
-
--print-leading-underscore -
Вывести YES, если GHC был собран для использования символов с подчёркиванием в начале в объектных файлах, и NO в противном случае. Обычно это зависит от целевой платформы.
-
--print-libdir -
Вывести путь к каталогу библиотек GHC. Это корень дерева каталогов, содержащего библиотеки, интерфейсы и включаемые файлы GHC (в Unix он обычно выглядит примерно так: /usr/local/lib/ghc-5.04). Это значение $libdir в файле конфигурации пакета (см. Пакеты).
-
--print-ld-flags -
Вывести флаги компоновки, используемые для сборки GHC.
-
--print-object-splitting-supported -
Вывести NO, поскольку разделение объектных файлов больше не поддерживается. Более переносимый и надёжный альтернативный вариант описан в разделе -split-sections.
-
--print-project-git-commit-id -
Вывести идентификатор коммита Git, из которого был собран этот GHC. Это позволяет определить конкретную ревизию, из которой получен текущий двоичный файл, что особенно полезно при разработке самого GHC. Значение задаётся скриптом configure.
-
--print-project-version -
Вывести версию, заданную скриптом configure во время сборки. Это просто версия GHC.
-
--print-rts-ways -
Пакеты, например система выполнения (Runtime System), могут собираться в нескольких вариантах: - profiling — с поддержкой профилирования - dynamic — с динамической компоновкой - logging — с журналированием событий RTS - threaded — многопоточная RTS - debug — RTS с отладочной информацией
Возможны различные сочетания этих вариантов.
-
--print-stage -
GHC собирается с помощью самого GHC, и эта сборка выполняется по этапам, которым присваиваются номера.
- Этап 0 — установленный у вас GHC. «Установленный у вас GHC» также называется «компилятором начальной загрузки».
- Этап 1 — первый GHC, который мы собираем с помощью этапа 0. Затем этап 1 используется для сборки пакетов.
- Этап 2 — второй GHC, который мы собираем с помощью этапа 1. Обычно именно его мы устанавливаем при выполнении make install.
- Этап 3 является необязательным, но иногда его собирают для проверки этапа 2.
Этап 1 не поддерживает интерактивное выполнение (GHCi) и Template Haskell.
-
--print-support-smp -
Вывести YES, если GHC был собран с поддержкой нескольких процессоров, и NO в противном случае.
-
--print-tables-next-to-code -
Вывести YES, если GHC был собран с флагом --enable-tables-next-to-code, и NO в противном случае. Этот параметр включён по умолчанию, поскольку он позволяет получить более эффективное расположение кода.
-
--print-target-platform -
Вывести целевую строку целевой платформы, то есть платформы, на которой будут выполняться сгенерированные двоичные файлы, в формате, сформированном GNU Autotools. Формат: cpu-manufacturer-operating_system-(kernel), например x86_64-unknown-linux.
-
--print-unregisterised -
Вывести YES, если этот GHC был собран в режиме без регистровой оптимизации (unregisterised), и NO в противном случае. «Без регистровой оптимизации» означает, что GHC отключит большинство платформенно-зависимых приёмов и оптимизаций. Будут доступны только генераторы кода LLVM и C. Подробнее см. Компиляция без регистровой оптимизации.
5.1.5.1. Использование ghc --make
В этом режиме GHC собирает многомодульную программу на Haskell, проходя по зависимостям от одного или нескольких корневых модулей (обычно это только Main). Например, если ваш модуль Main находится в файле с именем Main.hs, скомпилировать и скомпоновать программу можно так:
ghc --make Main.hs
На самом деле GHC автоматически переходит в режим make, если в командной строке указаны исходные файлы Haskell и не задан другой режим. Поэтому в этом случае можно просто ввести:
ghc Main.hs
Можно указать любое количество имён исходных файлов или модулей; GHC определит все модули программы, пройдя по импортам из этих начальных модулей. Затем он попытается скомпилировать каждый устаревший модуль и, наконец, если есть модуль Main, программа также будет скомпонована в исполняемый файл.
Основные преимущества использования ghc --make по сравнению с традиционными Makefile:
- GHC не нужно перезапускать перед каждой компиляцией, поэтому он может кэшировать данные между компиляциями. Компиляция многомодульной программы с помощью
ghc --make может выполняться до двух раз быстрее, чем последовательный запуск ghc для каждого исходного файла. - Вам не нужно писать
Makefile. - При каждом запуске GHC заново вычисляет зависимости, поэтому они никогда не расходятся с исходным кодом.
- С помощью флага
-j[⟨n⟩] можно компилировать модули параллельно. Укажите -j ⟨n⟩, чтобы компилировать ⟨n⟩ задач параллельно. Если ⟨n⟩ не задано, используется число процессоров.
С --make можно использовать любые параметры командной строки, описанные в остальной части этой главы. Однако следует учитывать, что все параметры, указанные в командной строке, применяются ко всем компилируемым исходным файлам. Если параметр должен применяться только к одному исходному файлу, используйте прагму OPTIONS_GHC (см. Параметры командной строки в исходных файлах).
Если программу нужно скомпоновать с дополнительными объектными файлами (например, с некоторым вспомогательным кодом на C), укажите эти файлы в командной строке, и GHC включит их в исполняемый файл при компоновке.
Для обратной совместимости с существующими сценариями make при использовании вместе с -c этап компоновки пропускается (как и при --make -no-link).
Обратите внимание, что GHC может отслеживать зависимости, только если ему доступен исходный файл. Поэтому, если программа включает модуль без исходного файла, GHC выдаст сообщение об ошибке, даже если для этого модуля имеются объектный файл и файл интерфейса. Исключение составляют модули пакетов, исходные файлы которых могут как присутствовать, так и отсутствовать.
Исходные файлы программы необязательно должны находиться в одном каталоге; для добавления каталогов в путь поиска можно использовать параметр -i (см. Путь поиска).
-
-j[⟨n⟩] -
По возможности выполнять компиляцию параллельно. Во время компиляции GHC будет использовать до ⟨N⟩ потоков. Если N не задано, используется число процессоров. Обратите внимание, что компиляция модуля может начаться только после сборки его зависимостей.
5.1.5.2. Протокол Jobserver GHC
Протокол Jobserver GHC описан в предложении GHC № 540.
Этот протокол позволяет серверу динамически запускать множество экземпляров клиентского процесса, ограничивая их общее число используемых возможностей значением <n>. Это достигается координацией через системный семафор (семафор POSIX в Linux и Darwin или семафор Win32 в Windows).
В протоколе Jobserver GHC участвуют два типа процессов:
-
Jobserver создаёт системный семафор с определённым количеством доступных токенов.
Каждый раз перед запуском нового подпроцесса jobclient сервер обязан сначала получить один токен из семафора. Этот токен необходимо освободить после завершения подпроцесса.
После завершения работы jobserver обязан уничтожить созданный им семафор.
-
Jobclient — это подпроцесс, запущенный jobserver или другим jobclient.
Каждый jobclient начинает работу с одним доступным токеном (своим неявным токеном, полученным от родительского процесса, который его запустил) и может запросить дополнительные токены через протокол Jobserver, ожидая появления токена в семафоре.
Каждый раз перед запуском нового подпроцесса jobclient обязан передать дочернему jobclient один токен. Это может быть неявный токен jobclient или другой токен, полученный им из семафора.
Каждый jobclient обязан освободить ровно столько токенов, сколько он получил из семафора (не считая неявных токенов).
Сам GHC выступает в роли jobclient; этот режим можно включить с помощью флага -jsem.
-
-jsem -
По возможности выполнять компиляцию параллельно, координируя работу с другими процессами через семафор ⟨sem⟩ (заданный в виде строки). Если семафор не существует, возникает ошибка.
Параметр -jsem имеет приоритет над параметром -j[⟨n⟩], и наоборот.
5.1.5.3. Несколько домашних единиц
Компилятор также поддерживает сборку нескольких единиц за один запуск. В современных проектах часто приходится одновременно работать с несколькими взаимозависимыми пакетами. Поддержка нескольких домашних единиц позволяет загрузить все эти локальные пакеты в один сеанс ghc и быстро получать сведения о том, как изменения влияют на другие зависимые пакеты.
Чтобы задать несколько единиц, параметр -unit @⟨filename⟩ указывается несколько раз, вместе с файлом ответа, содержащим аргументы для каждой единицы. Файл ответа содержит список аргументов, разделённых символами новой строки.
ghc -unit @unitA -unit @unitB
где файл ответа unitA содержит обычные аргументы, которые передаются в режим --make.
-this-unit-id a-0.1.0.0
-i
-isrc
A1
A2
...
После этого, запустившись в режиме --make, компилятор скомпилирует обе единицы — a и b.
GHCi полностью поддерживает несколько домашних единиц. Их можно указать с помощью параметра -unit @⟨filename⟩.
ghc --interactive -unit @unitA -unit @unitB
-
-unit @⟨filename⟩ -
Этот параметр передаётся несколько раз, чтобы сообщить компилятору обо всех домашних единицах, которые он будет компилировать. Параметры каждой единицы указываются в файле ответа, содержащем список обычных аргументов, разделённых символами новой строки.
Для упрощения работы с несколькими единицами добавлено несколько дополнительных флагов.
-
-working-dir ⟨dir⟩ -
Обычно предполагается, что пакет компилируется в каталоге, где находится его файл cabal. Поэтому все пути, используемые компилятором, считаются относительными к этому каталогу. При работе с несколькими домашними единицами компилятор часто запускается не из стандартного каталога, а из каталога, где находится файл cabal.project. В этом случае можно передать параметр -working-dir, задающий путь от текущего каталога до каталога, который единица считает корневым; обычно это каталог, содержащий файл cabal.
При указании этого флага все относительные пути, используемые компилятором, отсчитываются от рабочего каталога. В частности, это относится к флагам -i и -I⟨dir⟩.
Значение этого параметра также можно получить с помощью функции Template Haskell getPackageRoot. Он предназначен для использования с вспомогательными функциями, такими как makeRelativeToProject, которые делают относительные пути к файлам относительными к каталогу компиляции, а не к каталогу, содержащему файл .cabal.
-
-this-package-name ⟨unit-id⟩ -
Этот флаг упрощает неудобное взаимодействие расширения PackageImports и нескольких домашних единиц. При использовании PackageImports в импорте можно указать имя пакета, чтобы различать модули с одинаковыми именами, находящиеся в разных пакетах.
Этот флаг позволяет задать имя пакета для домашней единицы, чтобы можно было различать несколько домашних единиц, предоставляющих модули с одинаковыми именами.
-
-hidden-module ⟨module name⟩ -
Этот флаг можно указывать несколько раз, чтобы задать модули домашней единицы, которые не должны быть видны за её пределами.
Флаг предназначен главным образом для воссоздания различия между открытыми и скрытыми модулями установленных пакетов.
-
-reexported-module ⟨reexport-spec⟩ -
Этот флаг можно указывать несколько раз, чтобы задать модули, которые не определены в единице, но должны быть реэкспортированы. В результате другие единицы будут видеть такой модуль так, как будто он определён в этой единице.
Простая форма флага позволяет реэкспортировать один модуль под тем же именем:
-reexported-module A
В более сложной форме флага при реэкспорте можно переименовать модуль:
-reexported-module "A as B"
Этот флаг позволяет воспроизвести возможность реэкспорта модулей пакетов при работе с несколькими домашними единицами.
5.1.5.3.1. Требование замкнутости домашних единиц
При работе с несколькими домашними единицами необходимо обеспечить одно важное свойство замкнутости.
Внешние единицы не должны зависеть от домашних единиц.
Компилятор проверяет это свойство замкнутости, однако вызывающий GHC инструмент должен обеспечить, чтобы переданный список домашних единиц удовлетворял этому инварианту.
Например, предположим, что у нас есть три единицы: p, q и r, причём p зависит от q, а q — от r. Тогда согласно свойству замкнутости, если мы загружаем p и r как домашние единицы, необходимо также загрузить q, поскольку q зависит от домашней единицы r, а q нужна, потому что от неё зависит p.
5.1.5.4. Режим вычисления выражений
Этот режим очень похож на интерактивный, но в нём вычисляется одно выражение, заданное в командной строке как аргумент параметра -e:
ghc -e expr
В командной строке можно указать исходные файлы Haskell; они будут загружены так же, как в интерактивном режиме. Выражение вычисляется в контексте загруженных модулей.
Например, чтобы загрузить и запустить программу на Haskell, содержащую модуль Main, можно выполнить:
ghc -e Main.main Main.hs
Или можно просто использовать этот режим для вычисления выражений в контексте Prelude:
$ ghc -e "interact (unlines.map reverse.lines)"
hello
olleh