Spec-Zone.ru › Haskell 9

13. Запуск GHC на системах Win32

13.1. Запуск GHC на платформах Windows

Установщик, который устанавливает GHC на Win32, также настраивает ассоциации расширений файлов для файлов с расширениями «.hs» и «.lhs» таким образом, что двойной щелчок по ним запускает ghci.

Обратите внимание, что ghc и ghci требуют, чтобы имена файлов, содержащие пробелы, были экранированы с помощью кавычек:

c:\ghc\bin\ghci "c:\\Program Files\\Haskell\\Project.hs"

Если кавычки опущены в вышеприведённой команде, ghci интерпретирует имя файла как два отдельных имени, c:\\\\Program и Files\\\\Haskell\\\\Project.hs.

13.2. Запуск GHCi на Windows

Рекомендуется запускать GHCi в стандартной консоли Windows: выберите опцию GHCi в меню Пуск, добавленном установщиком GHC, или используйте Start->Run->cmd, чтобы получить консоль Windows и вызвать ghci оттуда (если она находится в PATH).

Если вы запускаете GHCi в оболочке Cygwin или MSYS, поведение сочетания клавиш Control-C ухудшается. В одной из этих сред вы должны использовать скрипт ghcii.sh для запуска GHCi; в противном случае при нажатии Control-C вы вернётесь к приглашению командной строки, но процесс GHCi всё ещё будет выполняться. Однако, даже используя скрипт ghcii.sh, при нажатии Control-C процесс GHCi будет убит немедленно, а не позволит прервать выполняемую программу внутри GHCi, как должно быть. Эта проблема вызвана тем, что среды оболочки Cygwin и MSYS не передают события Control-C дочерним процессам, не являющимся Cygwin, так как для этого должна быть консоль Windows.

Существует исключение: вы можете использовать оболочку Cygwin, если переменная среды CYGWIN не содержит tty. В этом режиме оболочка Cygwin ведёт себя как оболочка консоли Windows, и события консоли передаются дочерним процессам. Обратите внимание, что переменная среды CYGWIN должна быть установлена до запуска оболочки Cygwin; изменение её после этого не повлияет на оболочку.

Эта проблема затрагивает не только GHCi, но и любые программы, скомпилированные GHC, которые хотят перехватывать события консоли. См. модуль GHC.ConsoleHandler.

13.3. Взаимодействие с терминалом

По умолчанию GHC строит приложения, которые открывают окно консоли при запуске. Если вы хотите создать приложение только с графическим интерфейсом без окна консоли, используйте флаг -optl-mwindows на этапе компоновки.

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

Программы Windows с графическим интерфейсом без консоли не имеют stdin, stdout или stderr, поэтому использование обычных функций ввода-вывода Haskell приведёт к сбою вашей программы с исключением IO, например:

Fail: <stdout>: hPutChar: failed (Bad file descriptor)

Однако использование Debug.Trace.trace приемлемо, так как оно использует поддержку отладки Windows, а не stderr.

По какой-то причине Mingw поставляется с библиотекой readline, но без заголовков readline. В результате GHC (как и Hugs) не использует readline для интерактивного ввода на Windows. Вы можете получить близкое к этому моделирование, используя буфер оболочки emacs!

13.4. Различия в поведении библиотек

Некоторые стандартные библиотеки Haskell ведут себя немного иначе в Windows.

  • В Windows символ ^Z интерпретируется как символ конца файла, поэтому при чтении файла, содержащего этот символ, файл будет казаться оборванным непосредственно перед ним. Чтобы избежать этого, используйте IOExts.openFileEx для открытия файла в бинарном (непереведённом) режиме или измените уже открытый дескриптор файла в бинарный режим, используя IOExts.hSetBinaryMode. Модуль IOExts входит в состав пакета lang.

13.5. Пути к файлам в Windows

Пути к файлам в Windows не всегда одинаковы. Различные виды путей имеют разное значение. Ограничение MAX_PATH не является ограничением операционной системы или файловой системы. Это ограничение по умолчанию, наложенное API Win32 для обеспечения обратной совместимости.

Однако ядро NT предоставляет способы отказаться от предварительной обработки путей API Win32. Это делается путём явного использования нужного пространства имён в пути.

Пространства имён:

  • пространство имён файлов: \\?\
  • пространство имён устройств: \\.\
  • пространство имён NT: \

Каждый из них полностью отключает обработку пути API Win32, и пути передаются в неизменённом виде файловой системе.

Пути с буквой диска являются устаревшими путями. Буквы дисков на самом деле бессмысленны для ядра. Как и в Unix-системах, буквы дисков — это просто точки монтирования. Вы можете просмотреть свои точки монтирования, используя команду mountvol.

Начиная с GHC 8.6.1, менеджер ввода-вывода Haskell автоматически преобразует пути в устаревшем формате в пространство имён файлов Win32. По умолчанию менеджер ввода-вывода выполнит две операции с вашими путями:

  • заменить \ на \\
  • преобразовать относительные пути в абсолютные пути

