Spec-Zone.ru › Haskell 8

7.12. Использование общих библиотек

На некоторых платформах GHC поддерживает создание Haskell-кода в динамических библиотеках. Динамические библиотеки также иногда известны как динамические библиотеки, в частности, в Windows они называются динамическими библиотеками (DLL).

Динамические библиотеки позволяют одному экземпляру предварительно скомпилированного кода делиться между несколькими программами. В отличие от статической компоновки, код копируется в каждую программу. Использование динамических библиотек позволяет экономить дисковое пространство. Они также позволяют использовать один экземпляр кода в памяти между несколькими программами, которые его используют. Динамические библиотеки часто используются для структурирования больших проектов, особенно там, где разные части написаны на разных языках программирования. Динамические библиотеки также часто используются в качестве механизма плагинов различными приложениями. Это особенно распространено в Windows с использованием COM.

В версии GHC 6.12 создание динамических библиотек поддерживается для Linux (на архитектурах x86 и x86-64). Версия GHC 7.0 добавляет поддержку для Windows (см. Создание и использование Win32 DLL), FreeBSD и OpenBSD (x86 и x86-64), Solaris (x86) и Mac OS X (x86 и PowerPC).

Создание и использование динамических библиотек немного сложнее, чем создание и использование статических библиотек. При использовании Cabal большая часть деталей скрыта, просто используйте --enable-shared при конфигурировании пакета для создания его в виде динамической библиотеки или для его связывания с другими пакетами, созданными как динамические библиотеки. Дополнительная сложность при создании кода заключается в том, чтобы различать, будет ли код использоваться в динамической библиотеке или будет использовать версии динамических библиотек других зависимых пакетов. Существует дополнительная сложность при установке и распространении динамических библиотек или программ, использующих динамические библиотеки, для обеспечения того, чтобы все необходимые динамические библиотеки во время выполнения находились в соответствующих местах.

7.12.1. Создание программ, использующих динамические библиотеки

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

ghc --make -dynamic Main.hs

Это имеет два эффекта. Во-первых, код компилируется таким образом, что он может быть связан с версиями динамических библиотек Haskell-пакетов (таких как base). Во-вторых, при компоновке происходит связывание с динамическими версиями библиотек пакетов, а не со статическими версиями. Очевидно, что это требует, чтобы пакеты были созданы с динамическими библиотеками. На поддерживаемых платформах GHC поставляется с динамическими библиотеками для всех основных пакетов, но если вы устанавливаете дополнительные пакеты (например, с помощью Cabal), то их также необходимо создать с динамическими библиотеками (--enable-shared для Cabal).

7.12.2. Динамические библиотеки для Haskell-пакетов

Вы можете создать Haskell-код в динамической библиотеке и создать пакет, который будет использоваться другими Haskell-программами. Самый простой способ — использовать Cabal, просто настройте пакет Cabal с флагом --enable-shared.

Если вы хотите выполнить шаги вручную или пишете собственную систему сборки, то необходимо соблюдать определенные соглашения. Создание динамической библиотеки, экспортирующей Haskell-код, для использования другим Haskell-кодом, немного сложнее, чем для динамической библиотеки, экспортирующей C API и предназначенной для использования C-кодом. Если вы ошибетесь, обычно получите ошибки компоновщика.

В частности, Haskell-динамические библиотеки должны быть пакетами. Вы не можете произвольно назначать модули в отдельные динамические библиотеки. Haskell-динамические библиотеки должны соответствовать границам пакетов. Причина в том, что GHC обрабатывает ссылки на символы внутри одной и той же динамической библиотеки (или исполняемого файла) по-другому, чем ссылки на символы между различными динамическими библиотеками. GHC должен знать для каждого импортируемого модуля, если этот модуль находится локально в той же динамической библиотеке или в отдельной динамической библиотеке. Для этого используется система пакетов. При использовании -dynamic, модуль из другого пакета предполагается, что он происходит из отдельной динамической библиотеки, в то время как модули из одного пакета (или по умолчанию «главного» пакета) предполагаются внутри одной и той же динамической библиотеки (или исполняемого файла).

Большинство соглашений, ожидаемых GHC при использовании пакетов, описаны в Создание пакета из Haskell-исходного кода. Кроме того, обратите внимание, что GHC ожидает, что файлы .hi будут использовать расширение .dyn_hi. Другие требования такие же, как для C-библиотек, и описаны ниже, в частности, использование флагов -dynamic, -fPIC и -shared.

7.12.3. Динамические библиотеки, экспортирующие C API

Создание Haskell-кода в динамической библиотеке — хороший способ включить Haskell-код в более крупный проект с использованием нескольких языков. Хотя при статической компоновке рекомендуется использовать GHC для выполнения последнего шага компоновки, с динамическими библиотеками Haskell-библиотека может обрабатываться как любая другая динамическая библиотека. Связывание можно выполнить с помощью обычного компилятора или компоновщика C.

