В данном разделе описывается, какие файлы ожидает найти GHC, какие файлы он создаёт, где хранятся эти файлы и какие параметры влияют на это поведение.
Конвенции именования путей различаются в зависимости от системы. В частности, разделитель каталогов — “/” в системах Unix и “\” в системах Windows. В следующих разделах мы будем последовательно использовать “/” в качестве разделителя каталогов; замените его соответствующим символом для вашей системы.
5.8.1. Файлы исходного кода Haskell
Каждый модуль Haskell должен храниться в отдельном файле.
Обычно имя файла должно совпадать с именем модуля, заменяя точки в имени модуля разделителями каталогов. Например, в системе Unix модуль A.B.C должен храниться в файле A/B/C.hs, относительно некоторого базового каталога. Если модуль не будет импортирован другим модулем (например, Main), то вы можете использовать любое имя файла для него.
GHC предполагает, что исходные файлы имеют кодировку ASCII или UTF-8, другие кодировки не распознаются. Однако невалидные последовательности UTF-8 будут игнорироваться в комментариях, поэтому можно использовать другие кодировки, такие как Latin-1, при условии, что некомментируемый исходный код использует только ASCII.
5.8.2. Файлы вывода
При компиляции исходного файла GHC обычно генерирует два файла: файл объекта и файл интерфейса.
Файл объекта, который обычно имеет суффикс .o , содержит скомпилированный код для модуля.
Файл интерфейса, который обычно имеет суффикс .hi , содержит информацию, необходимую GHC для компиляции других модулей, зависящих от данного модуля. Он содержит такие данные, как типы экспортированных функций, определения типов данных и так далее. Он хранится в бинарном формате, поэтому не пытайтесь его прочитать; используйте вместо этого параметр --show-iface ⟨file⟩ (см. Другие параметры, относящиеся к файлам интерфейса).
Вы должны рассматривать файл объекта и файл интерфейса как пару, поскольку файл интерфейса представляет собой, в некотором смысле, читаемый компилятором описание содержимого файла объекта. Если по какой-либо причине файл интерфейса и файл объекта разойдутся, компилятор может сделать предположения о файле объекта, которые не будут верны; в таком случае могут возникнуть проблемы. По этой причине рекомендуется хранить файлы объектов и интерфейсов в одном месте (GHC делает это по умолчанию, но можно изменить настройки, как мы объясним вскоре).
Каждый модуль имеет определённое имя модуля, определённое в его исходном коде (module A.B.C where ...).
Имя файла объекта, сгенерированного GHC, выводится по следующим правилам, где ⟨osuf⟩ — суффикс файла объекта (это можно изменить параметром -osuf ).
- Если параметр
-odirне указан (по умолчанию), имя файла объекта выводится из имени исходного файла (игнорируя имя модуля) путём замены суффикса на ⟨osuf⟩. - Если параметр
-odir ⟨dir⟩указан, имя файла объекта будет ⟨dir⟩/⟨mod⟩.⟨osuf⟩, где ⟨mod⟩ — имя модуля с точками, заменёнными на слэши. GHC будет молча создавать необходимую структуру каталогов под ⟨dir⟩, если она ещё не существует.
Имя файла интерфейса выводится по тем же правилам, за исключением того, что суффикс — ⟨hisuf⟩ (.hi по умолчанию) вместо ⟨osuf⟩, а соответствующие параметры — -hidir ⟨dir⟩ и -hisuf ⟨suffix⟩ вместо -odir ⟨dir⟩ и -osuf ⟨suffix⟩ соответственно.
Например, если GHC компилирует модуль A.B.C в файле src/A/B/C.hs, без флагов -odir или -hidir, файл интерфейса будет помещён в src/A/B/C.hi, а файл объекта — в src/A/B/C.o.
Для любого импортируемого модуля GHC требует, чтобы имя модуля в инструкции импорта точно совпадало с именем модуля в файле интерфейса (или исходном файле), найденном с помощью стратегии, указанной в Пути поиска. Это означает, что для большинства модулей имя исходного файла должно совпадать с именем модуля.
Однако, обратите внимание, что разумно иметь модуль Main в файле с именем foo.hs, но это работает только потому, что GHC никогда не должен искать интерфейс для модуля Main (потому что он никогда не импортируется). Поэтому можно иметь несколько модулей Main в отдельных исходных файлах в одном каталоге, и GHC не запутается.
В режиме пакетной компиляции имя файла объекта также может быть переопределено с помощью параметра -o ⟨file⟩, а имя файла интерфейса может быть указано непосредственно с помощью параметра -ohi ⟨file⟩.
5.8.3. Пути поиска
В вашей программе вы импортируете модуль Foo с помощью import Foo. В режиме --make или GHCi GHC будет искать исходный файл для Foo и произведёт его компиляцию в первую очередь. Без --make GHC будет искать файл интерфейса для Foo, который должен был быть создан в ходе предыдущей компиляции Foo.
Стратегия поиска исходных файлов следующая: GHC хранит список каталогов, называемый путём поиска. Для каждого из этих каталогов он пытается добавить ⟨basename⟩.⟨extension⟩ к каталогу и проверяет, существует ли файл. Значение ⟨basename⟩ — имя модуля с точками, заменёнными разделителями каталогов (”/” или “\\", в зависимости от системы), а ⟨extension⟩ — суффикс исходного файла (hs, lhs) если мы находимся в режиме --make или GHCi.
При поиске файлов интерфейсов в режиме -c мы ищем файлы интерфейсов в -hidir, если он задан. В противном случае используется та же стратегия, что и для исходных файлов, для поиска файла интерфейса.
Например, предположим, что путь поиска содержит каталоги d1, d2, и d3, и мы находимся в режиме --make, ищем исходный файл для модуля A.B.C. GHC будет искать в d1/A/B/C.hs, d1/A/B/C.lhs, d2/A/B/C.hs, и так далее.
По умолчанию путь поиска содержит один каталог: “.” (т.е. текущий каталог). Следующие параметры могут быть использованы для добавления или изменения содержимого пути поиска:
-
-i⟨dir⟩[:⟨dir⟩]* -
Этот параметр добавляет список каталогов, разделённых двоеточием, к пути поиска.
-
-i -
сбрасывает путь поиска до пустого.
Это не вся история: GHC также ищет модули в предварительно скомпилированных библиотеках, известных как пакеты. Подробности см. в разделе о пакетах (Пакеты).