Если вы хотите отказаться от всей предварительной обработки, просто явно используйте пространства имён в своих путях. Из-за этого изменения, если вам нужно открыть сырые устройства (например, порты COM), вам нужно явно использовать пространство имён устройств. (например, \\.\COM1). GHC и программы Haskell в целом больше не поддерживают открытие устройств в устаревшем формате.

Дополнительные сведения см. в документации Windows.

13.6. Использование GHC (и других исполняемых файлов, скомпилированных GHC) с Cygwin

13.6.1. Общие сведения

Инструменты Cygwin стремятся предоставить API в стиле Unix поверх библиотек Windows для облегчения переноса Unix-приложений на Windows. Для этого они вводят иерархию каталогов в стиле Unix под некоторым корневым каталогом (обычно / — это C:\cygwin\). Кроме того, всё, что создано на основе API Cygwin (включая инструменты Cygwin и программы, скомпилированные с помощью GHC Cygwin), будет видеть / как корень своей файловой системы, имитируя работу в обычной Unix-среде и находя такие вещи, как /bin и /usr/include, не заботясь при этом об их фактическом расположении в системе Windows (вероятно, C:\cygwin\bin и C:\cygwin\usr\include).

13.6.2. Проблема

GHC по умолчанию больше не зависит от cygwin, а является собственной программой Windows. Он создаётся с использованием mingw и использует mingw’s GHC при компиляции ваших Haskell-источников (даже если вы вызываете его из bash Cygwin), но здесь важно то, что, как и любая другая обычная программа Windows, ни GHC, ни создаваемые им исполняемые файлы не осведомлены о вымышленной Unix-иерархии Cygwin. GHC будет спокойно принимать как /, так и \\ в качестве разделителей путей, но не будет знать, где найти /home/joe/Main.hs или /bin/bash и т. п. Это приводит к всяческим проблемам, когда GHC используется из bash Cygwin или в сессиях make, выполняемых под Cygwin.

13.6.3. Действия

  • Не используйте абсолютные пути в make, configure и т. д., если есть вероятность, что они могут быть переданы GHC (или программам, скомпилированным GHC). Относительные пути подходят, поскольку инструменты cygwin с ними работают, а GHC принимает / в качестве разделителя путей. И относительные пути не зависят от того, где находится корневой каталог Cygwin, или на каком разделе или сетевом диске расположен ваш исходный код, если вы cd туда сначала.
  • Если вам необходимо использовать абсолютные пути (остерегайтесь бессовестного ROOT=$(pwd) в иерархиях make или скриптах configure), Cygwin предоставляет инструмент под названием cygpath, который может преобразовать пути Cygwin в стиле Unix в их эквиваленты в стиле Windows. Многие инструменты Cygwin фактически принимают абсолютные пути в стиле Windows (вспомните, что вам либо нужно экранировать \\ или преобразовать \\ в /), поэтому вы можете просто использовать их везде. Если вам нужны инструменты, которые выполняют какие-либо манипуляции с путями, зависящими от путей в стиле Unix (например, попытка интерпретировать : как разделитель в списках путей), вы всё ещё можете попробовать преобразовать пути с помощью cygpath перед тем, как передать их GHC и т. п.
  • Если у вас нет cygpath, у вас, вероятно, нет cygwin, и, следовательно, нет проблем с ним… если только вы не захотите написать одну сборку для нескольких платформ. Опять же, относительные пути — ваши друзья, но если вам нужно использовать абсолютные пути и не хотите использовать разные инструменты на разных платформах, вы можете просто написать небольшую Haskell-программу для вывода текущей директории (спасибо George Russell за эту идею): скомпилированная с помощью GHC, она даст вам представление о файловой системе, от которой зависит GHC (которое будет отличаться в зависимости от того, скомпилирован ли GHC с gcc cygwin или mingw или на реальной Unix-системе..) - эта небольшая программа также может обрабатывать экранирование \\ в путях. Помимо баннера и времени загрузки, что-то вроде этого также будет:

    $ echo "Directory.getCurrentDirectory >>= putStrLn . init . tail . show " | ghci
    

13.7. Создание и использование DLL-библиотек Win32

Динамические библиотеки, DLL-библиотеки Win32, на платформах Win32 компилятор способен как создавать, так и использовать динамические библиотеки (DLL) содержащие код, скомпилированный ghc. Этот раздел покажет вам, как использовать эту возможность.

Существует два различных способа использования DLL-библиотек:

  • Вы можете преобразовать каждый пакет Haskell в DLL, чтобы несколько исполняемых файлов Haskell, использующих одни и те же пакеты, могли совместно использовать файлы DLL. (В отличие от статической компоновки библиотек, которая фактически создает новую копию RTS и всех библиотек для каждого сгенерированного исполняемого файла.)

    Это то же самое, что динамическая компоновка на других платформах, и это описано в Использование общих библиотек.

  • Вы можете упаковать полную Haskell-программу в DLL, чтобы ее вызывал какой-либо внешний (обычно не Haskell) программа. Это обычно используется для реализации плагинов и тому подобного, и описано ниже.

13.7.1. Создание DLL-библиотеки

Создание DLL-библиотеки Win32 - упаковка вашей Haskell-библиотеки в DLL проста; скомпилируйте объектные файлы, составляющие библиотеку, а затем создайте DLL, выполнив команду в формате:

