Как получить доступ к информации cabal/Setup.hs из моих исходных файлов Haskell?

Мне нужно использовать некоторую информацию уровня Cabal из моих исходных файлов Haskell .hs.

Например, получение пути к каталогу сборки dist/ (configDistPref; я не хочу жестко кодировать dist/), чтобы я мог искать в нем некоторое содержимое, используя runIO в TemplateHaskell.

Похоже, что из обычных файлов Haskell я не могу получить доступ к информации уровня Cabal.

Какими способами я могу перенести эту информацию из пользовательского файла Setup.hs в мои исходные файлы Haskell?


person nh2    schedule 05.02.2018    source источник


Ответы (1)


Похоже, что у Cabal и ghc нет готовых функций, позволяющих узнать ваш каталог dist/ или аналогичную информацию. Вот несколько способов сделать это вручную:

Решение 1. Сериализация всей информации Кабалы в известное место

Одно из решений показано cabal- набор инструментов библиотека.

Это дает вам модификатор ловушки userHooksWithBuildInfo :: UserHooks -> UserHooks, который вы вызываете в своем Setup.hs, который сериализует LocalBuildInfo (который включает всю важную информацию уровня Cabal, включая ConfigFlags{ configDistPref }) в точечный файл (называемый .lbi.buildinfo) в исходном каталоге.

Затем он предоставляет функцию TemplateHaskell localBuildInfoTypedQ :: Q (TExp LocalBuildInfo), которая читает этот файл, чтобы вы могли загрузить информацию в TH.

(В версии Hackage, на которую я ссылался выше, вероятно, отсутствует вызов addDependentFile здесь, чтобы заметить, когда сериализованная информация изменяется, но версия Github уже имеет исправить для этого).


Однако, если вы хотите, чтобы временные файлы, подобные этому, оказывались в каталоге dist/, это также не поможет (поскольку, как уже упоминалось, точечный файл находится на верхнем уровне вашего проекта, и вы должны добавить его, например, в .gitignore).

Решение 2. Передача конкретной информации в GHC с использованием флагов -D

Через confHook, который вызывает настройку клики с помощью "-ghc-options=-D__CPP_CABAL_DIST_DIR__=" ++ configDistPref. Пример для вашего файла Setup.hs:

main = do
  defaultMainWithHooks $
    simpleUserHooks
      { confHook = \inputs configFlags@ConfigFlags{ configDistPref = configDistPref } -> do
          putStrLn "In Cabal configure hook"

          let distDir = fromFlagOrDefault "dist" configDistPref

          (confHook simpleUserHooks) inputs (configFlags{ configProgramArgs = ("ghc", ["-D__CPP_CABAL_DIST_DIR__=" ++ distDir]) : configProgramArgs configFlags })
      }

Тогда единственная трудность состоит в том, чтобы на самом деле вставить макрос __CPP_CABAL_DIST_DIR__ в код, потому что вы не можете написать "__CPP_CABAL_DIST_DIR__" в строковом литерале Haskell, который не будет заменен CPP, поэтому вам нужно использовать строковую квазикавычку, например этот, например [r|__CPP_CABAL_DIST_DIR__] только для того, чтобы вставить этот макрос; обычный способ выполнения CPP строкинга с #__CPP_CABAL_DIST_DIR__ не не работает в CPP ghc, как описано здесь:

Есть три основные причины, по которым код с CPP может работать не так, как ожидалось:

Вы использовали #, ## или __VA_ARGS__. GHC запускает CPP в традиционном режиме, который отключает все дополнительные функции.

Решение 3. Передача конкретной информации путем создания модуля Haskell с препроцессором

Вы можете добавить hookedPreProcessors к своим крючкам в Setup.hs. В этом хуке вы визуализируете как литералы или строки Haskell любую информацию от Cabal, к которой вы хотите получить доступ.

Пример написания препроцессора можно найти здесь в документации Cabal или, что еще понятнее, в этом ответе.

Не забудьте добавить модуль, который будет сгенерирован вашим препроцессором, в exposed-modules или other-modules в вашем файле .cabal, и поместите файл, соответствующий расширению препроцессора, в ваш проект, иначе препроцессор не создаст файл .hs.


В своих собственных случаях я использую Решение 3, потому что оно чистое, не требует CPP или магии TemplateHaskell, дает хорошие сообщения об ошибках и не требует для запуска эквивалента runhaskell Setup.hs configure, build достаточно, потому что препроцессоры запускаются во время сборки.

person nh2    schedule 05.02.2018