Как я уже говорил,
ocamldep делает слишком много допущений и пытается быть хитрее, чем на
самом деле нужно.
Например, ocamlopt при компиляции реализации модуля baz.ml генерирует объектный файл baz.o и метаданные baz.cmx.
Ocamldep это знает, что считает, что всем зависимым модулям достаточно
прописать зависимость _только_ от baz.cmx - мол, один фиг оба файла
генерируются одновременно.
Смотрите, к чему это приводит
То есть, я поменял исходный файл, перекомпилировал его, baz.o
изменился, а baz.cmx остался точно таким же. Для make это все равно -
он смотрит только на время модификации файла, поэтому make может
использовать информацию о зависимостях, которую генерирует ocamldep.
Для ocamlbuild это тоже не страшно, так как он содержит кусок кода,
который явно исправляет этот баг.
Но если у вас не make и не ocamlbuild, а какая-то другая система
сборки, которая проверяет еще и md5sum файла, то она решит, что модуль
не поменялся, и не будет пересобирать все зависимые модули! Учитывая,
что интерфейс модуля вобщем-то не поменялся (см. md5sum файла
baz.cmi), это решение выглядит логичным - в конце-концов новая
реализаця понадобится только на этапе линковки, правильно?
Неправильно! Из-за cross-module inlining _нужно_ пересобирать все
зависимые модули, иначе при линковке будет ошибка о "inconsistent
assumptions over interface Blah". А отключить inlining для ускорения
dev build-ов - нельзя.
Что же мы имеем в сухом остатке? Ocamldep пытается абстрагировать нас
от подробностей процесса сборки, но получается только хуже. Вместо
облегчения процесса сборки он его усложняет - приходится держать в уме
и подробности о том, как работает компилятор, и о том, что умалчивает
ocamldep. Leaky abstractions в чистом виде.
Вывод: инструменты более низкого уровня, чем ocamlbuild -
ocamlbuild и непосредственно компилятор ocaml - использовать
противопоказано, посколько в них полно мелких и крупных косяков,
которые исправляются только на следующих уровнях абстракции.
А никакого другого инструмента, кроме ocamldep - нет.
ocamldep делает слишком много допущений и пытается быть хитрее, чем на
самом деле нужно.
Например, ocamlopt при компиляции реализации модуля baz.ml генерирует объектный файл baz.o и метаданные baz.cmx.
Ocamldep это знает, что считает, что всем зависимым модулям достаточно
прописать зависимость _только_ от baz.cmx - мол, один фиг оба файла
генерируются одновременно.
Смотрите, к чему это приводит
adept> echo 'let baz = "baz"' > baz.ml adept> ocamlopt -c baz.ml adept> md5sum baz.c* baz.o ca6ab45ef85dd930e7f8e29ee4eddf8f baz.cmi 617e472bd3854192a30c443e313b2757 baz.cmx 894e61c407ac77f5b5f813008aec38e3 baz.o
adept> echo 'let baz = "baz baz baz"' > baz.ml adept> ocamlopt -c baz.ml adept> md5sum baz.c* baz.o ca6ab45ef85dd930e7f8e29ee4eddf8f baz.cmi 617e472bd3854192a30c443e313b2757 baz.cmx d05d63f7d7fa0f638982ef3d8a0eacf0 baz.o
То есть, я поменял исходный файл, перекомпилировал его, baz.o
изменился, а baz.cmx остался точно таким же. Для make это все равно -
он смотрит только на время модификации файла, поэтому make может
использовать информацию о зависимостях, которую генерирует ocamldep.
Для ocamlbuild это тоже не страшно, так как он содержит кусок кода,
который явно исправляет этот баг.
Но если у вас не make и не ocamlbuild, а какая-то другая система
сборки, которая проверяет еще и md5sum файла, то она решит, что модуль
не поменялся, и не будет пересобирать все зависимые модули! Учитывая,
что интерфейс модуля вобщем-то не поменялся (см. md5sum файла
baz.cmi), это решение выглядит логичным - в конце-концов новая
реализаця понадобится только на этапе линковки, правильно?
Неправильно! Из-за cross-module inlining _нужно_ пересобирать все
зависимые модули, иначе при линковке будет ошибка о "inconsistent
assumptions over interface Blah". А отключить inlining для ускорения
dev build-ов - нельзя.
Что же мы имеем в сухом остатке? Ocamldep пытается абстрагировать нас
от подробностей процесса сборки, но получается только хуже. Вместо
облегчения процесса сборки он его усложняет - приходится держать в уме
и подробности о том, как работает компилятор, и о том, что умалчивает
ocamldep. Leaky abstractions в чистом виде.
Вывод: инструменты более низкого уровня, чем ocamlbuild -
ocamlbuild и непосредственно компилятор ocaml - использовать
противопоказано, посколько в них полно мелких и крупных косяков,
которые исправляются только на следующих уровнях абстракции.
А никакого другого инструмента, кроме ocamldep - нет.
(no subject)
Date: 2011-11-11 12:50 pm (UTC)Собственно и зависимость от .cmi не указывает все по той же причине - связка с make.
Там довольно нетривиально с заданием правил, генерирующих более чем один целевой файл одновременно.
(no subject)
Date: 2011-11-11 01:27 pm (UTC)(no subject)
Date: 2011-11-11 01:33 pm (UTC)Хочется взять какой-нибудь tup или redo или scons и собирать ocaml.
Без информации о зависимостях это делать нелья (ибо компилятор туп).
Без разбора исходников это делать нелья (т.к. язык сложный и, грубо говоря, регекспами это не сделаешь).
Единственная официальная идущая вместе с компилятором утилита для анализа зависимостей решает не общую задачу, а какую-то узкоспециальную, которая за пределом этой ниши нафиг не нужна. Отсюда вывод про то, что утилита - полная фигня.
Бонусом идет то, что собирать с приемлимым уровнем комфорта с помощью make любой сложный проект на ocaml, вобщем-то, практически нереально. Из-за того, что make не умеет зачитывать информацию о зависимостях на ходу.
То есть, повторюсь, проблема ocamldep именно в том, что он ориентирован на make. Из более подробного вывода можно было бы "натупить" правила для make, а вот наоборот сделать не получается.
(no subject)
Date: 2011-11-11 01:40 pm (UTC)В предыдущем посте из этой серии мне сообщили, что, вроде как, код на OCaml не нужно собирать ничем, кроме ocamlbuild. Я так понимаю, никаких предпосылок для этого в спецификации языка нет, а есть проблемы с реализацией -- ну так почему бы их не поправить вместо того, чтобы городить ocamlbuild?
(no subject)
Date: 2011-11-11 04:50 pm (UTC)Претензия имхо очень странная. Абсолютно любая программа за пределами своей ниши нафиг никому не нужна.
Программа делает то, на что расчитана. ocamldep как следует из его документации - генератор make-файлов.
То есть, повторюсь, проблема ocamldep именно в том, что он ориентирован на make.
Это не его проблема, это проблема тех, кто не пользуется make.
Для любителей странного есть ocamldep -modules который вполне поддается парсингу регэкспами.
(no subject)
Date: 2011-11-11 05:07 pm (UTC)$ cat A.hs module A where a = "foo" $ ghc -O2 -ddump-hi A.hs [1 of 1] Compiling A ( A.hs, A.o ) ==================== FINAL INTERFACE ==================== interface main:A 7002 interface hash: 4d16d97acc6ad66b377fa6561c4bb5db ABI hash: b5a50b19d49d3f75363a8d9e8dfe75f3 export-list hash: 6dc11cf3948bebe8a7423d65a84dc7f0 orphan hash: 693e9af84d3dfcc71e640e005bdc5e2e where export main:A a module dependencies: package dependencies: base ghc-prim integer-gmp orphans: base:GHC.Base base:GHC.Float base:GHC.Num family instance modules: import base:Prelude 6cb91ac29334c0836b27c462c75636c7 f34b0179b148d8bcb96cf9d6533ba4cd a :: [GHC.Types.Char] {- Unfolding: (GHC.Base.unpackCString# "foo") -} vectorised variables: vectorised tycons: vectorised reused tycons:(no subject)
Date: 2011-11-11 05:09 pm (UTC)На самом деле, в GNU make (а может, и не только в нём) есть на эту тему костыль. Он состоит вот в чём: если в Makefile есть include или -include, и ещё есть правило для делания файла, который include'ится, то тогда, если это правило говорит, что этот файл нужно переделать, то после его переделки make перезапускается и подсасывает новый файл.
(no subject)
Date: 2011-11-11 05:24 pm (UTC)Компилятор сам не компилирует, ocamldep сделан абы как (что с -modules, что без).
(no subject)
Date: 2011-11-11 05:27 pm (UTC)Ага?
(no subject)
Date: 2011-11-11 05:31 pm (UTC)(no subject)
Date: 2011-11-11 05:40 pm (UTC)Ну или на худой конец можно заставить make запускать генерацию зависимостей на совсем каждый запуск, например:
(no subject)
Date: 2011-11-11 05:57 pm (UTC)make parser.ml
make depend
(no subject)
Date: 2011-11-12 08:51 am (UTC)(no subject)
Date: 2011-11-12 03:28 pm (UTC)(no subject)
Date: 2011-11-14 07:44 am (UTC)Да, и разумеется это не однократный запуск. Это приходится делать перед каждой сборкой.
(no subject)
Date: 2011-11-14 07:47 am (UTC)(no subject)
Date: 2011-11-14 08:10 am (UTC)(no subject)
Date: 2011-11-14 08:25 am (UTC)Иначе, если поменялся parser.mll или lexer.mly, зависимости будут неправильные.
(no subject)
Date: 2011-11-14 08:29 am (UTC)Если вам это действительно надо (мне вот ни разу не понадобилось) - как уже сказали выше, пускать make depend автоматически при каждой сборке.
(no subject)
Date: 2011-11-14 08:35 am (UTC)Особенно чудесно будет, если в результате изменения какой-то автогенерируемый файл перестал существовать (разработчик его удалил). В этом случае даже make depend не спасет. (Да, я знаю, что мы уже начинаем переходить в область обсуждения чистого make)
(no subject)
Date: 2011-11-14 08:47 am (UTC)(no subject)
Date: 2011-11-14 09:03 am (UTC)Да, например - использовать что-то другое место make. Но в случа с ocaml это сложно, так как ocaml => ocamldep => только make
(no subject)
Date: 2011-11-14 09:07 am (UTC)(no subject)
Date: 2011-11-14 09:09 am (UTC)(no subject)
Date: 2011-11-14 09:16 am (UTC)(no subject)
Date: 2011-11-14 09:25 am (UTC)(no subject)
Date: 2011-11-14 09:30 am (UTC)