Избегание оракула ИЛИ разделение оракулов одного типа

У меня следующая ситуация:

  • find-deps — это внешняя программа, которая очень быстро запускается и обнаруживает информацию о зависимостях, аналогичную ghc -M. Его вывод — некоторый файл deps.
  • compile — это внешняя программа, которая работает очень медленно; в отличие от ghc --make, он очень медленный, даже если ни один из входных данных не изменился.

Таким образом, идея состоит в том, чтобы добавить правило Shake, которое запускает find-deps для создания deps, анализирует его в список файлов srcs, а затем правило компиляции будет need srcs гарантировать, что compile будет повторно запущен только в том случае, если какой-либо из источников, обнаруженных find-deps, измененный.

Сложность заключается в том, что find-deps нужно alwaysRerun, чтобы обнаруживать новые зависимые исходные файлы. Так что теперь, если правило compile зависит от deps для получения списка файлов, оно также будет alwaysRerun. Стандартным решением было бы использование оракула: мы можем добавить оракул, который needs deps и анализирует его в список файлов, а затем правило compile сначала запросит этот список исходных файлов и только need их. Таким образом, в цепочке need из compile нет alwaysRerun.

Однако в моем случае я не пишу конкретный Shakefile. Вместо этого я пишу библиотеку многократного использования Rules, которую пользователи могут использовать для создания своего собственного основного Shakefile. Так что мне нужно было бы упаковать его как что-то вроде

myRules :: FilePath -> Rules ()
myRules dir = do
    dir </> "deps" %> \depFile -> do
        alwaysRerun
        cmd_ (Cwd dir) "find-deps" ["-o", depFile]

    dir </> "exe" %> \exeFile -> do
        srcs <- askOracle $ Sources dir
        need srcs
        cmd_ (Cwd dir) "compile" ["-o", exeFile]

Но куда бы я поместил часть addOracle $ \Sources dir -> ..., которая бы need [dir </> "deps"] проанализировала ее и вернула список исходных файлов? Я не могу поместить его в rules, потому что тогда два вызова rules с разными каталогами попытаются установить обработчик оракула два раза для одного и того же типа. И я не могу сделать dir частью типа вопроса оракула, потому что это переменная уровня термина, поэтому я не могу поднять ее в индекс Symbol запроса.

И это оставляет меня с чем-то супер-хромым, например, с includeThisOnlyOnce :: Rules (), который пользователь должен не забыть включить ровно один раз в свой Shakefile.

Итак, мой вопрос:

  • Есть ли способ отслеживать зависимости (т. е. избегать запуска compile, когда исходные файлы не изменились) без участия оракула?
  • В качестве альтернативы, есть ли способ разделить оракулы одного типа, каким-то образом определив их область действия, чтобы я мог добавить этот Sources оракул только в контекст каждого отдельного вызова myRules someDir?

person Cactus    schedule 08.10.2020    source источник


Ответы (1)


Есть ли способ отслеживать зависимости без участия оракула?

Да - если вывод find-deps вообще не изменится, то он не будет перестраивать compile. Этого можно добиться, указав значение Change, например ChangeModtimeAndDigest, но это глобальная настройка. В качестве альтернативы вы можете поместить вывод find-deps где-нибудь, например, foo.deps.out, а затем вызвать copyFileChanged "foo.deps.out" "foo.deps", который не будет обновлять метку времени, если файл не изменился.

Есть ли способ разделить оракулы одного типа?

Не легко и сразу, хотя я понимаю, почему это полезно. Я могу придумать два возможных пути решения этой проблемы:

  1. Можно было бы добавить addOracleIdempotent, который игнорировал любые ошибки о повторном добавлении одного и того же оракула. Это довольно простое изменение в Shake (по сути, установите флаг в Rules, чтобы игнорировать дубликаты).
  2. В качестве альтернативы вы можете попробовать повысить уровень dir до уровня типа и убедиться, что каждый оракул имеет другой тип. Вероятно, это делает ваш API более сложным и требует магии типов.

Из всех этих решений я бы использовал copyFileChanged, так как он простой и локальный.

person Neil Mitchell    schedule 09.10.2020
comment
Я надеялся, что добавление оракула с ограниченной областью действия не будет слишком сложным, tbh :) - person Cactus; 09.10.2020
comment
Для вещей невозможно действительно определить область действия, поскольку все они должны быть доступны вверху при проверке, не нужно ли что-то перезапустить. - person Neil Mitchell; 11.10.2020