Stack (Haskell) сборка кеша исходных файлов с помощью GitHub Actions - PullRequest
7 голосов
/ 28 марта 2020

При создании моего Haskell проекта локально с использованием stack build, только измененные исходные файлы перекомпилируются. К сожалению, я не могу заставить стек работать так на GitHub Actions. Любые предложения, пожалуйста?

Пример

Я создал простой пример с Lib.hs и Fib.hs, я даже проверяю, что кешированная папка .stack-work обновляется между компилируется, но всегда компилирует оба файла, даже когда изменяется только один.

Вот пример:

  1. (кэш не используется, создаются зависимости Lib.hs и Fib.hs + ): https://github.com/MarekSuchanek/stack-test/runs/542163994
  2. (только Lib.hs изменяется, строит как Lib.hs, так и Fib.hs): https://github.com/MarekSuchanek/stack-test/runs/542174351

Я могу наблюдать из журналов (многословный стек), что что-то в кеше обновляется, но мне совершенно не понятно, что и почему. Он правильно обнаруживает, что изменено только Lib.hs: "stack-test-0.1.0.0: unregistering (local file changes: src/Lib.hs)", поэтому я не могу понять, почему все компилируется. Я заметил, что в 2. Fib.hi не обновляется в .stack-work, но другие (Fib.o, Fib.dyn_hi и Fib.dyn_o).

Примечание

Кэширование ~ / .stack в порядке, а также отсутствие сборки, если исходный файл не изменен. Конечно, это фиктивный пример, но у нас есть разные проекты с большим количеством исходных файлов, где это значительно ускорит сборку. При изменении файла, не являющегося исходным кодом (например, файла README), ничего не создается должным образом.

1 Ответ

3 голосов
/ 13 апреля 2020

Причиной этой проблемы является то, что стек использует метку времени (как и многие другие инструменты), чтобы выяснить, изменился ли исходный файл или нет. Когда вы восстанавливаете кэш на CI и делаете это правильно, ни одна из зависимостей не будет восстановлена, но проблема с исходными файлами состоит в том, что, когда поставщик CI клонирует для вас репо, устанавливаются временные метки для всех файлов в репо. к дате и времени, когда он был клонирован.

Надеемся, что причина для перекомпиляции неизмененных исходных файлов теперь имеет смысл. Что мы делаем для решения этой проблемы. Единственный реальный способ получить это - восстановить временную метку последнего git коммита, который изменил определенный файл. Я заметил это довольно давно go и немного погуглил дал мне несколько ответов по SO, вот один из них, я думаю: Восстановите время модификации файла в Git

Я немного изменил его, чтобы соответствовать моим потребностям, и вот что я в итоге получил:

  git ls-tree -r --name-only HEAD | while read filename; do
    TS="$(git log -1 --format="%ct" -- ${filename})"
    touch "${filename}" -mt "$(date --date="@$TS" "+%Y%m%d%H%M.%S")"
  done

Этот работник отлично подходит для меня на Ubuntu CI, но решает эту проблему в зависимости от операционной системы c Способ с bash - это не то, что я хотел сделать, когда мне нужно было настроить Azure CI. По этой причине я написал скрипт Haskell, который работает для всех версий GH C -8.2 и новее, не требуя каких-либо неосновных зависимостей. Я использую его для всех своих проектов, и я добавлю сюда его сок, но также предоставлю ссылку на постоянный гист :

main = do
  args <- getArgs
  let rev = case args of
        [] -> "HEAD"
        (x:_) -> x
  fs <- readProcess "git" ["ls-tree", "-r", "-t", "--full-name", "--name-only", rev] ""
  let iso8601 = iso8601DateFormat (Just "%H:%M:%S%z")
      restoreFileModtime fp = do
        modTimeStr <- readProcess "git" ["log", "--pretty=format:%cI", "-1", rev, "--", fp] ""
        modTime <- parseTimeM True defaultTimeLocale iso8601 modTimeStr
        setModificationTime fp modTime
        putStrLn $ "[" ++ modTimeStr ++ "] " ++ fp
  putStrLn "Restoring modification time for all these files:"
  mapM_ restoreFileModtime $ lines fs

Как бы вы go об использовании его без особых накладных расходов. Хитрость заключается в том, чтобы:

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

Выше двух пунктов гарантирует, что никакие избыточные зависимости или версии gh c не будут установлены. В общем, нужны только две вещи: stack и что-то вроде curl или wget, и он будет работать кроссплатформенно:

# Script for restoring source files modification time from commit to avoid recompilation.
curl -sSkL https://gist.githubusercontent.com/lehins/fd36a8cc8bf853173437b17f6b6426ad/raw/4702d0252731ad8b21317375e917124c590819ce/git-modtime.hs -o git-modtime.hs
# Restore mod time and setup ghc, if it wasn't restored from cache
stack script --resolver ${RESOLVER} git-modtime.hs --package base --package time --package directory --package process

Вот реальный проект, который использует этот подход, и вы можете просмотреть его, чтобы увидеть, как он работает: massiv-io

Редактировать @Simon Michael в комментариях упомянул, что он не может воспроизвести эту проблему локально. Причиной этого является то, что не все так же, как на локальном уровне. Довольно часто абсолютный путь отличается, например, возможно, другие вещи, о которых я не могу думать прямо сейчас. Эти вещи вместе с отметкой времени исходного файла вызывают перекомпиляцию исходных файлов.

Например, следуйте этим шагам, и вы обнаружите, что ваш проект будет перекомпилирован:

~/tmp$ git clone git@github.com:fpco/safe-decimal.git
~/tmp$ cd safe-decimal
~/tmp/safe-decimal$ stack build
safe-decimal> configure (lib)
[1 of 2] Compiling Main
...
Configuring safe-decimal-0.2.0.0...
safe-decimal> build (lib)
Preprocessing library for safe-decimal-0.2.0.0..
Building library for safe-decimal-0.2.0.0..
[1 of 3] Compiling Numeric.Decimal.BoundedArithmetic
[2 of 3] Compiling Numeric.Decimal.Internal
[3 of 3] Compiling Numeric.Decimal
...
~/tmp/safe-decimal$ cd ../
~/tmp$ mv safe-decimal safe-decimal-moved
~/tmp$ cd safe-decimal-moved/
~/tmp/safe-decimal-moved$ stack build
safe-decimal-0.2.0.0: unregistering (old configure information not found)
safe-decimal> configure (lib)
[1 of 2] Compiling Main
...

Вы будете Видно, что расположение проекта вызвало проект здания. Несмотря на то, что сам проект был перестроен, вы заметите, что ни один из исходных файлов не был перекомпилирован. Теперь, если вы объедините эту процедуру с touch исходного файла, этот исходный файл будет перекомпилирован.

Подводя итог:

  • Среда может привести к тому, что проект будет rebuild
  • Содержимое исходного файла может привести к перекомпиляции исходного файла (и других, зависящих от него)
  • Среда вместе с содержимым исходного файла или изменение метки времени может вызвать проект вместе с этот исходный файл для перекомпиляции
...