ghc -shared -o foo.dll bar.o baz.o wibble.a -lfooble

Передавая компилятору ghc опцию -shared, он создаст DLL-библиотеку вместо исполняемого файла. DLL будет состоять из всех объектных файлов и архивов, указанных в командной строке.

Несколько замечаний:

  • По умолчанию, точки входа всех объектных файлов будут экспортированы из DLL при использовании -shared. Если вы хотите ограничить это, вы можете указать файл определения модуля для использования в командной строке следующим образом:

    ghc -shared -o .... MyDef.def
    

    См. документацию Microsoft для получения подробностей, но файл определения модуля просто перечисляет точки входа, которые вы хотите экспортировать. Вот один, подходящий для создания DLL-сервера COM Haskell:

    EXPORTS
     DllCanUnloadNow     = DllCanUnloadNow@0
     DllGetClassObject   = DllGetClassObject@12
     DllRegisterServer   = DllRegisterServer@0
     DllUnregisterServer = DllUnregisterServer@0
    
  • В дополнение к созданию DLL, опция -shared также создает библиотеку импорта. Имя библиотеки импорта выводится из имени DLL следующим образом:

    DLL: HScool.dll  ==> import lib: libHScool.dll.a
    

    Схема именования может показаться немного странной, но она предназначена для обеспечения сосуществования библиотек импорта с обычными статическими библиотеками (например, libHSfoo.a и libHSfoo.dll.a. Кроме того, когда компилятор связывает нестатически, он перепишет вхождение -lHSfoo в командной строке на -lHSfoo.dll. Сделав это за вас, переход от нестатической к статической компоновке сводится к добавлению -static в вашу командную строку.

13.7.2. Создание DLL-библиотек для вызова из других языков

Этот раздел описывает, как создавать DLL-библиотеки, которые можно вызывать из других языков, таких как Visual Basic или C++. Это частный случай Создание Haskell-библиотеки, которую можно вызывать из внешнего кода; ниже мы рассмотрим возникающие проблемы, специфичные для DLL. Вот пример:

Используйте объявления внешнего экспорта для экспорта функций Haskell, которые вы хотите вызывать извне. Например:

-- Adder.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Adder where

adder :: Int -> Int -> IO Int  -- gratuitous use of IO
adder x y = return (x+y)

foreign export ccall adder :: Int -> Int -> IO Int

Добавьте некоторый вспомогательный код, который запускает и завершает Haskell RTS:

// StartEnd.c
#include <Rts.h>

void HsStart()
{
   int argc = 1;
   char* argv[] = {"ghcDll", NULL}; // argv must end with NULL

   // Initialize Haskell runtime
   char** args = argv;
   hs_init(&argc, &args);
}

void HsEnd()
{
   hs_exit();
}

Здесь Adder - имя корневого модуля в дереве модулей (как упоминалось выше, должен быть один корневой модуль, и, следовательно, одно дерево модулей в DLL). Скомпилируйте всё:

ghc -c Adder.hs
ghc -c StartEnd.c
ghc -shared -o Adder.dll Adder.o Adder_stub.o StartEnd.o

Теперь файл Adder.dll можно использовать из других языков программирования. Перед вызовом любых функций в Adder необходимо вызвать HsStart, а в самом конце вызвать HsEnd.

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

Возможно, вам покажется заманчивым использовать DllMain для вызова hs_init/hs_exit, но это не сработает (особенно если вы компилируете с -threaded). Существуют строгие ограничения на действия, которые можно выполнять во время DllMain, и hs_init нарушает эти ограничения, что может привести к зависанию вашей DLL во время запуска (см. #3605).

13.7.2.1. Использование из VBA

Пример использования Adder.dll из VBA:

Private Declare Function Adder Lib "Adder.dll" Alias "adder@8" _
      (ByVal x As Long, ByVal y As Long) As Long

Private Declare Sub HsStart Lib "Adder.dll" ()
Private Declare Sub HsEnd Lib "Adder.dll" ()

Private Sub Document_Close()
HsEnd
End Sub

Private Sub Document_Open()
HsStart
End Sub

Public Sub Test()
MsgBox "12 + 5 = " & Adder(12, 5)
End Sub

В этом примере используются функции Document_Open/Close Microsoft Word, но при условии, что HsStart вызывается перед первой функцией, а HsEnd — после последней, всё будет работать нормально.

13.7.2.2. Использование из C++

Пример использования Adder.dll из C++:

// Tester.cpp
#include "HsFFI.h"
#include "Adder_stub.h"
#include <stdio.h>

extern "C" {
    void HsStart();
    void HsEnd();
}

int main()
{
    HsStart();
    // can now safely call functions from the DLL
    printf("12 + 5 = %i\n", adder(12,5))    ;
    HsEnd();
    return 0;
}

Это можно скомпилировать и запустить с помощью:

$ ghc -o tester Tester.cpp Adder.dll.a
$ tester
12 + 5 = 17

© 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/9.12.1/docs/users_guide/win32-dlls.html

Spec-Zone.ru

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