From 194a32004c4ad6e1b91a1eacbf0c129dce5cd9f9 Mon Sep 17 00:00:00 2001 From: Julien Blond Date: Mon, 15 Jul 2024 16:24:19 +0200 Subject: [PATCH] Fixed #224: added sites managment. --- .drom | 58 ++++--- .github/workflows/workflow.yml | 2 +- .gitignore | 1 - Makefile.drom | 83 ++++++++++ drom.toml | 8 +- dune-project | 31 +++- opam/drom.opam | 5 +- opam/drom_lib.opam | 9 +- opam/drom_toml.opam | 5 +- scripts/copy-bin.sh | 2 +- scripts/static-build.sh | 6 +- sphinx/reference.rst | 105 +++++++++++- sphinx/usecases.rst | 44 +++++ src/drom/dune | 6 +- src/drom/linking_flags.sh | 3 + src/drom/package.toml | 35 ++-- src/drom_lib/dune | 9 +- src/drom_lib/dune.ml | 22 ++- src/drom_lib/globals.ml | 5 +- src/drom_lib/package.ml | 9 +- src/drom_lib/package.toml | 15 +- src/drom_lib/project.ml | 18 +- src/drom_lib/protocol.ml | 207 +++++++++++++++++++++++ src/drom_lib/protocol.mli | 31 ++++ src/drom_lib/sites.ml | 273 +++++++++++++++++++++++++++++++ src/drom_lib/sites.mli | 30 ++++ src/drom_lib/types.ml | 97 ++++++++++- src/drom_lib/version.mlt | 9 +- src/drom_lib/versionCompare.ml | 3 + src/drom_lib/versionCompare.mli | 3 + src/toml.7.1.0/dune | 7 +- src/toml.7.1.0/package.toml | 7 + src/toml.7.1.0/version.mlt | 9 +- test/expect-tests/dune.drom | 14 ++ test/inline-tests/dune.drom | 13 ++ test/output-tests/dune.drom | 24 +++ test/output-tests/test2.expected | 46 +++++- test/output-tests/test2.ml | 61 ++++++- 38 files changed, 1243 insertions(+), 72 deletions(-) create mode 100644 Makefile.drom create mode 100644 src/drom_lib/protocol.ml create mode 100644 src/drom_lib/protocol.mli create mode 100644 src/drom_lib/sites.ml create mode 100644 src/drom_lib/sites.mli create mode 100644 test/expect-tests/dune.drom create mode 100644 test/inline-tests/dune.drom create mode 100644 test/output-tests/dune.drom diff --git a/.drom b/.drom index a4c74721..84398f89 100644 --- a/.drom +++ b/.drom @@ -5,17 +5,17 @@ version:0.9.0 # hash of toml configuration files # used for generation of all files -cfa41e62814087cf2f340eab496a6542:. +cfd502831bbf686b41df41219629c1c4:. # end context for . # begin context for .github/workflows/workflow.yml # file .github/workflows/workflow.yml -533318fb1c01b0861406a644a7d9f39c:.github/workflows/workflow.yml +eee26f5d13b222b48706c4fcbd98a19a:.github/workflows/workflow.yml # end context for .github/workflows/workflow.yml # begin context for .gitignore # file .gitignore -33c88a25e8b40f3683f6c03ec26be5db:.gitignore +f7a24abcad0ba9301c03bb64464b1718:.gitignore # end context for .gitignore # begin context for .ocamlformat @@ -48,6 +48,11 @@ cfa41e62814087cf2f340eab496a6542:. 9018881cf0682f4f7f262417b0d4f9ff:Makefile # end context for Makefile +# begin context for Makefile.drom +# file Makefile.drom +2b07c62bfabf8f45733eafbc696c6146:Makefile.drom +# end context for Makefile.drom + # begin context for README.md # file README.md 616ddd2660258447be65bdb664b9241d:README.md @@ -90,7 +95,7 @@ c8281f46ba9a11d0b61bc8ef67eaa357:docs/style.css # begin context for drom.toml # file drom.toml -b7b7b8f4a370e3c1b425b7a9316c3e0f:drom.toml +9949d65b87e55480a30122e9e77ae7d3:drom.toml # end context for drom.toml # begin context for dune @@ -100,22 +105,22 @@ e850a13c004f963e9f5a568eac93c217:dune # begin context for dune-project # file dune-project -47416b4e7b7e7febc8da32932cae0d54:dune-project +c063288437da29a5a21e8c76c1906adc:dune-project # end context for dune-project # begin context for opam/drom.opam # file opam/drom.opam -d3c8882f1742f865c2987dea6c7c83e2:opam/drom.opam +ff84198ff87fa46b8ac39ed2951d2d26:opam/drom.opam # end context for opam/drom.opam # begin context for opam/drom_lib.opam # file opam/drom_lib.opam -8b323689fcc28f9783fa85ca4a4fac55:opam/drom_lib.opam +788b17212e259bbb8fe7c22c85c76d35:opam/drom_lib.opam # end context for opam/drom_lib.opam # begin context for opam/drom_toml.opam # file opam/drom_toml.opam -81116a1889e6414dee5c2e921961aa77:opam/drom_toml.opam +e55d659410b556d7bc60183d8ff1f788:opam/drom_toml.opam # end context for opam/drom_toml.opam # begin context for scripts/after.sh @@ -130,12 +135,12 @@ d3c8882f1742f865c2987dea6c7c83e2:opam/drom.opam # begin context for scripts/copy-bin.sh # file scripts/copy-bin.sh -bb3a9d286f0dc64021db4194427263ee:scripts/copy-bin.sh +7fe4ada2a2fc5a0ebf9a2f483679a0ae:scripts/copy-bin.sh # end context for scripts/copy-bin.sh # begin context for scripts/static-build.sh # file scripts/static-build.sh -2a405479b3313d79d421c2e7fc282304:scripts/static-build.sh +cda3f8bad69d9c6e8cbeab1c720d739a:scripts/static-build.sh # end context for scripts/static-build.sh # begin context for sphinx/_static/css/fixes.css @@ -170,7 +175,7 @@ a44c87f3a364dd95f55427fe40b2c5d1:sphinx/about.rst # begin context for src/drom/dune # file src/drom/dune -efad88968955023a354f16469eb1ae15:src/drom/dune +68aec4169671c0163b98bb38205d2e4d:src/drom/dune # end context for src/drom/dune # begin context for src/drom/index.mld @@ -180,7 +185,7 @@ efad88968955023a354f16469eb1ae15:src/drom/dune # begin context for src/drom/linking_flags.sh # file src/drom/linking_flags.sh -9a3b02ff1d99723e9ee2f3c0e97d7b30:src/drom/linking_flags.sh +9fdfca3cc53df639758ff04fe09d3243:src/drom/linking_flags.sh # end context for src/drom/linking_flags.sh # begin context for src/drom/main.ml @@ -190,12 +195,12 @@ efad88968955023a354f16469eb1ae15:src/drom/dune # begin context for src/drom/package.toml # file src/drom/package.toml -5bdfaa2663247cad045abb018d576a5e:src/drom/package.toml +bb6803d414fd5a5479ed6381914604de:src/drom/package.toml # end context for src/drom/package.toml # begin context for src/drom_lib/dune # file src/drom_lib/dune -a97be0d7328e7c15eb149c454844646a:src/drom_lib/dune +103b365e9f24dbbae02b6be22b3efd0a:src/drom_lib/dune # end context for src/drom_lib/dune # begin context for src/drom_lib/index.mld @@ -205,17 +210,17 @@ a97be0d7328e7c15eb149c454844646a:src/drom_lib/dune # begin context for src/drom_lib/package.toml # file src/drom_lib/package.toml -18ae084348cbfdbb80ca1469113e0e9f:src/drom_lib/package.toml +c4d6597dc5b0966758c213bfffb9d594:src/drom_lib/package.toml # end context for src/drom_lib/package.toml # begin context for src/drom_lib/version.mlt # file src/drom_lib/version.mlt -fc09abbaf032f775d148928fa1398cf9:src/drom_lib/version.mlt +a3e5d8a00664ceaa6addf376bedf6de1:src/drom_lib/version.mlt # end context for src/drom_lib/version.mlt # begin context for src/toml.7.1.0/dune # file src/toml.7.1.0/dune -229a5f90ef7f812db6f3560ccb3d131f:src/toml.7.1.0/dune +0841a2e47118b9fcd9b900f84ff66c34:src/toml.7.1.0/dune # end context for src/toml.7.1.0/dune # begin context for src/toml.7.1.0/index.mld @@ -230,12 +235,12 @@ d1b05207fce876a1b44a8b268bcaf226:src/toml.7.1.0/index.mld # begin context for src/toml.7.1.0/package.toml # file src/toml.7.1.0/package.toml -706b170edac8a3051745f05ece52fb62:src/toml.7.1.0/package.toml +16c1103a2b8042862d209fc275b52f65:src/toml.7.1.0/package.toml # end context for src/toml.7.1.0/package.toml # begin context for src/toml.7.1.0/version.mlt # file src/toml.7.1.0/version.mlt -fc09abbaf032f775d148928fa1398cf9:src/toml.7.1.0/version.mlt +a3e5d8a00664ceaa6addf376bedf6de1:src/toml.7.1.0/version.mlt # end context for src/toml.7.1.0/version.mlt # begin context for test/expect-tests/dune @@ -243,6 +248,11 @@ fc09abbaf032f775d148928fa1398cf9:src/toml.7.1.0/version.mlt d7e2675a767e30edf28d061c94f0ec62:test/expect-tests/dune # end context for test/expect-tests/dune +# begin context for test/expect-tests/dune.drom +# file test/expect-tests/dune.drom +6d2c689d437f17d4e99e01df3f4515c6:test/expect-tests/dune.drom +# end context for test/expect-tests/dune.drom + # begin context for test/expect-tests/dune_ # file test/expect-tests/dune_ f15fedac6f43c154bcd0348b776ac08c:test/expect-tests/dune_ @@ -258,6 +268,11 @@ f15fedac6f43c154bcd0348b776ac08c:test/expect-tests/dune_ a0d7baa94246a5122e55a255dcefa9d0:test/inline-tests/dune # end context for test/inline-tests/dune +# begin context for test/inline-tests/dune.drom +# file test/inline-tests/dune.drom +4cd12cd1258c38ccd44b225323601ff5:test/inline-tests/dune.drom +# end context for test/inline-tests/dune.drom + # begin context for test/inline-tests/dune_ # file test/inline-tests/dune_ 75d7504f9c8002ec5ccdd2458f01da09:test/inline-tests/dune_ @@ -273,6 +288,11 @@ a0d7baa94246a5122e55a255dcefa9d0:test/inline-tests/dune b5ea41e29d37e997b482b1bf7f5953d7:test/output-tests/dune # end context for test/output-tests/dune +# begin context for test/output-tests/dune.drom +# file test/output-tests/dune.drom +a3371489c9d6aee62aeba112ecfddcf0:test/output-tests/dune.drom +# end context for test/output-tests/dune.drom + # begin context for test/output-tests/dune_ # file test/output-tests/dune_ 28db2068920c1259cbd67a7c322730ed:test/output-tests/dune_ diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index ebbf589a..932539f8 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -25,7 +25,7 @@ jobs: include: - os: ubuntu-latest - ocaml-compiler: 4.07.0 + ocaml-compiler: 4.08.0 skip_test: true diff --git a/.gitignore b/.gitignore index 183b145f..4469f6e8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ *~ _build .merlin -.vscode /_drom /_opam /_build diff --git a/Makefile.drom b/Makefile.drom new file mode 100644 index 00000000..095fa123 --- /dev/null +++ b/Makefile.drom @@ -0,0 +1,83 @@ +# Maintained by "drom project" +.PHONY: $(DROMNS)build $(DROMNS)build-deps $(DROMNS)fmt $(DROMNS)fmt-check +.PHONY: $(DROMNS)install $(DROMNS)dev-deps $(DROMNS)test +.PHONY: $(DROMNS)clean $(DROMNS)distclean + +DROM_DEV_DEPS := merlin ocamlformat odoc ppx_expect ppx_inline_test $(DEV_DEPS) + + +SPHINX_TARGET:=_drom/docs/sphinx + +ODOC_TARGET:=_drom/docs/doc/. + + +$(DROMNS)build: + ./scripts/before.sh build + opam exec -- dune build @install + ./scripts/copy-bin.sh drom drom_lib drom_toml + ./scripts/after.sh build + +$(DROMNS)build-deps: + if ! [ -e _opam ]; then \ + opam switch create . 4.14.1 ; \ + fi + opam install ./opam/*.opam --deps-only + + +.PHONY: $(DROMNS)doc-common $(DROMNS)odoc $(DROMNS)view +.PHONY: $(DROMNS)sphinx +$(DROMNS)doc-common: $(DROMNS)build + mkdir -p _drom/docs + rsync -auv docs/. _drom/docs/. + +$(DROMNS)sphinx: $(DROMNS)doc-common + ./scripts/before.sh sphinx ${SPHINX_TARGET} + sphinx-build sphinx ${SPHINX_TARGET} + ./scripts/after.sh sphinx ${SPHINX_TARGET} + +$(DROMNS)odoc: $(DROMNS)doc-common + mkdir -p ${ODOC_TARGET} + ./scripts/before.sh odoc ${ODOC_TARGET} + opam exec -- dune build @doc + rsync -auv --delete _build/default/_doc/_html/. ${ODOC_TARGET} + ./scripts/after.sh odoc ${ODOC_TARGET} + +$(DROMNS)doc: $(DROMNS)doc-common $(DROMNS)odoc $(DROMNS)sphinx + +$(DROMNS)view: + xdg-open file://$$(pwd)/_drom/docs/index.html + +$(DROMNS)fmt: + opam exec -- dune build @fmt --auto-promote + +$(DROMNS)fmt-check: + opam exec -- dune build @fmt + +$(DROMNS)install: + opam pin -y --no-action -k path . + opam install -y . + +$(DROMNS)opam: + opam pin -k path . + +$(DROMNS)uninstall: + opam uninstall . + +$(DROMNS)dev-deps: + opam install ./opam/*.opam --deps-only --with-doc --with-test + +$(DROMNS)test: + ./scripts/before.sh test + opam exec -- dune build @runtest + ./scripts/after.sh test + +$(DROMNS)clean: + rm -rf _build + rm -f *.exe *~ + ./scripts/after.sh clean + +$(DROMNS)distclean: $(DROMNS)clean + rm -rf _opam _drom + ./scripts/after.sh distclean + + diff --git a/drom.toml b/drom.toml index a4b95462..e1eeb47f 100644 --- a/drom.toml +++ b/drom.toml @@ -1,8 +1,8 @@ [project] create-project = false -drom-version = "0.1" +drom-version = "0.9.2" share-repo = "https://github.com/OCamlPro/drom-share" -share-version = "0.9.0" +share-version = "branch:ninjapouet:prepare_sites" [project] authors = ["Fabrice Le Fessant ", "Léo Andrès "] @@ -11,7 +11,7 @@ copyright = "OCamlPro SAS" edition = "4.14.1" github-organization = "ocamlpro" license = "LGPL2" -min-edition = "4.07.0" +min-edition = "4.08.0" name = "drom" skeleton = "program" synopsis = "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like user experience" @@ -42,6 +42,8 @@ skip = ["sphinx/about.rst", "src/drom_lib/main.ml", "sphinx/index.rst", "CHANGES [dependencies] # project-wide tools dependencies (not for package-specific deps) +[tools] +dune = ">=2.8" [tools.ocamlformat] for-test = true [tools.odoc] diff --git a/dune-project b/dune-project index 8ff437cd..e4232f49 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,5 @@ -(lang dune 2.7) +(lang dune 2.8) +(using dune_site 0.1) ; This file was generated by drom, using drom.toml (cram enable) @@ -13,13 +14,19 @@ (name drom) (synopsis "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like user experience") (description "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like\nuser experience. It can be used to create full OCaml projects with\nsphinx and odoc documentation. It has specific knowledge of Github and\nwill generate files for Github Actions CI and Github pages.\n") + + (sites + + ) + (depends - (ocaml (>= 4.07.0)) + (ocaml (>= 4.08.0)) (drom_lib (= version)) ppx_inline_test ppx_expect odoc ocamlformat + (dune ( >= 2.8 )) ) ) @@ -27,9 +34,18 @@ (name drom_lib) (synopsis "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like user experience") (description "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like\nuser experience. It can be used to create full OCaml projects with\nsphinx and odoc documentation. It has specific knowledge of Github and\nwill generate files for Github Actions CI and Github pages.\n") + + (sites + + ) + (depends - (ocaml (>= 4.07.0)) + (ocaml (>= 4.08.0)) + (ppx_protocol_conv ( >= 5.2.1 )) + (otoml ( >= 1.0.4 )) (omd ( >= 2.0 )) + (jingoo ( >= 1.5.0 )) + (fmt ( >= 0.9.0 )) (ez_subst ( >= 0.1 )) (ez_opam_file (and (>= 0.1.0) (< 1.0.0))) (ez_file (and (>= 0.3.0) (< 1.0.0))) @@ -40,6 +56,7 @@ ppx_expect odoc ocamlformat + (dune ( >= 2.8 )) ) ) @@ -47,8 +64,13 @@ (name drom_toml) (synopsis "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like user experience") (description "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like\nuser experience. It can be used to create full OCaml projects with\nsphinx and odoc documentation. It has specific knowledge of Github and\nwill generate files for Github Actions CI and Github pages.\n") + + (sites + + ) + (depends - (ocaml (>= 4.07.0)) + (ocaml (>= 4.08.0)) (menhirLib ( > 2019 )) (ISO8601 ( >= 0.2 )) (menhir ( > 2019 )) @@ -56,6 +78,7 @@ ppx_expect odoc ocamlformat + (dune ( >= 2.8 )) ) ) diff --git a/opam/drom.opam b/opam/drom.opam index 25b99c39..453f8288 100644 --- a/opam/drom.opam +++ b/opam/drom.opam @@ -45,12 +45,13 @@ install: [ ["sh" "-c" "./scripts/before.sh install '%{name}%'"] ] depends: [ - "ocaml" {>= "4.07.0"} - "dune" {>= "2.7.0"} + "ocaml" {>= "4.08.0"} + "dune" {>= "2.8"} "drom_lib" {= version} "ppx_inline_test" {with-test} "ppx_expect" {with-test} "odoc" {with-doc} "ocamlformat" {with-test} + "dune" {>= "2.8"} ] # Content of `opam-trailer` field: \ No newline at end of file diff --git a/opam/drom_lib.opam b/opam/drom_lib.opam index 0f737688..49a630b2 100644 --- a/opam/drom_lib.opam +++ b/opam/drom_lib.opam @@ -45,9 +45,13 @@ install: [ ["sh" "-c" "./scripts/before.sh install '%{name}%'"] ] depends: [ - "ocaml" {>= "4.07.0"} - "dune" {>= "2.7.0"} + "ocaml" {>= "4.08.0"} + "dune" {>= "2.8"} + "ppx_protocol_conv" {>= "5.2.1"} + "otoml" {>= "1.0.4"} "omd" {>= "2.0"} + "jingoo" {>= "1.5.0"} + "fmt" {>= "0.9.0"} "ez_subst" {>= "0.1"} "ez_opam_file" {>= "0.1.0" & < "1.0.0"} "ez_file" {>= "0.3.0" & < "1.0.0"} @@ -58,5 +62,6 @@ depends: [ "ppx_expect" {with-test} "odoc" {with-doc} "ocamlformat" {with-test} + "dune" {>= "2.8"} ] # Content of `opam-trailer` field: \ No newline at end of file diff --git a/opam/drom_toml.opam b/opam/drom_toml.opam index da2ccdc5..7c6ef9ad 100644 --- a/opam/drom_toml.opam +++ b/opam/drom_toml.opam @@ -35,8 +35,8 @@ install: [ ["sh" "-c" "./scripts/before.sh install '%{name}%'"] ] depends: [ - "ocaml" {>= "4.07.0"} - "dune" {>= "2.7.0"} + "ocaml" {>= "4.08.0"} + "dune" {>= "2.8"} "menhirLib" {> "2019"} "ISO8601" {>= "0.2"} "menhir" {> "2019"} @@ -44,5 +44,6 @@ depends: [ "ppx_expect" {with-test} "odoc" {with-doc} "ocamlformat" {with-test} + "dune" {>= "2.8"} ] # Content of `opam-trailer` field: \ No newline at end of file diff --git a/scripts/copy-bin.sh b/scripts/copy-bin.sh index 096ef525..09b03174 100755 --- a/scripts/copy-bin.sh +++ b/scripts/copy-bin.sh @@ -5,6 +5,6 @@ PACKAGES="$*" for package in ${PACKAGES}; do file=_build/default/src/${package}/main.exe if [ -f ${file} ]; then - cp -f ${file} ${package} + cp -f ${file} ${package}.exe fi done diff --git a/scripts/static-build.sh b/scripts/static-build.sh index 39eb30f5..000948d0 100755 --- a/scripts/static-build.sh +++ b/scripts/static-build.sh @@ -9,12 +9,14 @@ cd $(dirname "$0")/.. set -o pipefail +# Use `docker-alpine-image` field to replace ocamlpro/ocaml:4.13 +# and `docker-alpine-packages` to add more apk packages git ls-files -z | xargs -0 tar c | \ - docker run --rm -i \ +docker run --rm -i \ ocamlpro/ocaml:4.13 \ sh -uexc \ 'tar x >&2 && - sudo apk add g++ openssl-libs-static bash >&2 && + sudo apk add g++ openssl-libs-static bash >&2 && opam update >&2 && opam switch create . ocaml-system --deps-only --locked >&2 && opam exec make LINKING_MODE=static >&2 && diff --git a/sphinx/reference.rst b/sphinx/reference.rst index 72f6c4d3..80de324f 100644 --- a/sphinx/reference.rst +++ b/sphinx/reference.rst @@ -306,6 +306,9 @@ For example:: dune-libraries = "bigarray" +The package fields +~~~~~~~~~~~~~~~~~~ + The following fields are allowed in a package definition: * :code:`dir`: the directory of the package sources (defaults to @@ -356,6 +359,9 @@ with the following fields: * :code:`modules`: a string array with all the modules to add to the rule * :code:`flags`: an optional string array with all the flags to add to menhir +The :code:`[fields]` table +~~~~~~~~~~~~~~~~~~~~~~~~~~ + Finally, there is a table :code:`[fields]` within a package. Currently, the following fields are used by skeletons: @@ -369,7 +375,104 @@ package. Currently, the following fields are used by skeletons: In skeletons, *fields* are referenced using parens (for example :code:`!(dune-stanzas)`). - + + +The :code:`sites` table +~~~~~~~~~~~~~~~~~~~~~~~ +.. _sites_ref: + +The :code:`sites` table specifies the installation rules for the +project's additionnal files. For each directories, a path is created +and made available for dynamic lookup through a generated module. + +The predefined directories' fields +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are several predefined directories in the :code:`sites` table: + +- :code:`lib` +- :code:`bin` +- :code:`sbin` +- :code:`toplevel` +- :code:`share` +- :code:`etc` +- :code:`stublibs` +- :code:`doc` +- :code:`man` + +Each of these fields is an array of site specification which is a table with +the following fields: + +- :code:`root`: a boolean specifying if the directory is installed in the root + prefix or in the package prefix. It has meaning only for :code:`lib` and + :code:`share` directories. Default is :code:`false`. +- :code:`exec`: a boolean specifying if the files are installed with the + executable flag. It has meaning only for :code:`lib` directory. Default is + :code:`false`. +- :code:`dir`: the name of a custom site, if any. +- :code:`install`: a list of installation specifications. + +Each installation specification is a table with the following fields: + +- :code:`source` +- :code:`destination` +- :code:`recursive` + +:code:`source` is the source file or directory to install. It can be a glob +pattern like :code:`foo/*.js` for instance. :code:`destination` is the +destination file or directory. :code:`recursive` says that :code:`source` is +a directory and should be installed recursively. + +For example: + +.. code-block:: toml + + [[sites.etc]] + [[sites.etc.install]] + source = "foo.conf" + # will install in /etc//foo.conf + + [[sites.share]] + root = true + dir = "www" + [[sites.share.install]] + source = "javascript" + destination = "js" + recursive = true + # will install the javascript directory is /share/www/js + +The :code:`name` field +^^^^^^^^^^^^^^^^^^^^^^ + +The various sites defined in the :code:`sites` table are made available +at runtime through a generated module. The name of this module can be +specified with the :code:`name` field. For example, if the previous +code was introduced by: + +.. code-block:: toml + + [sites] + name = "MySites" + +This would generate a module :code:`MySites` with the following signature: + +.. code-block:: ocaml + + module MySites : sig + module Sites : sig + val www : string list + end + end + +where the :code:`www` value is the list of files installed in it. +If not specified, the module is simply named :code:`Sites`. + +.. warning:: + With the default name, the generated module will be :code:`Sites.Sites`. + +.. warning:: + Only explicit custom sites are made available in the generated module. + Skeletons --------- diff --git a/sphinx/usecases.rst b/sphinx/usecases.rst index 7958ee6f..dad310b0 100644 --- a/sphinx/usecases.rst +++ b/sphinx/usecases.rst @@ -91,3 +91,47 @@ Of course, this is just a basic tuning and you can modify the flags or targets as needed. The overall result will likely fit in most of :code:`menhir` usages in :code:`drom` projects waiting for a better :code:`menhir` support. + + +Using sites +----------- + +Installation sites can be specified in the :code:`package.toml` file under +the :code:`sites` section. By default, :code:`drom` does nothing about it +but you can use it to generate site definitions for your project. + +Thos sites are directories with predefined root +to follow the standards: +* :code:`lib` for libraries +* :code:`bin` for executables +* :code:`sbin` for system executables +* :code:`toplevel` for toplevel executables +* :code:`share` for shared data +* :code:`etc` for configuration files +* :code:`stublibs` for OCaml stub libraries +* :code:`doc` for documentation + +The :code:`man` directory is also a valid installation directory but we can't +use it in sites for it has a special treatment. + +For each of those directories, you can specify a list of directories to install +files in it. For a complete list of possibities, see the +:ref:`sites reference `. + +The most common use is handling configuration or web files which can be +done with the following declaration: + +.. code-block:: toml + + [[sites.share]] + dir = "www" + [[sites.share.install]] + source = "javascript" + destination = "js" + recursive = true + +This will make the :code:`www` directory available both at runtime (through +the value :code:`Sites.Sites.www`) and for web files installation. In this +example, we install all :code:`javascript` subdirectory in +:code:`/share//www/js`. + diff --git a/src/drom/dune b/src/drom/dune index a17a36c7..0cd5365d 100644 --- a/src/drom/dune +++ b/src/drom/dune @@ -3,9 +3,12 @@ (name main) (public_name drom) (package drom) + ; use field 'dune-libraries' to add libraries without opam deps (libraries drom_lib ) + (flags ((:standard (:include linking.sexp)))) + ; use field 'dune-stanzas' to add more stanzas here + - (flags (:standard (:include linking.sexp))) ) ; Use `static-clibs` to specify static C libs (without lib prefix) @@ -28,4 +31,5 @@ (documentation (package drom)) +; use field 'dune-trailer' to add more stuff here diff --git a/src/drom/linking_flags.sh b/src/drom/linking_flags.sh index ae7c14d7..2e1cb70b 100644 --- a/src/drom/linking_flags.sh +++ b/src/drom/linking_flags.sh @@ -52,6 +52,8 @@ case $(uname -s) in Linux) case $(. /etc/os-release && echo $ID) in alpine) + # Use `static-alpine-clibs` field to add libs more here + # (or `static-clibs` for both Linux and Macos) COMMON_LIBS=" camlstr unix c" # `m` and `pthread` are built-in musl echo2 '(-noautolink' @@ -68,6 +70,7 @@ case $(uname -s) in esac ;; Darwin) + # Use `static-macos-clibs` field to add libs more here COMMON_LIBS=" unix" # `m` and `pthread` are built-in in libSystem echo2 '(-noautolink' diff --git a/src/drom/package.toml b/src/drom/package.toml index 49523d59..be7f9472 100644 --- a/src/drom/package.toml +++ b/src/drom/package.toml @@ -21,10 +21,17 @@ kind = "program" # name of a file to generate with the current version # gen-version = "version.ml" -# supported file generators are "ocamllex", "ocamlyacc" and "menhir" -# default is [ "ocamllex", "ocamlyacc" ] +# supported file generators are "ocamllex", "ocamlyacc" and "menhir" +# default is [ "ocamllex", "ocamlyacc" ] # generators = [ "ocamllex", "menhir" ] +# menhir options for the package +#Example: +#version = "2.0" +#parser = { modules = ["parser"]; tokens = "Tokens" } +#tokens = { modules = ["tokens"]} +# menhir = ... + # whether all modules should be packed/wrapped (default is true) # pack-modules = false @@ -35,7 +42,7 @@ kind = "program" # pack = "Mylib" # preprocessing options -# preprocess = "per-module (((action (run ./toto.sh %{input-file})) mod))" +# preprocess = "per-module (((action (run ./toto.sh %{input-file})) mod))" # preprocess = "pps ppx_deriving_encoding" # files to skip while updating at package level @@ -44,7 +51,7 @@ kind = "program" # package library dependencies # [dependencies] # ez_file = ">=0.1 <1.3" -# base-unix = { libname = "unix", version = ">=base" } +# base-unix = { libname = "unix", version = ">=base" } [dependencies] drom_lib = "version" @@ -54,14 +61,14 @@ drom_lib = "version" # package fields (depends on package skeleton) #Examples: -# dune-stanzas = "(preprocess (pps ppx_deriving_encoding))" -# dune-libraries = "bigstring" -# dune-trailer = "(install (..))" -# opam-trailer = "pin-depends: [..]" -# no-opam-test = "yes" -# no-opam-doc = "yes" -# gen-opam = "some" | "all" -# dune-stanzas = "(flags (:standard (:include linking.sexp)))" -# static-clibs = "unix" +# dune-stanzas = "(preprocess (pps ppx_deriving_encoding))" +# dune-libraries = "bigstring" +# dune-trailer = "(install (..))" +# opam-trailer = "pin-depends: [..]" +# no-opam-test = "yes" +# no-opam-doc = "yes" +# gen-opam = "some" | "all" +# dune-stanzas = "(flags (:standard (:include linking.sexp)))" +# static-clibs = "unix" [fields] -dune-stanzas = "(flags (:standard (:include linking.sexp)))" +dune-flags = "(:standard (:include linking.sexp))" diff --git a/src/drom_lib/dune b/src/drom_lib/dune index 628c3b40..bc43c911 100644 --- a/src/drom_lib/dune +++ b/src/drom_lib/dune @@ -4,9 +4,13 @@ (name drom_lib) (public_name drom_lib) (wrapped true) - (libraries omd ez_subst ez_opam_file ez_file ez_cmdliner drom_toml directories bigarray) - + ; use field 'dune-libraries' to add libraries without opam deps + (libraries ppx_protocol_conv otoml omd jingoo fmt ez_subst ez_opam_file ez_file ez_cmdliner drom_toml directories bigarray ppx_protocol_conv.runtime ppx_protocol_conv.driver) + ; use field 'dune-flags' to set this value + (flags (:standard)) + ; use field 'dune-stanzas' to add more stanzas here + (preprocess (pps ppx_deriving.show ppx_protocol_conv)) ) @@ -18,4 +22,5 @@ (documentation (package drom_lib)) +; use field 'dune-trailer' to add more stuff here diff --git a/src/drom_lib/dune.ml b/src/drom_lib/dune.ml index 3dc7c3e0..4410ba01 100644 --- a/src/drom_lib/dune.ml +++ b/src/drom_lib/dune.ml @@ -128,6 +128,18 @@ let package_dune_files package = | None -> () | Some file -> Buffer.add_string b @@ GenVersion.dune package file end; + if ( + VersionCompare.(package.project.project_drom_version >= "0.9.2") && + package.p_sites <> Sites.default + ) then + (* Sites dynamic loading is available only after 0.9.2 and if really + needed *) + begin + let sites_content = Sites.to_dune + ~package:package.name + package.p_sites in + Buffer.add_string b sites_content + end; Buffer.contents b let packages p = @@ -138,9 +150,17 @@ let packages p = (name %s) (synopsis %S) (description %S) + %s |} package.name (Misc.p_synopsis package) - (Misc.p_description package); + (Misc.p_description package) + ( + (* Sites declaration is available only from 0.9.2 *) + if VersionCompare.(package.project.project_drom_version >= "0.9.2") + then package.p_sites |> Sites.to_dune_project + else "" + ) + ; let depend_of_dep (name, d) = match d.depversions with diff --git a/src/drom_lib/globals.ml b/src/drom_lib/globals.ml index 942ef4ac..4822cbff 100644 --- a/src/drom_lib/globals.ml +++ b/src/drom_lib/globals.ml @@ -21,7 +21,7 @@ let min_ocaml_edition = "4.07.0" let current_ocaml_edition = "4.13.0" -let current_dune_version = "2.7.0" +let current_dune_version = "2.8.0" let default_synopsis ~name = Printf.sprintf "The %s project" name @@ -180,5 +180,6 @@ and dummy_package = p_menhir = None; p_skip = None; p_optional = None; - p_preprocess = None + p_preprocess = None; + p_sites = Sites.default; } diff --git a/src/drom_lib/package.ml b/src/drom_lib/package.ml index 898d7a59..4a2c05dd 100644 --- a/src/drom_lib/package.ml +++ b/src/drom_lib/package.ml @@ -343,7 +343,6 @@ let to_string pk = {| no-opam-test = "yes" |}; {| no-opam-doc = "yes" |}; {| gen-opam = "some" | "all" |}; - {| dune-stanzas = "(flags (:standard (:include linking.sexp)))" |}; {| static-clibs = "unix" |}; ] (encoding fields_encoding pk.p_fields) @@ -452,7 +451,10 @@ let of_toml ?default ?p_file table = EzToml.get_encoding_option skip_encoding table [ "skip" ] ?default:default.p_skip in - + let p_sites = + match Toml.Lenses.get table Toml.Lenses.(key "sites" |-- table) with + | None -> Sites.default + | Some toml -> EzToml.TYPES.TTable toml |> Sites.of_eztoml in { name; dir; project; @@ -473,7 +475,8 @@ let of_toml ?default ?p_file table = p_generators; p_menhir; p_skip; - p_optional + p_optional; + p_sites; } let of_toml ?default table = diff --git a/src/drom_lib/package.toml b/src/drom_lib/package.toml index c18fbdb3..6fa769b6 100644 --- a/src/drom_lib/package.toml +++ b/src/drom_lib/package.toml @@ -25,6 +25,13 @@ gen-version = "version.ml" # default is [ "ocamllex", "ocamlyacc" ] # generators = [ "ocamllex", "menhir" ] +# menhir options for the package +#Example: +#version = "2.0" +#parser = { modules = ["parser"]; tokens = "Tokens" } +#tokens = { modules = ["tokens"]} +# menhir = ... + # whether all modules should be packed/wrapped (default is true) pack-modules = true @@ -49,11 +56,14 @@ pack-modules = true directories = ">=0.2" drom_toml = "version" ez_cmdliner = "0.2.0" -#ez_config = "0.1.0" ez_file = "0.3.0" ez_opam_file = "0.1.0" ez_subst = ">=0.1" +fmt = ">=0.9.0" +jingoo = ">=1.5.0" omd = ">=2.0" +otoml = ">=1.0.4" +ppx_protocol_conv = ">=5.2.1" # package tools dependencies [tools] @@ -71,5 +81,6 @@ omd = ">=2.0" # dune-stanzas = "(flags (:standard (:include linking.sexp)))" # static-clibs = "unix" [fields] -dune-libraries = "bigarray" +dune-libraries = "bigarray ppx_protocol_conv.runtime ppx_protocol_conv.driver" +dune-stanzas = "(preprocess (pps ppx_deriving.show ppx_protocol_conv))" dune-trailer = "" diff --git a/src/drom_lib/project.ml b/src/drom_lib/project.ml index c671017f..c40a9a62 100644 --- a/src/drom_lib/project.ml +++ b/src/drom_lib/project.ml @@ -673,7 +673,23 @@ let project_of_toml ?file ?default table = } in package.project <- project; - List.iter (fun p -> p.project <- project) packages; + List.iter (fun p -> + p.project <- project; + if ( + VersionCompare.(project_drom_version >= "0.9.2") && + p.p_sites <> Sites.default && + not (List.mem_assoc "dune-site" p.p_dependencies) + ) then + (* Sites dynamic loading (available after 0.9.2) needs [dune-site]. *) + p.p_dependencies <- ("dune-site", { + depname = None; + depversions = [ Ge "3.14.0" ]; + deptest = false; + depdoc = false; + depopt = false; + dep_pin = None; + }) :: p.p_dependencies + ) packages; project let of_string ~msg ?default content = diff --git a/src/drom_lib/protocol.ml b/src/drom_lib/protocol.ml new file mode 100644 index 00000000..d65798e4 --- /dev/null +++ b/src/drom_lib/protocol.ml @@ -0,0 +1,207 @@ +(**************************************************************************) +(* *) +(* Copyright 2024 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +module Toml = struct + + module Driver : Ppx_protocol_driver.Driver with type t = Otoml.t = struct + type t = Otoml.t + + let to_string_hum toml = Otoml.Printer.to_string toml + + let of_list = Otoml.array + let to_list = function + | Otoml.TomlArray a | Otoml.TomlTableArray a -> a + | _ -> failwith "array expected" + + let is_list = function + | Otoml.TomlArray _ | Otoml.TomlTableArray _ -> true | _ -> false + + let of_alist = Otoml.table + let to_alist = function + | Otoml.TomlTable l | Otoml.TomlInlineTable l -> l + | _ -> failwith "table expected" + + + let is_alist = function + | Otoml.TomlTable _ | Otoml.TomlInlineTable _ -> true + | _ -> false + + let of_int = Otoml.integer + let to_int = function + | Otoml.TomlInteger i -> i + | _ -> failwith "int expected" + + let of_int32 i = Int32.to_int i |> of_int + let to_int32 toml = to_int toml |> Int32.of_int + + let of_int64 i = Int64.to_int i |> of_int + let to_int64 toml = to_int toml |> Int64.of_int + + let of_nativeint i = of_int (Nativeint.to_int i) + let to_nativeint toml = to_int toml |> Nativeint.of_int + + let of_float = Otoml.float + let to_float = function + | Otoml.TomlFloat f -> f + | _ -> failwith "float expected" + + let of_string = Otoml.string + let to_string = function + | Otoml.TomlString s -> s + | _ -> failwith "string expected" + + let is_string = function + | Otoml.TomlString _ -> true + | _ -> false + + let of_char c = Otoml.string (String.make 1 c) + let to_char = function + | Otoml.TomlString s when String.length s > 0 -> + String.get s 0 (* can't fail *) + | _ -> failwith "char expected" + + + let of_bool = Otoml.boolean + let to_bool = function + | Otoml.TomlBoolean b -> b + | _ -> failwith "bool expected" + + let to_bytes toml = Bytes.of_string (to_string toml) + let of_bytes bytes = of_string (Bytes.to_string bytes) + + (* ToML has no null value but they are used to encode/decode [None] + with empty tables. *) + let null = Otoml.table [] + let is_null = (=) null + + end + + module Make(P : Ppx_protocol_driver.Parameters) = struct + include Ppx_protocol_driver.Make(Driver)(P) + end + include Make(Ppx_protocol_driver.Default_parameters) +end + +module Jinja2 = struct + + module Driver : Ppx_protocol_driver.Driver + with type t = Jingoo.Jg_types.tvalue = + struct + type t = Jingoo.Jg_types.tvalue + + let null = Jingoo.Jg_types.Tnull + + let is_null = (=) null + + let of_string = Jingoo.Jg_types.box_string + let to_string jg = + try Jingoo.Jg_types.unbox_string jg with _ -> failwith "string expected" + + let is_string = function + | Jingoo.Jg_types.Tstr _ -> true + | _ -> false + + let of_bytes b = of_string (Bytes.to_string b) + let to_bytes jg = Bytes.of_string (to_string jg) + + let of_bool = Jingoo.Jg_types.box_bool + let to_bool jg = + try Jingoo.Jg_types.unbox_bool jg with _ -> failwith "bool expected" + + let of_float = Jingoo.Jg_types.box_float + let to_float jg = + try Jingoo.Jg_types.unbox_float jg with _ -> failwith "float expected" + + let of_int = Jingoo.Jg_types.box_int + let to_int jg = + try Jingoo.Jg_types.unbox_int jg with _ -> failwith "int expected" + + let of_int32 i = i |> Int32.to_int |> of_int + let to_int32 jg = to_int jg |> Int32.of_int + + let of_int64 i = i |> Int64.to_int |> of_int + let to_int64 jg = to_int jg |> Int64.of_int + + let of_nativeint i = i |> Nativeint.to_int |> of_int + let to_nativeint jg = to_int jg |> Nativeint.of_int + + let of_char c = of_string (String.make 1 c) + let to_char = function + | Jingoo.Jg_types.Tstr s when String.length s > 0 -> + String.get s 0 (* can't fail *) + | _ -> failwith "char expected" + + let of_alist = Jingoo.Jg_types.box_obj + let to_alist jg = + try Jingoo.Jg_types.unbox_obj jg with _ -> failwith "alist expected" + + let is_alist = function + | Jingoo.Jg_types.Tobj _ -> true + | _ -> false + + let of_list = Jingoo.Jg_types.box_list + let to_list jg = + try Jingoo.Jg_types.unbox_list jg with _ -> failwith "list expected" + + let is_list = function + | Jingoo.Jg_types.Tlist _ -> true + | _ -> false + + (* Not very sure about this printing but it seems ok for our needs. *) + let rec pp : t Fmt.t = fun ppf t -> + match t with + | Jingoo.Jg_types.Tnull -> Fmt.string ppf "null" + | Jingoo.Jg_types.Tbool b -> Fmt.bool ppf b + | Jingoo.Jg_types.Tint i -> Fmt.int ppf i + | Jingoo.Jg_types.Tfloat f -> Fmt.float ppf f + | Jingoo.Jg_types.Tstr s -> Fmt.(quote string) ppf s + | Jingoo.Jg_types.Tlist l -> + Fmt.(brackets (list ~sep:(any ";@ ") pp)) ppf l + | Jingoo.Jg_types.Tset s -> + Fmt.(braces (list ~sep:(any ";@ ") pp)) ppf s + | Jingoo.Jg_types.Tobj o -> + Fmt.(braces + (list ~sep:(any ";@\n") (pair ~sep:(any " =@;") string pp))) ppf o + | Jingoo.Jg_types.Thash h -> + Fmt.(braces + (hashtbl ~sep:(any ";@\n") + (pair ~sep:(any " =@;") (brackets string) pp))) ppf h + | Jingoo.Jg_types.Tpat _ -> + Fmt.string ppf "" + | Jingoo.Jg_types.Tfun _ -> + Fmt.string ppf "" + | Jingoo.Jg_types.Tarray a -> + Fmt.(brackets (array ~sep:(any ",@ ") pp)) ppf a + | Jingoo.Jg_types.Tlazy l -> + pp ppf (Lazy.force l) + | Jingoo.Jg_types.Tvolatile _ -> + Fmt.string ppf "" + | Jingoo.Jg_types.Tsafe s -> + Fmt.(quote string) ppf s + + let to_string_hum = Fmt.str "%a" pp + + + end + + module Make(P : Ppx_protocol_driver.Parameters) = struct + include Ppx_protocol_driver.Make(Driver)(P) + end + + (* Overriding the default driver because we want explicit substitutions + even with default values. *) + module Defaults = struct + include Ppx_protocol_driver.Default_parameters + let omit_default_values = false + end + + include Make(Defaults) + +end \ No newline at end of file diff --git a/src/drom_lib/protocol.mli b/src/drom_lib/protocol.mli new file mode 100644 index 00000000..eaebe1e8 --- /dev/null +++ b/src/drom_lib/protocol.mli @@ -0,0 +1,31 @@ +(**************************************************************************) +(* *) +(* Copyright 2024 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** This module implement various drivers for [ppx_protocol_conv]. *) + + +(** The ToML driver. + + We use [otoml] and not [toml]/[drom_toml]/[eztoml] because the + latters use a silly array encoding (and not conform to the ToML standard) + that prevents us to inject OCaml values into it without dynamic typing. *) +module Toml : Protocol_conv.Runtime.Driver + with type t = Otoml.t + +(** The Jinja2 driver. + + Jinja2 is the templating de facto standard for content templating. This + drivers allows to use OCaml values (and specially records) as + substitutions. + + We use the [jingoo] library which isn't complete according to Jinja2 + specification but quite sufficient for our current needs. *) +module Jinja2 : Protocol_conv.Runtime.Driver + with type t = Jingoo.Jg_types.tvalue \ No newline at end of file diff --git a/src/drom_lib/sites.ml b/src/drom_lib/sites.ml new file mode 100644 index 00000000..f5a40975 --- /dev/null +++ b/src/drom_lib/sites.ml @@ -0,0 +1,273 @@ +(**************************************************************************) +(* *) +(* Copyright 2024 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + + +type t = Types.sites +[@@deriving + show, + protocol ~driver:(module Protocol.Toml), + protocol ~driver:(module Protocol.Jinja2)] + +let default = Types.{ + sites_name = default_sites_name; + sites_lib = default_sites_lib; + sites_bin = default_sites_bin; + sites_sbin = default_sites_sbin; + sites_toplevel = default_sites_toplevel; + sites_share = default_sites_share; + sites_etc = default_sites_etc; + sites_stublibs = default_sites_stublibs; + sites_doc = default_sites_doc; + sites_man = default_sites_man; +} + +let rec eztoml_value_to_otoml = function + | EzToml.TYPES.TBool b -> Otoml.boolean b + | EzToml.TYPES.TInt i -> Otoml.integer i + | EzToml.TYPES.TFloat f -> Otoml.float f + | EzToml.TYPES.TString s -> Otoml.string s + | EzToml.TYPES.TArray a -> Otoml.array (eztoml_array_to_otoml a) + | EzToml.TYPES.TTable a -> + EzToml.TYPES.Table.bindings a |> + List.map (fun (k, v) -> + (EzToml.TYPES.Table.Key.to_string k, eztoml_value_to_otoml v)) |> + Otoml.table + | EzToml.TYPES.TDate d -> Otoml.float d + +and eztoml_array_to_otoml = function + | EzToml.TYPES.NodeEmpty -> [] + | EzToml.TYPES.NodeBool l -> List.map (fun b -> Otoml.boolean b) l + | EzToml.TYPES.NodeInt l -> List.map (fun i -> Otoml.integer i) l + | EzToml.TYPES.NodeFloat l -> List.map (fun f -> Otoml.float f) l + | EzToml.TYPES.NodeString l -> List.map (fun s -> Otoml.string s) l + | EzToml.TYPES.NodeDate l -> List.map (fun d -> Otoml.float d) l + | EzToml.TYPES.NodeArray l -> + List.map (fun a -> Otoml.array (eztoml_array_to_otoml a)) l + | EzToml.TYPES.NodeTable l -> + List.map (fun t -> eztoml_value_to_otoml (EzToml.TYPES.TTable t)) l + +let of_eztoml toml = + eztoml_value_to_otoml toml |> of_toml_exn + +(* This template should be externalized with the whole dune-project template + but it needs: + - refactoring the whole drom templating strategy + - await a bump for next version so that site generation would be + available for drom itself. *) +let project_template = {| + (sites{# lib #} + {% for spec in sites.lib -%} + {# if no dir, no declaration is needed #} + {%- if spec.dir != "" -%} + {%- if spec.exec -%} + {%- if spec.root -%} + (libexec_root {{ spec.dir }}) + {%- else -%} + (libexec {{ spec.dir }}) + {%- endif -%} + {%- else -%} + {% if spec.root -%} + (lib_root {{ spec.dir }}) + {% else -%} + (lib {{ spec.dir }}) + {%- endif -%} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + + {# bin #} + {%- for spec in sites.bin -%} + {# if no dir, no declaration is needed #} + {% if spec.dir != "" %}(bin {{ spec.dir }}){% endif %} + {%- endfor -%} + + {# sbin #} + {%- for spec in sites.sbin -%} + {# if no dir, no declaration is needed #} + {% if spec.dir != "" %}(sbin {{ spec.dir }}){% endif %} + {%- endfor -%} + + {# toplevel #} + {%- for spec in sites.toplevel -%} + {# if no dir, no declaration is needed #} + {% if spec.dir != "" %}(toplevel {{ spec.dir }}){% endif %} + {%- endfor -%} + + {# share #} + {%- for spec in sites.share -%} + {# if no dir, no declaration is needed #} + {%- if spec.dir != "" %} + {%- if spec.root -%} + (share_root {{ spec.dir }}) + {%- else -%} + (share {{ spec.dir }}) + {%- endif -%} + {%- endif -%} + {%- endfor -%} + + {# etc #} + {%- for spec in sites.etc -%} + {# if no dir, no declaration is needed #} + {% if spec.dir != "" %}(etc {{ spec.dir }}){% endif -%} + {%- endfor -%} + + {# stublibs #} + {%- for spec in sites.stublibs -%} + {# if no dir, no declaration is needed #} + {% if spec.dir != "" %}(stublibs {{ spec.dir }}){% endif -%} + {%- endfor -%} + + {# doc #} + {%- for spec in sites.doc -%} + {# if no dir, no declaration is needed #} + {% if spec.dir != "" %}(doc {{ spec.dir }}){% endif -%} + {%- endfor %} + ) +|} + +let to_dune_project t = + let model = to_jinja2 t in + let res = Jingoo.Jg_template.from_string project_template ~models:[ + "sites", model; + ] in + res + +let package_template = {| +(generate_sites_module + (module {{ sites.name }}) + (sites {{ package }})) + +{% macro install_stanza (site_spec, install_spec, package, section) %} +(install{# #} +{%- if install_spec.source | contains("*") -%}{# this is a glob pattern #} + (files + (glob_files{%- if install_spec.recursive %}_rec{% endif %} + {{ install_spec.source }}{# #} + {%- if install_spec.destination != '' -%} + with_prefix {{ install_spec.destination }} + {%- endif %} + ) + ) +{%- elseif install_spec.recursive -%}{# this is a whole directory #} + (source_trees + {% if install_spec.destination == '' -%} + {{ install_spec.source }} + {%- else -%} + ({{ install_spec.source }} as {{ install_spec.destination }}) + {%- endif %} + ) +{%- else -%}{# this is a simple file #} + (files + {% if install_spec.destination == '' -%} + {{ install_spec.source }} + {%- else -%} + ({{ install_spec.source }} as {{ install_spec.destination }}) + {%- endif %} + ) +{%- endif %} + (section + {% if site_spec.dir != '' -%} + (site ({{ package }} {{ spec.dir }})) + {%- else -%} + {{ section }} + {%- endif %} + ) + (package {{ package }}) +) +{% endmacro -%} + +{%- function section (spec, root) -%} + {% set res = root %} + {% if root == 'lib' -%} + {% if spec.exec -%}{% set res += 'exec' %}{% endif -%} + {% if spec.root -%}{% set res += '_root' %}{% endif -%} + {{ res }} + {%- elif root == 'share' -%}h + {% if spec.root -%}{% set res += '_root' %}{% endif -%} + {%- endif -%} + {{ res }} +{%- endfunction -%} + +{% for spec in sites.lib -%} + {%- for install in spec.install -%} + {{ install_stanza (spec, install, package, section (spec, 'lib')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.bin -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'bin')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.sbin -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'sbin')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.toplevel -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'toplevel')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.share -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'share')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.etc -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'etc')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.stublibs -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'stublibs')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.doc -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'doc')) }} + {%- endfor -%} +{%- endfor -%} +{%- for spec in sites.man -%} + {%- for install in spec.install -%} + {{ install_stanza(spec, install, package, section(spec, 'man')) }} + {%- endfor -%} +{%- endfor -%} +|} + + +(* A regexp matcher to be used as filter. *) +let contains ?kwargs:_ pattern_value string_value = + try + let p = Jingoo.Jg_types.unbox_string pattern_value in + let s = Jingoo.Jg_types.unbox_string string_value in + let re = Str.regexp p in + ignore (Str.search_forward re s 0); + Jingoo.Jg_types.box_bool true + with _ -> + Jingoo.Jg_types.box_bool false + + +let to_dune ~package t = + let t_model = to_jinja2 t in + let p_model = Protocol.Jinja2.of_string package in + let res = + Jingoo.Jg_template.from_string package_template + ~env:Jingoo.Jg_types.{ + std_env with + filters = [ + "contains", func_arg2 contains; + ]; + } + ~models:[ + "sites", t_model; + "package", p_model; + ] in + res \ No newline at end of file diff --git a/src/drom_lib/sites.mli b/src/drom_lib/sites.mli new file mode 100644 index 00000000..b56d7dfe --- /dev/null +++ b/src/drom_lib/sites.mli @@ -0,0 +1,30 @@ +(**************************************************************************) +(* *) +(* Copyright 2024 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** This module implement sites management. *) + +(** Just aliasing {!Types.sites}. *) +type t = Types.sites +[@@deriving + show, + protocol ~driver:(module Protocol.Toml), + protocol ~driver:(module Protocol.Jinja2)] + +(** The default sites specification. *) +val default : t + +(** Converts an eztoml value to a sites value. *) +val of_eztoml : EzToml.TYPES.value -> t + +(** Generates the package dune stanza for sites. *) +val to_dune_project : t -> string + +(** Generates the dynamic sites stanzas for [package]'s dune. *) +val to_dune : package:string -> t -> string \ No newline at end of file diff --git a/src/drom_lib/types.ml b/src/drom_lib/types.ml index 4ddbb16b..3c04130b 100644 --- a/src/drom_lib/types.ml +++ b/src/drom_lib/types.ml @@ -64,6 +64,100 @@ type menhir = tokens: menhir_tokens option; } +let default_install_destination = "" +let default_install_recursive = false + +type install_spec = { + install_source : string; + [@key "source"] + + install_destination : string; + [@default default_install_destination] + [@key "destination"] + + install_recursive : bool; + [@default default_install_recursive] + [@key "recursive"] +}[@@deriving + show, + protocol ~driver:(module Protocol.Toml), + protocol ~driver:(module Protocol.Jinja2)] + + +(** Lib site specification. *) +type sites_spec = { + sites_spec_exec : bool; [@default false][@key "exec"] + sites_spec_root : bool; [@default false][@key "root"] + sites_spec_dir : string; [@default ""][@key "dir"] + sites_spec_install : install_spec list; [@default []][@key "install"] +} +[@@deriving + show, + protocol ~driver:(module Protocol.Toml), + protocol ~driver:(module Protocol.Jinja2)] + +(** Various default values for sites. *) + +let default_sites_name = "sites" +let default_sites_lib = [] +let default_sites_bin = [] +let default_sites_sbin = [] +let default_sites_toplevel = [] +let default_sites_share = [] +let default_sites_etc = [] +let default_sites_stublibs = [] +let default_sites_doc = [] +let default_sites_man = [] + +(** Sites' specification. *) +type sites = { + + sites_name : string; + [@default default_sites_name] + [@key "name"] + + sites_lib : sites_spec list; + [@default default_sites_lib] + [@key "lib"] + + sites_bin : sites_spec list; + [@default default_sites_bin] + [@key "bin"] + + sites_sbin : sites_spec list; + [@default default_sites_sbin] + [@key "sbin"] + + sites_toplevel : sites_spec list; + [@default default_sites_toplevel] + [@key "toplevel"] + + sites_share : sites_spec list; + [@default default_sites_share] + [@key "share"] + + sites_etc : sites_spec list; + [@default default_sites_etc] + [@key "etc"] + + sites_stublibs : sites_spec list; + [@default default_sites_stublibs] + [@key "stublibs"] + + sites_doc : sites_spec list; + [@default default_sites_doc] + [@key "doc"] + + sites_man : sites_spec list; + [@default default_sites_man] + [@key "man"] +} +[@@deriving + show, + protocol ~driver:(module Protocol.Toml), + protocol ~driver:(module Protocol.Jinja2)] + + type package = { name : string; mutable dir : string; @@ -85,7 +179,8 @@ type package = mutable p_file : string option; mutable p_skip : string list option; mutable p_optional : bool option; - mutable p_preprocess : string option + mutable p_preprocess : string option; + mutable p_sites : sites; } and project = diff --git a/src/drom_lib/version.mlt b/src/drom_lib/version.mlt index 0bf27f13..cd3c5de2 100644 --- a/src/drom_lib/version.mlt +++ b/src/drom_lib/version.mlt @@ -11,8 +11,13 @@ let query cmd = else None with End_of_file -> None -let commit_hash = query "git show -s --pretty=format:%H" -let commit_date = query "git show -s --pretty=format:%ci" +let gitdir = + try Sys.getenv "DUNE_SOURCEROOT" with Not_found -> "" + +let commit_hash = + query ("git -C \""^gitdir^"\" show -s --pretty=format:%H") +let commit_date = + query ("git -C \""^gitdir^"\" show -s --pretty=format:%ci") let version = "0.9.2" let string_option = function diff --git a/src/drom_lib/versionCompare.ml b/src/drom_lib/versionCompare.ml index 4f541350..18e2e9e4 100644 --- a/src/drom_lib/versionCompare.ml +++ b/src/drom_lib/versionCompare.ml @@ -212,3 +212,6 @@ let equal (x : string) (y : string) = true else compare x y = 0 + + +let ( >= ) a b = compare a b >= 0 diff --git a/src/drom_lib/versionCompare.mli b/src/drom_lib/versionCompare.mli index 1bfbfadf..297361f5 100644 --- a/src/drom_lib/versionCompare.mli +++ b/src/drom_lib/versionCompare.mli @@ -35,3 +35,6 @@ val equal : string -> string -> bool (** [compare x y] returns 0 if x is eqivalent to y, -1 if x is smaller than y, and 1 if x is greater than y. This is consistent with [Stdlib.compare]. *) val compare : string -> string -> int + +(** [a >= b] is a syntactic sugar for [compare a b >= 0]. *) +val ( >= ) : string -> string -> bool diff --git a/src/toml.7.1.0/dune b/src/toml.7.1.0/dune index 72bad47a..53796fab 100644 --- a/src/toml.7.1.0/dune +++ b/src/toml.7.1.0/dune @@ -4,13 +4,17 @@ (name drom_toml) (public_name drom_toml) (wrapped true) + ; use field 'dune-libraries' to add libraries without opam deps (libraries menhirLib ISO8601 str unix) + ; use field 'dune-flags' to set this value + (flags (:standard)) + ; use field 'dune-stanzas' to add more stanzas here ) -(menhir (modules menhir_parser)) (ocamllex lexer) +(menhir (modules menhir_parser)) (rule (targets version.ml) @@ -20,4 +24,5 @@ (documentation (package drom_toml)) +; use field 'dune-trailer' to add more stuff here diff --git a/src/toml.7.1.0/package.toml b/src/toml.7.1.0/package.toml index a8bd2fb5..c4c2bfbc 100644 --- a/src/toml.7.1.0/package.toml +++ b/src/toml.7.1.0/package.toml @@ -25,6 +25,13 @@ gen-version = "version.ml" # default is [ "ocamllex", "ocamlyacc" ] generators = ["menhir", "ocamllex"] +# menhir options for the package +#Example: +#version = "2.0" +#parser = { modules = ["parser"]; tokens = "Tokens" } +#tokens = { modules = ["tokens"]} +# menhir = ... + # whether all modules should be packed/wrapped (default is true) # pack-modules = false diff --git a/src/toml.7.1.0/version.mlt b/src/toml.7.1.0/version.mlt index 0bf27f13..cd3c5de2 100644 --- a/src/toml.7.1.0/version.mlt +++ b/src/toml.7.1.0/version.mlt @@ -11,8 +11,13 @@ let query cmd = else None with End_of_file -> None -let commit_hash = query "git show -s --pretty=format:%H" -let commit_date = query "git show -s --pretty=format:%ci" +let gitdir = + try Sys.getenv "DUNE_SOURCEROOT" with Not_found -> "" + +let commit_hash = + query ("git -C \""^gitdir^"\" show -s --pretty=format:%H") +let commit_date = + query ("git -C \""^gitdir^"\" show -s --pretty=format:%ci") let version = "0.9.2" let string_option = function diff --git a/test/expect-tests/dune.drom b/test/expect-tests/dune.drom new file mode 100644 index 00000000..480942a8 --- /dev/null +++ b/test/expect-tests/dune.drom @@ -0,0 +1,14 @@ +; This file is managed by 'drom project' + +; You may want to remove it by adding 'test/expect-tests/dune.drom' +; to 'skip' in 'drom.toml', or '@test' for all tests +; and remove the '(include dune.drom)' from 'dune' in the current dir + +(library + (name lib_expect_tests) + (preprocess + (pps ppx_expect)) + (inline_tests + (modes best)) ; add js for testing with nodejs + (libraries drom_lib drom_toml) ; add your project libraries here + ) diff --git a/test/inline-tests/dune.drom b/test/inline-tests/dune.drom new file mode 100644 index 00000000..a37dfb90 --- /dev/null +++ b/test/inline-tests/dune.drom @@ -0,0 +1,13 @@ +; This file is managed by 'drom project' + +; You may want to remove it by adding 'test/expect-tests/dune.drom' +; to 'skip' in 'drom.toml', or '@test' for all tests +; and remove the '(include dune.drom)' from 'dune' in the current dir + +(library + (name lib_inline_tests) + (preprocess + (pps ppx_inline_test)) + (inline_tests + (modes best)) ; add js for testing with nodejs + (libraries drom_lib drom_toml)) diff --git a/test/output-tests/dune.drom b/test/output-tests/dune.drom new file mode 100644 index 00000000..4e670f12 --- /dev/null +++ b/test/output-tests/dune.drom @@ -0,0 +1,24 @@ +; This file is managed by 'drom project' + +; You may want to remove it by adding 'test/output-tests/dune.drom' +; to 'skip' in 'drom.toml', or '@test' for all tests +; and remove the '(include dune.drom)' from 'dune' in the current dir + +(executable + (name test2) + (libraries drom_lib drom_toml) + ) + +(alias + (name buildtest) + (deps test2.exe)) + +(rule + (with-stdout-to + test2.output + (run %{exe:test2.exe}))) + +(rule + (alias runtest) + (action + (diff test2.expected test2.output))) diff --git a/test/output-tests/test2.expected b/test/output-tests/test2.expected index 632e4fe7..da71290b 100644 --- a/test/output-tests/test2.expected +++ b/test/output-tests/test2.expected @@ -1 +1,45 @@ -Bonjour +Sites (project): + + (sites + (libexec bar)(share www) + ) + +Sites (package): + +(generate_sites_module + (module sites) + (sites foo)) + + +(install + (files + (glob_files + bar/* + ) + ) + (section + (site (foo bar)) + ) + (package foo) +) + +(install + (source_trees + (javascript as js) + ) + (section + (site (foo www)) + ) + (package foo) +) + +(install + (files + index.html + ) + (section + share_root + ) + (package foo) +) + diff --git a/test/output-tests/test2.ml b/test/output-tests/test2.ml index 31422a41..e95ed797 100644 --- a/test/output-tests/test2.ml +++ b/test/output-tests/test2.ml @@ -1,3 +1,62 @@ +open Drom_lib + +let package = { + Globals.dummy_package with + name = "foo"; + p_sites = { + Sites.default with + sites_lib = [ + { + sites_spec_dir = "bar"; + sites_spec_root = false; + sites_spec_exec = true; + sites_spec_install = [ + { + install_source = "bar/*"; + install_recursive = false; + install_destination = ""; + } + ]; + } + ]; + sites_share = [ + { + sites_spec_dir = "www"; + sites_spec_root = false; + sites_spec_exec = false; + sites_spec_install = [ + { + install_source = "javascript"; + install_recursive = true; + install_destination = "js"; + } + ]; + }; + { + sites_spec_dir = ""; + sites_spec_root = true; + sites_spec_exec = false; + sites_spec_install = [ + { + install_source = "index.html"; + install_recursive = false; + install_destination = ""; + } + ]; + } + ] + } +} + +let project = { + Globals.dummy_project with + packages = [package]; +} + let () = - Printf.printf "Bonjour\n%!"; + let sites = package.p_sites in + Fmt.pr "Sites (project):@\n%s@." @@ + Sites.to_dune_project sites; + Fmt.pr "Sites (package):@\n%s@." @@ + Sites.to_dune ~package:package.name sites; exit 0