Что мне не нравится в ocamlbuild.
Наконец, теперь можно написать про ocamlbuild.
Казалось бы, ocamlbuild весь из себя красив и пушист, и в простом
случае можно сказать "ocamlbuild main.native" и в две секунды получить
работающий бинарник - чего еще желать простому разработчику?.
Мои претензии к нему заключаются в следующем:
1. Информация о сборке размазывается по отдельным файлам: состав
библиотеки помещается в libname.mllib или .mlpack, куча метаданных
помещается в _tags, правила сборки помещаются в myocamlbuild.ml.
Читать это сложнее, чем читать среднестатистический Makefile или его
аналог.
2. Всегда надо запускать ocamlbuild только в корне проекта. Если у вас
развесистый проект, и вы редактируется исходники в foo/bar/baz/bah, вы
не можете взять и запустить ocamlbuild прямо там. Надо пойти в корень
проекта и сделать ocamlbuild foo/bar/baz/bah/main.native
3. Чтобы собрать все-все-все в проекте, в котором N библиотек и M
бинарников, надо либо указать их все в качестве аргумента для
ocamlbuild, либо создать в корне файл .itarget, в котором перечислены
все 100500 целей. К сожалению, лично у меня это почему-то периодически
не работало :(
4. Чтобы собрать .cmi модуля, у которого нет .mli, ocamlbuild зовет
компилятор два раза - один раз ocamlc для сборки .cmi, второй раз
ocamlopt/ocamlc для сборки .cmx или .cmo. Если таких файлов много -
это выливается в ощутимый проигрыш по времени.
5. Ocamlbuild использует ocamldep, и в результате из-за особенностей
работы последнего ocamlbuild может пересобирать больше, чем реально
требуется. Хорошо хоть, что ocamlbuild знает про все "детские болезни"
ocamldep, и пытается по мере сил их компенсировать. В частности,
ocamlbuild корректно обрабатывает ситуации, в которых .o меняется, а
.cmx - нет
6. Ocamlbuild пересобирает модули, когда в этом нет необходимости.
Напрмер, у нас есть main.ml и test.ml, и они оба используют test.ml. Я
неоднократно наблюдал, как при вызове "ocamlbuild main.native &&
ocamlbuild test.native" модуль test.ml компилируется дважды. Это
происходит не всегда, и я так и не докопался, почему. В то же время,
при "ocamlbuild main.native test.native" этого никогда не происходит(?)
Bottom line: перевести большой существующий проект на ocamlbuild -
морока еще та. Эксперименты на среднего размера подмножестве
исходников показали, что надо будет много всего делать руками (писать
.mllib, писать _tags, создавать свой myocamlbuild.ml, возможно
ковыряться в самом ocamlbuild). В случае проблем приходится копаться в
трех слоях оберток - в ocamldep, в ocamlfind и разбираться с тем, как
работает ocamlopt.
Казалось бы, ocamlbuild весь из себя красив и пушист, и в простом
случае можно сказать "ocamlbuild main.native" и в две секунды получить
работающий бинарник - чего еще желать простому разработчику?.
Мои претензии к нему заключаются в следующем:
1. Информация о сборке размазывается по отдельным файлам: состав
библиотеки помещается в libname.mllib или .mlpack, куча метаданных
помещается в _tags, правила сборки помещаются в myocamlbuild.ml.
Читать это сложнее, чем читать среднестатистический Makefile или его
аналог.
2. Всегда надо запускать ocamlbuild только в корне проекта. Если у вас
развесистый проект, и вы редактируется исходники в foo/bar/baz/bah, вы
не можете взять и запустить ocamlbuild прямо там. Надо пойти в корень
проекта и сделать ocamlbuild foo/bar/baz/bah/main.native
3. Чтобы собрать все-все-все в проекте, в котором N библиотек и M
бинарников, надо либо указать их все в качестве аргумента для
ocamlbuild, либо создать в корне файл .itarget, в котором перечислены
все 100500 целей. К сожалению, лично у меня это почему-то периодически
не работало :(
4. Чтобы собрать .cmi модуля, у которого нет .mli, ocamlbuild зовет
компилятор два раза - один раз ocamlc для сборки .cmi, второй раз
ocamlopt/ocamlc для сборки .cmx или .cmo. Если таких файлов много -
это выливается в ощутимый проигрыш по времени.
5. Ocamlbuild использует ocamldep, и в результате из-за особенностей
работы последнего ocamlbuild может пересобирать больше, чем реально
требуется. Хорошо хоть, что ocamlbuild знает про все "детские болезни"
ocamldep, и пытается по мере сил их компенсировать. В частности,
ocamlbuild корректно обрабатывает ситуации, в которых .o меняется, а
.cmx - нет
6. Ocamlbuild пересобирает модули, когда в этом нет необходимости.
Напрмер, у нас есть main.ml и test.ml, и они оба используют test.ml. Я
неоднократно наблюдал, как при вызове "ocamlbuild main.native &&
ocamlbuild test.native" модуль test.ml компилируется дважды. Это
происходит не всегда, и я так и не докопался, почему. В то же время,
при "ocamlbuild main.native test.native" этого никогда не происходит(?)
Bottom line: перевести большой существующий проект на ocamlbuild -
морока еще та. Эксперименты на среднего размера подмножестве
исходников показали, что надо будет много всего делать руками (писать
.mllib, писать _tags, создавать свой myocamlbuild.ml, возможно
ковыряться в самом ocamlbuild). В случае проблем приходится копаться в
трех слоях оберток - в ocamldep, в ocamlfind и разбираться с тем, как
работает ocamlopt.