Возможно загружать динамические библиотеки, сгенерированные GHC, в другие программы, написанные не на Haskell, поэтому они подходят для использования в качестве плагинов. Конечно, для создания плагина вам нужно использовать FFI для экспорта функций C и соблюдать правила инициализации RTS. См. Создание Haskell-библиотеки, которую можно вызывать из внешнего кода. В частности, вам, вероятно, потребуется экспортировать функцию C из вашей динамической библиотеки для инициализации плагина перед вызовом любых Haskell-функций.

Для создания Haskell-модулей, экспортирующих C API в динамическую библиотеку, используйте флаги -dynamic, -fPIC и -shared:

ghc --make -dynamic -shared -fPIC Foo.hs -o libfoo.so

Как и прежде, флаг -dynamic указывает, что эта библиотека связывается с версиями динамических библиотек пакета rts и base. Флаг -fPIC требуется для всего кода, который будет находиться в динамической библиотеке. Флаг -shared указывает на создание динамической библиотеки, а не программы. Чтобы это было более ясно, мы можем разбить это на отдельные шаги компиляции и компоновки:

ghc -dynamic -fPIC -c Foo.hs
ghc -dynamic -shared Foo.o -o libfoo.so

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

Предупреждение

Если ваша динамическая библиотека экспортирует Haskell API, то вы не можете напрямую связать ее с другой Haskell-программой и использовать этот Haskell API. Вы получите ошибки компоновщика. Вместо этого вы должны преобразовать ее в пакет, как описано в разделе выше.

7.12.4. Поиск динамических библиотек во время выполнения

Основная сложность с управлением динамическими библиотеками заключается в организации поиска программ необходимых библиотек во время выполнения. Подробности работы различаются в зависимости от платформы, особенно для трех основных систем: Unix ELF, Windows и Mac OS X.

7.12.4.1. Unix

В Unix существует два механизма. Динамические библиотеки могут быть установлены в стандартные места, известные динамическому компоновщику. Например, /usr/lib или /usr/local/lib на большинстве систем. Другой механизм — использование «пути времени выполнения» или «rpath», встроенного в сами программы и библиотеки. Эти пути могут быть абсолютными путями или, по крайней мере, на Linux и Solaris, они могут быть путями, относительными к самой программе или библиотеке. В принципе, это позволяет создавать полностью переносимые наборы программ и библиотек.

GHC имеет флаг компоновки -dynload для выбора метода поиска динамических библиотек во время выполнения. В настоящее время существует два режима:

sysdep

Зависимый от системы режим. Это также режим по умолчанию. В Unix ELF-системах это встраивает RPATH/RUNPATH записи в динамическую библиотеку или исполняемый файл. В частности, он использует абсолютные пути к местам расположения динамических библиотек для rts и каждого пакета. Это означает, что программу можно немедленно запустить, и она сможет найти необходимые библиотеки. Однако это может быть неподходящим для развертывания, если библиотеки установлены в другом месте на другом компьютере.

deploy

Это не встраивает никакие пути времени выполнения. Он полагается на то, что динамические библиотеки доступны в стандартном месте или в каталоге, указанном переменной среды LD_LIBRARY_PATH.

Для использования относительных путей для зависимых библиотек на Linux и Solaris вы можете передать соответствующий флаг -rpath компоновщику:

ghc -dynamic Main.hs -o main -lfoo -L. -optl-Wl,-rpath,'$ORIGIN'

Это предполагает, что библиотека libfoo.so находится в текущем каталоге и сможет быть найдена в том же каталоге, что и исполняемый файл main после развертывания программы. Аналогичным образом, можно использовать подкаталог, относительный к исполняемому файлу, например, -optl-Wl,-rpath,'$ORIGIN/lib'.

Эта техника относительных путей может быть использована с любым из двух -dynload режимов, хотя это имеет больше смысла в режиме deploy . Разница заключается в том, что в режиме deploy приведенный выше пример приведет к ELF RUNPATH только $ORIGIN, в то время как в режиме sysdep RUNPATH будет $ORIGIN за которым следуют все каталоги библиотек всех зависимых пакетов программы (например, пакетов base и rts и т. д.), которые обычно являются абсолютными путями. Инструмент Unix readelf --dynamic полезен для проверки записей RPATH/RUNPATH в ELF-динамических библиотеках и исполняемых файлах.

На большинстве платформ UNIX также можно создавать исполняемые файлы, которые могут быть dlopen'd, как общие библиотеки, используя флаг -pie во время компоновки.

7.12.4.2. Mac OS X

Стандартное предположение для Darwin/Mac OS X заключается в том, что динамические библиотеки будут помечены во время сборки установленным именем (install name), которое является полным конечным путём установки файла библиотеки. Любые библиотеки или исполняемые файлы, которые впоследствии будут с ними связаны (даже если они ещё не установлены), будут использовать этот путь как местоположение для поиска в режиме выполнения. При компиляции с помощью ghc напрямую, установленное имя по умолчанию устанавливается в место, где оно было построено. Вы можете переопределить это с помощью опции -dylib-install-name ⟨path⟩ (которая передаёт -install_name Apple-компоновщику). Cabal делает это за вас. Он автоматически устанавливает установленное имя для динамических библиотек в абсолютный путь конечного места установки.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/8.10.2/docs/html/users_guide/shared_libs.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API