├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .ocamlformat ├── .ocp-indent ├── CHANGES ├── CODE_OF_CONDUCT.md ├── LICENSE ├── META.in ├── Makefile ├── Makefile.options ├── README.md ├── doc ├── index.wiki ├── indexdoc.client └── indexdoc.server ├── opam ├── pull_request_template.md ├── scripts ├── 2distillery.sh ├── 2var.sh ├── install.sh └── uninstall.sh ├── src ├── os_comet.eliom ├── os_comet.eliomi ├── os_connect_phone.eliom ├── os_connect_phone.eliomi ├── os_core_db.ml ├── os_core_db.mli ├── os_current_user.eliom ├── os_current_user.eliomi ├── os_date.eliom ├── os_date.eliomi ├── os_db.ml ├── os_db.mli ├── os_db.ppx.ml ├── os_email.eliom ├── os_email.eliomi ├── os_fcm_notif.eliom ├── os_fcm_notif.eliomi ├── os_group.ml ├── os_group.mli ├── os_handlers.eliom ├── os_handlers.eliomi ├── os_icons.eliom ├── os_icons.eliomi ├── os_lib.eliom ├── os_lib.eliomi ├── os_msg.eliom ├── os_msg.eliomi ├── os_notif.eliom ├── os_notif.eliomi ├── os_page.eliom ├── os_page.eliomi ├── os_platform.eliom ├── os_platform.eliomi ├── os_request_cache.eliom ├── os_request_cache.eliomi ├── os_services.eliom ├── os_services.eliomi ├── os_session.eliom ├── os_session.eliomi ├── os_tips.eliom ├── os_tips.eliomi ├── os_types.eliom ├── os_types.eliomi ├── os_uploader.eliom ├── os_uploader.eliomi ├── os_user.eliom ├── os_user.eliomi ├── os_user_proxy.eliom ├── os_user_proxy.eliomi ├── os_user_view.eliom └── os_user_view.eliomi ├── template.distillery ├── .eliomignore ├── .eliomreserve ├── .eliomverbatim ├── .gitignore ├── .ocamlformat ├── .ocp-indent ├── LICENSE ├── Makefile ├── Makefile.db ├── Makefile.local.example ├── Makefile.mobile ├── Makefile.options ├── Makefile.os ├── Makefile.style ├── PROJECT_NAME.conf.in ├── PROJECT_NAME.eliom ├── PROJECT_NAME.opam ├── PROJECT_NAME.sql ├── PROJECT_NAME_base.eliom ├── PROJECT_NAME_config.eliom ├── PROJECT_NAME_config.eliomi ├── PROJECT_NAME_container.eliom ├── PROJECT_NAME_container.eliomi ├── PROJECT_NAME_drawer.eliom ├── PROJECT_NAME_handlers.eliom ├── PROJECT_NAME_handlers.eliomi ├── PROJECT_NAME_icons.eliom ├── PROJECT_NAME_language.eliom ├── PROJECT_NAME_language.eliomi ├── PROJECT_NAME_main.eliom ├── PROJECT_NAME_mobile.eliom ├── PROJECT_NAME_mobile.eliomi ├── PROJECT_NAME_page.eliom ├── PROJECT_NAME_page.eliomi ├── PROJECT_NAME_phone_connect.eliom ├── PROJECT_NAME_services.eliom ├── PROJECT_NAME_services.eliomi ├── PROJECT_NAME_settings.eliom ├── PROJECT_NAME_static_config.eliom.in ├── README.md ├── assets!PROJECT_NAME_Demo_i18n.tsv ├── assets!PROJECT_NAME_i18n.tsv ├── assets!images!icon.png ├── demo.eliom ├── demo_cache.eliom ├── demo_calendar.eliom ├── demo_carousel1.eliom ├── demo_carousel2.eliom ├── demo_carousel3.eliom ├── demo_i18n.eliom ├── demo_links.eliom ├── demo_notif.eliom ├── demo_pagetransition.eliom ├── demo_pgocaml.eliom ├── demo_pgocaml_db.ml ├── demo_popup.eliom ├── demo_pulltorefresh.eliom ├── demo_react.eliom ├── demo_ref.eliom ├── demo_rpc.eliom ├── demo_services.eliom ├── demo_spinner.eliom ├── demo_timepicker.eliom ├── demo_tips.eliom ├── demo_tongue.eliom ├── demo_tools.eliom ├── demo_users.eliom ├── dune ├── dune-project ├── dune.config ├── mobile!.chcpignore ├── mobile!chcp.json.in ├── mobile!config.xml.in ├── mobile!eliom.html.in ├── mobile!eliom_loader.ml ├── mobile!index.html.in ├── mobile!res!.gitignore ├── mobile!www!css!index.css ├── mobile!www!img!logo.png ├── sass!PROJECT_NAME.scss ├── sass!demo.scss ├── sass!os.scss ├── static!css!font-awesome.min.css ├── static!defaultcss!demo.css ├── static!defaultcss!os.css ├── static!fonts!FontAwesome.otf ├── static!fonts!fontawesome-webfont.eot ├── static!fonts!fontawesome-webfont.svg ├── static!fonts!fontawesome-webfont.ttf ├── static!fonts!fontawesome-webfont.woff ├── static!fonts!fontawesome-webfont.woff2 ├── static!images!ocsigen.png ├── tools!check_modules.ml ├── tools!dune ├── tools!gen_dune.ml └── tools!sort_deps.ml └── tools └── sort_deps.ml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | # Prime the caches every Monday 8 | - cron: 0 1 * * MON 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: 16 | - macos-latest 17 | - ubuntu-latest 18 | - windows-latest 19 | ocaml-compiler: 20 | - "4.14" 21 | - "5.2" 22 | include: 23 | - os: ubuntu-latest 24 | ocaml-compiler: "4.12" 25 | 26 | runs-on: ${{ matrix.os }} 27 | 28 | steps: 29 | - name: Checkout tree 30 | uses: actions/checkout@v4 31 | 32 | - name: Set-up Node.js 33 | uses: actions/setup-node@v4 34 | with: 35 | node-version: lts/* 36 | 37 | - name: Set-up OCaml 38 | uses: ocaml/setup-ocaml@v3 39 | with: 40 | ocaml-compiler: ${{ matrix.ocaml-compiler }} 41 | opam-pin: false 42 | 43 | - run: | 44 | opam pin add -n eliom https://github.com/ocsigen/eliom.git 45 | opam pin add -n ocsigen-toolkit https://github.com/ocsigen/ocsigen-toolkit.git 46 | 47 | - run: opam pin add ocsigen-start.7.0.0 . --no-action 48 | 49 | - run: opam install . 50 | 51 | - run: mkdir -p template 52 | 53 | - run: opam exec -- eliom-distillery -name template -template os.pgocaml 54 | 55 | - run: opam install -y . 56 | working-directory: template 57 | 58 | - run: make USE_SASS=no db-init db-create db-schema 59 | working-directory: template 60 | 61 | - run: opam exec -- make USE_SASS=no all 62 | working-directory: template 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | 11 | 12 | # ocsigen data 13 | .depend 14 | _client/ 15 | _deps/ 16 | _server/ 17 | local/ 18 | doc/client 19 | doc/server 20 | 21 | .*.sw* 22 | 23 | META 24 | Makefile.depend 25 | 26 | _opam 27 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | version=0.26.1 2 | break-cases = fit 3 | break-collection-expressions = fit-or-vertical 4 | break-fun-decl = wrap 5 | break-fun-sig = wrap 6 | break-infix = wrap 7 | break-infix-before-func = false 8 | break-sequences = false 9 | break-separators = before 10 | break-string-literals = never 11 | break-struct = force 12 | cases-matching-exp-indent = compact 13 | doc-comments = after-when-possible 14 | dock-collection-brackets = false 15 | indicate-multiline-delimiters = no 16 | infix-precedence = indent 17 | let-and = compact 18 | let-binding-spacing = compact 19 | module-item-spacing = compact 20 | parens-tuple = multi-line-only 21 | parens-tuple-patterns = multi-line-only 22 | sequence-style = terminator 23 | sequence-blank-line = compact 24 | single-case = compact 25 | type-decl = compact 26 | if-then-else = keyword-first 27 | field-space = loose 28 | space-around-arrays = false 29 | space-around-records = false 30 | space-around-lists = false 31 | space-around-variants = false 32 | ocp-indent-compat = true 33 | -------------------------------------------------------------------------------- /.ocp-indent: -------------------------------------------------------------------------------- 1 | normal 2 | with=0 3 | syntax=lwt mll 4 | max_indent=2 5 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | ===== 7.1.0 (2024-11) ===== 2 | * Compatibility with WASM 3 | 4 | ===== 7.0.0 (2024-09) ===== 5 | * Compatibility with Ocsigen Server 6 6 | * Build an app as an executable, with Ocsigen Server as a library, without config file 7 | 8 | ===== 6.3.0 (2024-03-25) ===== 9 | * Adding license Unlicense to the template 10 | * Dependecy to Tyxml >= 4.6 11 | 12 | ===== 6.2.0 (2024-02-18) ===== 13 | * Template simplification 14 | 15 | ===== 6.1.0 (2022-03-08) ===== 16 | * Adjust the server-side interface of Os_session.disconnect_all 17 | 18 | ===== 4.0.0 (2021-01-13) ===== 19 | * BREAKING CHANGE Os_tips: change the type of 'onclose' 20 | 21 | ===== 2.13.0 (2019-12-20) ===== 22 | * Os_tips: add unset_tip_seen 23 | 24 | ===== 2.11.0 (2019-12-10) ===== 25 | * Os_tips: add ?onclose parameter 26 | * Os_connect_phone: function to confirm a code without performing a sign-up 27 | 28 | ===== 2.8.0 (2019-11-15) ===== 29 | * restart app after disconnecting all my sessions 30 | * add "disconnect all sessions" button in parameters page 31 | 32 | ===== 2.7.0 (2019-10-17) ===== 33 | * Os_date.now: Make it possible to specify the timezone server side 34 | * Request cache: use cache only if during a request 35 | * compatibility with resource-pooling.1.0 36 | 37 | ===== 2.4.0 (2019-09-09) ===== 38 | 39 | * improved email error handling 40 | 41 | ===== 2.3.0 (2019-08-16) ===== 42 | 43 | * User connection: more detailed explanation of failed login 44 | 45 | ===== 2.2.0 (2019-07-17) ===== 46 | 47 | * implement Os_fcm_notif with Cohttp APIs 48 | 49 | ===== 2.0.0 (2019-07-09) ===== 50 | 51 | * rework scheduling of handlers for functions in Os_session: 52 | on_start_(un)connected_process, on_start_process 53 | 54 | ===== 1.8.0 (2019-06-28) ===== 55 | * change version of Sdk from 16 to 19 56 | * Os_notif: fix race condition during initialisation 57 | * Ot_pulltorefresh demo 58 | 59 | ===== 1.7.0 (2019-04-25) ===== 60 | * compatibility with js_of_ocaml.3.4.0 61 | 62 | ===== 1.6.0 (2019-03-29) ===== 63 | * use PPX syntax for PGOCaml code 64 | * remove deprecation warnings 65 | * better SMS activation codes 66 | 67 | ===== 1.5.0 (2019-01-17) ===== 68 | 69 | * Os_tips: cancel all scheduled tips on page change 70 | * Android push notifications: support setting channel IDs 71 | 72 | ===== 1.4.0 (2019-01-14) ===== 73 | 74 | * small fixes in the template and the documentation 75 | * Add function Os_tips.tip_seen 76 | 77 | ===== 1.3.0 (2018-12-26) ===== 78 | 79 | * Os_tips: additional features 80 | * change Bs_lib.reload to replace the current page 81 | 82 | ===== 1.2.0 (2018-12-19) ===== 83 | 84 | * Correct time zone handling (taking into account DST) 85 | * Improve and fix Android build instructions 86 | * Os_core_db: rollback if begin_work fails 87 | * rely on the resource-pooling package instead of Lwt_pool 88 | * Make sure users.main_email is synchronized with emails.email 89 | 90 | ===== 1.1 (2018-03-09) ===== 91 | 92 | * Compatibility with Eliom 6.3 and OCaml 4.06 93 | * Demo for Eliom's DOM caching 94 | * Registration by phone number 95 | * Various small fixes and improvements 96 | 97 | ===== 1.0 (2017-02-08) ===== 98 | 99 | * Initial stable version 100 | * Various bugfixes over 0.99 101 | 102 | ===== 0.99 (2016-12-12) ===== 103 | 104 | * Initial public version 105 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the [OCaml Code of Conduct](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md). 4 | 5 | # Enforcement 6 | 7 | This project follows the OCaml Code of Conduct [enforcement policy](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md#enforcement). 8 | 9 | To report any violations, please contact Jérôme 10 | Vouillon, Raphael Proust, Vincent Balat, Hugo Heuzard and Gabriel 11 | Radanne at 12 | (or some of them individually). 13 | -------------------------------------------------------------------------------- /META.in: -------------------------------------------------------------------------------- 1 | names = "@@PKG_NAME@@" 2 | version = "@@PKG_VERS@@" 3 | description = "@@PKG_DESC@@" 4 | 5 | package "server" ( 6 | requires = "@@SERVER_REQUIRES@@" 7 | directory = "server" 8 | archive(byte) = "@@SERVER_ARCHIVES_BYTE@@" 9 | archive(byte, plugin) = "@@SERVER_ARCHIVES_BYTE@@" 10 | archive(native) = "@@SERVER_ARCHIVES_NATIVE@@" 11 | archive(native, plugin) = "@@SERVER_ARCHIVES_NATIVE_PLUGIN@@" 12 | ) 13 | 14 | package "client" ( 15 | requires = "@@CLIENT_REQUIRES@@" 16 | directory = "client" 17 | archive(byte) = "@@CLIENT_ARCHIVES_BYTE@@" 18 | archive(byte, plugin) = "@@CLIENT_ARCHIVES_BYTE@@" 19 | ) 20 | -------------------------------------------------------------------------------- /Makefile.options: -------------------------------------------------------------------------------- 1 | 2 | #---------------------------------------------------------------------- 3 | # SETTINGS FOR YOUR PACKAGE 4 | #---------------------------------------------------------------------- 5 | 6 | # Package name for your 7 | PKG_NAME := ocsigen-start 8 | PKG_VERS := 1.1.0 9 | PKG_DESC := Skeleton for building your own Eliom application 10 | 11 | # Source files for the server 12 | SERVER_FILES := $(filter-out %.ppx.ml,$(wildcard \ 13 | src/*.ml* \ 14 | src/*.eliom* \ 15 | src/view/*.eliom* \ 16 | )) 17 | # Source files for the client 18 | CLIENT_FILES := $(wildcard \ 19 | src/*.eliom* \ 20 | src/view/*.eliom* \ 21 | ) 22 | 23 | # Source files for the server 24 | SERVER_FILES_DOC := $(wildcard \ 25 | src/*.eliomi \ 26 | src/*.mli \ 27 | src/view/*.eliomi \ 28 | ) 29 | # Source files for the client 30 | CLIENT_FILES_DOC := $(wildcard \ 31 | src/*.eliomi \ 32 | src/view/*.eliomi \ 33 | ) 34 | 35 | # Template directory (used when installing template with distillery) 36 | TEMPLATE_DIR := template.distillery 37 | 38 | # Template name (name used by distillery) 39 | TEMPLATE_NAME := os.pgocaml 40 | 41 | ##---------------------------------------------------------------------- 42 | ## Generate CSS from SASS files in the template. 43 | 44 | SCSS_FILES := $(TEMPLATE_DIR)/sass\!os.scss \ 45 | $(TEMPLATE_DIR)/sass\!demo.scss 46 | 47 | CSS_FILES := $(TEMPLATE_DIR)/static\!defaultcss\!os.css \ 48 | $(TEMPLATE_DIR)/static\!defaultcss\!demo.css 49 | 50 | SASS_TEMPORARY_FILENAME := .os_temporary_filename 51 | 52 | SASS_TEMPORARY_PROJECT_NAME := os_temporary_project_name 53 | 54 | ##---------------------------------------------------------------------- 55 | 56 | # OCamlfind packages for the server 57 | SERVER_PACKAGES := calendar safepass \ 58 | ocsigen-toolkit.server yojson re.str cohttp-lwt-unix 59 | 60 | SERVER_PPX_PACKAGES := js_of_ocaml-ppx_deriving_json ocsigen-ppx-rpc 61 | 62 | SERVER_DB_PACKAGES := pgocaml calendar safepass resource-pooling 63 | 64 | SERVER_DB_PPX_PACKAGES := pgocaml_ppx 65 | 66 | # OCamlfind packages for the client 67 | CLIENT_PACKAGES := calendar ocsigen-toolkit.client re.str 68 | 69 | CLIENT_PPX_PACKAGES := js_of_ocaml-ppx_deriving_json js_of_ocaml-ppx ocsigen-ppx-rpc 70 | 71 | # Debug package (yes/no): Debugging info in compilation 72 | DEBUG := yes 73 | 74 | # Native mode (yes/no): Compile also with native mode (it will always compile with byte mode) 75 | NATIVE := yes 76 | 77 | # Package's library $(PKG_NAME).{client,server}.cma (a,cmxa,cmxs only server side) 78 | LIBDIR := lib/ 79 | 80 | WARNING_FLAGS=-w +A@3-4@5..15-9@20..68-37-39-44-48-69 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ocsigen Start [![Build](https://github.com/ocsigen/ocsigen-start/actions/workflows/build.yml/badge.svg)](https://github.com/ocsigen/ocsigen-start/actions/workflows/build.yml) 2 | 3 | You can try the [online version](https://ocsigen.org/ocsigen-start/demo) and 4 | download 5 | the 6 | [Android application](https://play.google.com/store/apps/details?id=com.osdemo.mobile&hl=en). 7 | 8 | 1. [Introduction](#introduction) 9 | 2. [Installation](#install) 10 | 3. [Create your project](#create-your-project) 11 | 12 | ### Introduction 13 | Ocsigen Start is a set of higher-level libraries for building 14 | client-server web applications with Ocsigen (Js_of_ocaml and 15 | Eliom). It provides modules for 16 | * user management (session management, registration, action — e.g., activation — keys, ...), 17 | * managing groups of users, 18 | * displaying tips, and 19 | * easily sending notifications to the users. 20 | 21 | Ocsigen Start comes with an `eliom-distillery` template for an app 22 | with a database, user management, and session management. This 23 | template is intended to serve as a basis for quickly building the 24 | Minimum Viable Product for web applications with users. The goal is to 25 | enable the programmer to concentrate on the core of the app, and not 26 | on user management. 27 | 28 | If Ocsigen Start corresponds to your needs, it will probably help you 29 | a lot. If not, start with a simpler template. You can still use the 30 | modules from Ocsigen Start. 31 | 32 | ### Installation 33 | 34 | We recommend using OPAM to install Ocsigen Start. Here is the command: 35 | 36 | ``` 37 | opam install ocsigen-start 38 | ``` 39 | 40 | ### Create your project 41 | ``` 42 | eliom-distillery -name myproject -template os.pgocaml 43 | ``` 44 | 45 | To get started, take a look at the generated README.md. 46 | 47 | You have also the complete manual and API available on 48 | the [Ocsigen website](http://ocsigen.org/ocsigen-start/) 49 | -------------------------------------------------------------------------------- /doc/index.wiki: -------------------------------------------------------------------------------- 1 | = Ocsigen Start -- API reference 2 | 3 | <>. 4 | 5 | Follow the links on the left for API documentation. 6 | -------------------------------------------------------------------------------- /doc/indexdoc.client: -------------------------------------------------------------------------------- 1 | {1 Client API} 2 | 3 | {!modules: 4 | Os_current_user 5 | Os_date 6 | Os_handlers 7 | Os_icons 8 | Os_lib 9 | Os_msg 10 | Os_notif 11 | Os_page 12 | Os_platform 13 | Os_request_cache 14 | Os_services 15 | Os_session 16 | Os_tips 17 | Os_types 18 | Os_user 19 | Os_user_proxy 20 | Os_user_view 21 | } 22 | 23 | {2 Index} 24 | 25 | {!indexlist} 26 | -------------------------------------------------------------------------------- /doc/indexdoc.server: -------------------------------------------------------------------------------- 1 | {1 Server API} 2 | 3 | {!modules: 4 | Os_current_user 5 | Os_date 6 | Os_db 7 | Os_email 8 | Os_fcm_notif 9 | Os_group 10 | Os_handlers 11 | Os_icons 12 | Os_lib 13 | Os_msg 14 | Os_notif 15 | Os_page 16 | Os_platform 17 | Os_request_cache 18 | Os_services 19 | Os_session 20 | Os_tips 21 | Os_types 22 | Os_uploader 23 | Os_user 24 | Os_user_proxy 25 | Os_user_view 26 | } 27 | 28 | {2 Index} 29 | 30 | {!indexlist} 31 | -------------------------------------------------------------------------------- /opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "ocsigen-start" 3 | version: "7.1.0" 4 | authors: "dev@ocsigen.org" 5 | maintainer: "dev@ocsigen.org" 6 | synopsis: "Higher-level library for developing Web and mobile applications with users, registration, notifications, etc" 7 | description: """ 8 | Ocsigen Start is a set of higher-level libraries for building client-server Web and mobile applications with Ocsigen (Js_of_ocaml and Eliom). 9 | It provides modules for user management (session management, registration, activation keys, ...), managing groups of users, displaying tips, and easily sending notifications to the users. 10 | Ocsigen Start comes with an eliom-distillery template for an app with a database, user management, and session management. 11 | This template is intended to serve as a basis for quickly building the Minimum Viable Product for Web and mobile applications with users. 12 | The goal is to enable the programmer to concentrate on the core of the app, and not on user management. 13 | """ 14 | homepage: "https://ocsigen.org/ocsigen-start/" 15 | bug-reports: "https://github.com/ocsigen/ocsigen-start/issues" 16 | dev-repo: "git+https://github.com/ocsigen/ocsigen-start.git" 17 | license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception" 18 | build: [ make "-j%{jobs}%" ] 19 | install: [ make "install" ] 20 | depends: [ 21 | "ocaml" {>= "4.08.1"} 22 | "pgocaml" {>= "4.0"} 23 | "pgocaml_ppx" {>= "4.0"} 24 | "safepass" {>= "3.0"} 25 | "ocsigen-i18n" {>= "4.0.0"} 26 | "eliom" {>= "11.0.0" & < "12.0.0"} 27 | "ocsigen-toolkit" {>= "2.7.0"} 28 | "ocsigen-ppx-rpc" 29 | "ocsigen-i18n" {>= "3.7.0"} 30 | "yojson" {>= "1.6.0"} 31 | "resource-pooling" {>= "1.0" & < "2.0"} 32 | "cohttp-lwt-unix" 33 | "js_of_ocaml" {>= "6.0.0"} 34 | "re" {>= "1.7.2"} 35 | ] 36 | depexts: [ 37 | ["imagemagick" "ruby-sass" "postgresql" "postgresql-common"] {os-family = "debian"} 38 | ["postgresql" "md5sha1sum" "sassc"] {os = "macos" & os-distribution = "homebrew"} 39 | ] 40 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | WARNING! 2 | 3 | Did you update the template to your changes in library? 4 | 5 | To test the template: 6 | 7 | eliom-distillery -template os.pgocaml -name test 8 | cd test 9 | make db-init 10 | make db-create 11 | make db-schema 12 | make test.byte (or test.opt) 13 | -------------------------------------------------------------------------------- /scripts/2distillery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | usage() { 4 | echo "usage: $0 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /template.distillery/mobile!index.html.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 15 | 16 | 17 | 18 | 19 | %%MOBILE_APP_NAME%% 20 | 21 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /template.distillery/mobile!res!.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /template.distillery/mobile!www!css!index.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used in index.html when the mobile application starts. 3 | * - *app* and *blink* classes defines the style for the app loader image. 4 | * - *retry-button* and *app-error* is used when an error occurs while updating 5 | * the application. If an error occurs, *app-error* will replace *app*. 6 | * - the logo (saved in img/logo.png) is set as background of the DOM elements 7 | * which contains *app* or *app-error* class. 8 | * 9 | * See eliom_loader.ml and index.html for more information. 10 | */ 11 | 12 | /* ------------------------------------------------------------- */ 13 | /* Defines rules for the animation. It is used by *blink* class. */ 14 | 15 | @keyframes fade { 16 | from { opacity: 1.0; } 17 | 50% { opacity: 0.4; } 18 | to { opacity: 1.0; } 19 | } 20 | 21 | @-webkit-keyframes fade { 22 | from { opacity: 1.0; } 23 | 50% { opacity: 0.4; } 24 | to { opacity: 1.0; } 25 | } 26 | 27 | * { 28 | -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */ 29 | } 30 | 31 | body { 32 | -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ 33 | -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */ 34 | -webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */ 35 | background-color:#FFFFFF; 36 | font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif; 37 | height:100%; 38 | margin:0px; 39 | padding:0px; 40 | width:100%; 41 | } 42 | 43 | /* --------------------- */ 44 | /* app and error classes */ 45 | 46 | /* Portrait layout (default) */ 47 | .app, /* text area height */ 48 | .app-error{ 49 | background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */ 50 | position:absolute; /* position in the center of the screen */ 51 | left:50%; 52 | top:50%; 53 | height:50px; /* text area height */ 54 | width:225px; /* text area width */ 55 | text-align:center; 56 | padding:180px 0px 0px 0px; /* image height is 200px (bottom 20px are overlapped with text) */ 57 | margin:-115px 0px 0px -112px; /* offset vertical: half of image height and text area height */ 58 | /* offset horizontal: half of text area width */ 59 | font-size: 16px; 60 | } 61 | 62 | /* Landscape layout (with min-width) */ 63 | @media screen and (min-aspect-ratio: 1/1) and (min-width:400px) { 64 | .app { 65 | padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */ 66 | margin:-90px 0px 0px -198px; /* offset vertical: half of image height */ 67 | } 68 | .app-error { 69 | padding:40px 0px 75px 420px; /* padding-top + padding-bottom + text area = image height */ 70 | margin:-90px 0px 0px -450px; /* offset vertical: half of image height */ 71 | /* offset horizontal: half of image width and text area width */ 72 | } 73 | } 74 | 75 | /* -------------------------------- */ 76 | /* This class is the fade animation */ 77 | 78 | .blink { 79 | animation:fade 3000ms infinite; 80 | -webkit-animation:fade 3000ms infinite; 81 | } 82 | 83 | /* --------------------------------------------------------------- */ 84 | /* Style for the retry button when an error occurs while updating */ 85 | 86 | #retry-button { 87 | width: 100%; 88 | display: block; 89 | margin-top: 1.8rem; 90 | margin-bottom: 1.8rem; 91 | background-color: #64b5f6; 92 | color: white; 93 | height: 35px; 94 | border: none; 95 | font-size: 16px; 96 | } 97 | -------------------------------------------------------------------------------- /template.distillery/mobile!www!img!logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/mobile!www!img!logo.png -------------------------------------------------------------------------------- /template.distillery/sass!PROJECT_NAME.scss: -------------------------------------------------------------------------------- 1 | @import "font-awesome.min.css"; 2 | @import "ot_buttons.css"; 3 | @import "ot_carousel.css"; 4 | @import "ot_sticky.css"; 5 | @import "ot_datetime.css"; 6 | @import "ot_drawer.css"; 7 | @import "ot_icons.css"; 8 | @import "ot_picture_uploader.css"; 9 | @import "ot_popup.css"; 10 | @import "ot_spinner.css"; 11 | @import "ot_page_transition.css"; 12 | @import "ot_tongue.css"; 13 | @import "os"; 14 | @import "demo"; 15 | -------------------------------------------------------------------------------- /template.distillery/sass!demo.scss: -------------------------------------------------------------------------------- 1 | /* This file was generated by Ocsigen Start. 2 | Feel free to use it, modify it, and redistribute it as you wish. 3 | */ 4 | .demo-carousel1 { 5 | display: flex; 6 | justify-content: center; 7 | .demo-carousel1-box { 8 | position: relative; 9 | margin: auto; 10 | } 11 | .ot-carousel { 12 | width: 300px; 13 | height: 200px; 14 | } 15 | .demo-carousel1-page { 16 | width: 100%; 17 | height: 100%; 18 | padding: 16px; 19 | color: white; 20 | } 21 | .demo-carousel1-page-1 { 22 | background-color: #49b2cc; 23 | } 24 | .demo-carousel1-page-2 { 25 | background-color: #dddd55; 26 | } 27 | .demo-carousel1-page-3 { 28 | background-color: #c14768; 29 | } 30 | .demo-carousel1-page-4 { 31 | background-color: #45bf7d; 32 | } 33 | .ot-bullet-nav { 34 | position: absolute; 35 | bottom: 16px; 36 | right: 16px; 37 | margin: 0; 38 | } 39 | .ot-car-prev, 40 | .ot-car-next { 41 | position: absolute; 42 | top: 75px; 43 | width: 50px; 44 | height: 50px; 45 | color: white; 46 | outline: none; 47 | } 48 | .ot-car-prev { 49 | left: 0; 50 | } 51 | .ot-car-next { 52 | right: 0; 53 | } 54 | } 55 | .os-page-demo-carousel2 { 56 | .demo-carousel2 { 57 | margin: 0 -16px; 58 | } 59 | .ot-carousel { 60 | width: 100%; 61 | height: auto; 62 | } 63 | .demo-carousel2-page { 64 | padding: 16px; 65 | p { 66 | text-align: justify; 67 | } 68 | } 69 | .demo-carousel2-page-1 { 70 | background-color: white; 71 | } 72 | .demo-carousel2-page-2 { 73 | background-color: #ffffee; 74 | } 75 | .demo-carousel2-page-3 { 76 | background-color: #ffddee; 77 | } 78 | .demo-carousel2-page-4 { 79 | background-color: #ddffee; 80 | } 81 | .demo-carousel2-tabs { 82 | position: sticky; 83 | background-color: white; 84 | z-index: 1; 85 | top: 50px; 86 | } 87 | } 88 | .os-page-demo-carousel3 { 89 | .demo-prev, 90 | .demo-next { 91 | width: 20px; 92 | height: 20px; 93 | background-color: #6ae; 94 | color: white; 95 | } 96 | } 97 | .os-page-demo-notif, 98 | .os-page-demo-react { 99 | input:not([type]) { 100 | background-color: #eee; 101 | } 102 | } 103 | .os-page-demo-links .demo-static-img { 104 | width: 300px; 105 | } 106 | 107 | .os-page-demo-transition { 108 | .demo-list { 109 | padding-left: 0; 110 | list-style-type: none; 111 | } 112 | 113 | .demo-list-item { 114 | width: 100%; 115 | height: 150px; 116 | font-size: 20px; 117 | padding-top: 65px; 118 | text-align: center; 119 | } 120 | 121 | .demo-list-item > a:visited, 122 | .demo-list-item > a { 123 | color: white; 124 | } 125 | 126 | .demo-list-item.demo-list-item-0 { 127 | background-color: #b1eb00; 128 | } 129 | .demo-list-item.demo-list-item-1 { 130 | background-color: #53baf3; 131 | } 132 | .demo-list-item.demo-list-item-2 { 133 | background-color: #ff85cb; 134 | } 135 | .demo-list-item.demo-list-item-3 { 136 | background-color: #f4402c; 137 | } 138 | .demo-list-item.demo-list-item-4 { 139 | background-color: #ffac00; 140 | } 141 | 142 | .demo-button { 143 | margin: 0 auto; 144 | padding: 20px; 145 | width: 200px; 146 | font-size: 20px; 147 | text-align: center; 148 | background-color: #ffffee; 149 | } 150 | } 151 | .demo-tongue { 152 | display: flex; 153 | flex-direction: column; 154 | .demo-tongue-1 { 155 | background-color: #ffffee; 156 | height: 100px; 157 | } 158 | .demo-tongue-2 { 159 | background-color: #ffddee; 160 | height: 150px; 161 | } 162 | .demo-tongue-3 { 163 | background-color: #ddffee; 164 | height: 80px; 165 | } 166 | .demo-tongue-4 { 167 | background-color: #ddeeff; 168 | height: 200px; 169 | } 170 | .demo-tongue-5 { 171 | background-color: #eeccff; 172 | height: 130px; 173 | } 174 | .demo-tongue-6 { 175 | background-color: #ffeeaa; 176 | height: 300px; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /template.distillery/static!defaultcss!demo.css: -------------------------------------------------------------------------------- 1 | /* This file was generated by Ocsigen Start. 2 | Feel free to use it, modify it, and redistribute it as you wish. 3 | */ 4 | .demo-carousel1 { 5 | display: flex; 6 | justify-content: center; } 7 | .demo-carousel1 .demo-carousel1-box { 8 | position: relative; 9 | margin: auto; } 10 | .demo-carousel1 .ot-carousel { 11 | width: 300px; 12 | height: 200px; } 13 | .demo-carousel1 .demo-carousel1-page { 14 | width: 100%; 15 | height: 100%; 16 | padding: 16px; 17 | color: white; } 18 | .demo-carousel1 .demo-carousel1-page-1 { 19 | background-color: #49b2cc; } 20 | .demo-carousel1 .demo-carousel1-page-2 { 21 | background-color: #dddd55; } 22 | .demo-carousel1 .demo-carousel1-page-3 { 23 | background-color: #c14768; } 24 | .demo-carousel1 .demo-carousel1-page-4 { 25 | background-color: #45bf7d; } 26 | .demo-carousel1 .ot-bullet-nav { 27 | position: absolute; 28 | bottom: 16px; 29 | right: 16px; 30 | margin: 0; } 31 | .demo-carousel1 .ot-car-prev, .demo-carousel1 .ot-car-next { 32 | position: absolute; 33 | top: 75px; 34 | width: 50px; 35 | height: 50px; 36 | color: white; 37 | outline: none; } 38 | .demo-carousel1 .ot-car-prev { 39 | left: 0; } 40 | .demo-carousel1 .ot-car-next { 41 | right: 0; } 42 | 43 | .os-page-demo-carousel2 .demo-carousel2 { 44 | margin: 0 -16px; } 45 | .os-page-demo-carousel2 .ot-carousel { 46 | width: 100%; 47 | height: auto; } 48 | .os-page-demo-carousel2 .demo-carousel2-page { 49 | padding: 16px; } 50 | .os-page-demo-carousel2 .demo-carousel2-page p { 51 | text-align: justify; } 52 | .os-page-demo-carousel2 .demo-carousel2-page-1 { 53 | background-color: white; } 54 | .os-page-demo-carousel2 .demo-carousel2-page-2 { 55 | background-color: #ffffee; } 56 | .os-page-demo-carousel2 .demo-carousel2-page-3 { 57 | background-color: #ffddee; } 58 | .os-page-demo-carousel2 .demo-carousel2-page-4 { 59 | background-color: #ddffee; } 60 | .os-page-demo-carousel2 .demo-carousel2-tabs { 61 | position: sticky; 62 | background-color: white; 63 | z-index: 1; 64 | top: 50px; } 65 | 66 | .os-page-demo-carousel3 .demo-prev, .os-page-demo-carousel3 .demo-next { 67 | width: 20px; 68 | height: 20px; 69 | background-color: #6ae; 70 | color: white; } 71 | 72 | .os-page-demo-notif input:not([type]), 73 | .os-page-demo-react input:not([type]) { 74 | background-color: #eee; } 75 | 76 | .os-page-demo-links .demo-static-img { 77 | width: 300px; } 78 | 79 | .os-page-demo-transition .demo-list{ 80 | padding-left: 0; 81 | list-style-type: none; 82 | } 83 | 84 | .os-page-demo-transition .demo-list-item { 85 | width: 100%; 86 | height: 150px; 87 | font-size: 20px; 88 | padding-top: 65px; 89 | text-align: center; 90 | } 91 | 92 | .os-page-demo-transition .demo-list-item>a:visited, 93 | .os-page-demo-transition .demo-list-item>a { 94 | color: white; 95 | } 96 | 97 | .os-page-demo-transition .demo-list-item.demo-list-item-0 { 98 | background-color: #B1EB00 99 | } 100 | .os-page-demo-transition .demo-list-item.demo-list-item-1 { 101 | background-color: #53BAF3 102 | } 103 | .os-page-demo-transition .demo-list-item.demo-list-item-2 { 104 | background-color: #FF85CB 105 | } 106 | .os-page-demo-transition .demo-list-item.demo-list-item-3 { 107 | background-color: #F4402C 108 | } 109 | .os-page-demo-transition .demo-list-item.demo-list-item-4 { 110 | background-color: #FFAC00 111 | } 112 | 113 | .os-page-demo-transition .demo-button { 114 | margin: 0 auto; 115 | padding: 20px; 116 | width: 200px; 117 | font-size: 20px; 118 | text-align: center; 119 | background-color: #ffffee; 120 | } 121 | 122 | /*# sourceMappingURL=.os_temporary_filename.css.map */ 123 | -------------------------------------------------------------------------------- /template.distillery/static!fonts!FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/static!fonts!FontAwesome.otf -------------------------------------------------------------------------------- /template.distillery/static!fonts!fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/static!fonts!fontawesome-webfont.eot -------------------------------------------------------------------------------- /template.distillery/static!fonts!fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/static!fonts!fontawesome-webfont.ttf -------------------------------------------------------------------------------- /template.distillery/static!fonts!fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/static!fonts!fontawesome-webfont.woff -------------------------------------------------------------------------------- /template.distillery/static!fonts!fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/static!fonts!fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /template.distillery/static!images!ocsigen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocsigen/ocsigen-start/a4dcc73734413d1340371bee48e84fe4897eba15/template.distillery/static!images!ocsigen.png -------------------------------------------------------------------------------- /template.distillery/tools!check_modules.ml: -------------------------------------------------------------------------------- 1 | #load "unix.cma" 2 | 3 | #load "str.cma" 4 | 5 | let app = Sys.argv.(1) 6 | 7 | let modules_from_bytecode_executable nm = 8 | let ch = Unix.open_process_in (Printf.sprintf "ocamlobjinfo %s" nm) in 9 | while input_line ch <> "Imported units:" do 10 | () 11 | done; 12 | let lst = ref [] in 13 | (try 14 | while 15 | let l = input_line ch in 16 | if l <> "" && l.[0] = '\t' 17 | then ( 18 | let i = String.rindex l '\t' in 19 | lst := String.sub l (i + 1) (String.length l - i - 1) :: !lst; 20 | true) 21 | else false 22 | do 23 | () 24 | done 25 | with End_of_file -> ()); 26 | !lst 27 | 28 | let modules_from_bytecode_library nm = 29 | let ch = Unix.open_process_in (Printf.sprintf "ocamlobjinfo %s" nm) in 30 | let lst = ref [] in 31 | (try 32 | while true do 33 | let l = input_line ch in 34 | if String.length l > 11 && String.sub l 0 11 = "Unit name: " 35 | then lst := String.sub l 11 (String.length l - 11) :: !lst 36 | done 37 | with End_of_file -> ()); 38 | !lst 39 | 40 | let read_file f = 41 | let ch = open_in f in 42 | let s = really_input_string ch (in_channel_length ch) in 43 | close_in ch; s 44 | 45 | let section_re = Str.regexp "close_\\(server\\|client\\)_section" 46 | 47 | let match_substring sub_re s = 48 | try 49 | ignore (Str.search_forward sub_re s 0); 50 | true 51 | with Not_found -> false 52 | 53 | let eliom_modules dir = 54 | Sys.readdir dir |> Array.to_list |> List.sort compare 55 | |> List.filter_map @@ fun nm -> 56 | if Filename.check_suffix nm ".pp.eliom" 57 | then 58 | let f = read_file (Filename.concat dir nm) in 59 | Some 60 | ( String.capitalize_ascii (Filename.chop_suffix nm ".pp.eliom") 61 | , match_substring section_re f ) 62 | else None 63 | 64 | let print_modules side l = 65 | Format.printf "[%%%%%s.start]@.@." side; 66 | List.iter (fun m -> Format.printf "module %s = %s@." m m) l; 67 | Format.printf "@." 68 | 69 | let _ = 70 | let client_modules = 71 | modules_from_bytecode_executable (Printf.sprintf "./client/%s.bc" app) 72 | in 73 | let server_modules = 74 | modules_from_bytecode_library (Printf.sprintf "./%s.cma" app) 75 | in 76 | let eliom_modules = eliom_modules "." in 77 | let missing_server_modules = 78 | List.filter_map 79 | (fun (m, sect) -> 80 | let c = List.mem m client_modules in 81 | let s = List.mem m server_modules in 82 | match c, s, sect with true, false, true -> Some m | _ -> None) 83 | eliom_modules 84 | in 85 | let missing_client_modules = 86 | List.filter_map 87 | (fun (m, sect) -> 88 | let c = List.mem m client_modules in 89 | let s = List.mem m server_modules in 90 | match c, s, sect with false, true, true -> Some m | _ -> None) 91 | eliom_modules 92 | in 93 | let missing_modules = 94 | missing_server_modules <> [] || missing_client_modules <> [] 95 | in 96 | if missing_modules 97 | then Format.eprintf "Some modules are missing in %s.eliom:@.@." app; 98 | if missing_server_modules <> [] 99 | then print_modules "server" missing_server_modules; 100 | if missing_client_modules <> [] 101 | then print_modules "client" missing_client_modules; 102 | if missing_modules then exit 1 103 | -------------------------------------------------------------------------------- /template.distillery/tools!dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name eliom_ppx_client) 3 | (modes native) 4 | (modules eliom_ppx_client) 5 | (libraries ocsigen-ppx-rpc eliom.ppx.client)) 6 | 7 | (rule 8 | (action (with-stdout-to eliom_ppx_client.ml 9 | (echo "let () = Ppxlib.Driver.standalone ()")))) 10 | -------------------------------------------------------------------------------- /template.distillery/tools!gen_dune.ml: -------------------------------------------------------------------------------- 1 | let with_suffixes nm l f = 2 | List.iter 3 | (fun suffix -> 4 | if Filename.check_suffix nm suffix then f (Filename.chop_suffix nm suffix)) 5 | l 6 | 7 | let handle_file_client nm = 8 | if Filename.check_suffix nm ".pp.eliom" then () 9 | else if Filename.check_suffix nm ".pp.eliomi" then () 10 | else 11 | with_suffixes nm [ ".eliom"; ".tsv" ] (fun nm -> 12 | Printf.printf 13 | "(rule (target %s.ml) (deps ../%s.eliom)\n\ 14 | \ (action\n\ 15 | \ (with-stdout-to %%{target}\n\ 16 | \ (chdir .. (run tools/eliom_ppx_client.exe --as-pp -server-cmo \ 17 | %%{cmo:../%s} --impl %s.eliom)))))\n" 18 | nm nm nm nm); 19 | if Filename.check_suffix nm ".eliomi" then 20 | let nm = Filename.chop_suffix nm ".eliomi" in 21 | Printf.printf 22 | "(rule (target %s.mli) (deps ../%s.eliomi)\n\ 23 | \ (action\n\ 24 | \ (with-stdout-to %%{target}\n\ 25 | \ (chdir .. (run tools/eliom_ppx_client.exe --as-pp --intf \ 26 | %%{deps})))))\n" 27 | nm nm 28 | 29 | let () = 30 | Array.concat (List.map Sys.readdir [ "../../.."; "../../../assets" ]) 31 | |> Array.to_list |> List.sort compare 32 | |> List.filter (fun nm -> nm.[0] <> '.') 33 | |> List.iter handle_file_client 34 | -------------------------------------------------------------------------------- /template.distillery/tools!sort_deps.ml: -------------------------------------------------------------------------------- 1 | #load "str.cma" 2 | 3 | let space_re = Str.regexp " +" 4 | let edges = Hashtbl.create 128 5 | let edge_count = Hashtbl.create 128 6 | 7 | let chop s = 8 | try 9 | let i = String.rindex s '.' in 10 | String.sub s 0 i 11 | with Not_found -> s 12 | 13 | let add_edge target dep = 14 | if target <> dep 15 | then ( 16 | Hashtbl.replace edges dep 17 | (target :: (try Hashtbl.find edges dep with Not_found -> [])); 18 | Hashtbl.replace edge_count target 19 | (1 + try Hashtbl.find edge_count target with Not_found -> 0); 20 | if not (Hashtbl.mem edge_count dep) then Hashtbl.add edge_count dep 0) 21 | 22 | let sort l = 23 | let res = ref [] in 24 | List.iter 25 | (fun (target, deps) -> 26 | let target = chop target in 27 | if not (Hashtbl.mem edge_count target) 28 | then Hashtbl.add edge_count target 0; 29 | List.iter (fun dep -> add_edge target (chop dep)) deps) 30 | l; 31 | let q = Queue.create () in 32 | Hashtbl.iter 33 | (fun target count -> if count = 0 then Queue.add target q) 34 | edge_count; 35 | while not (Queue.is_empty q) do 36 | let n = Queue.take q in 37 | res := n :: !res; 38 | let l = try Hashtbl.find edges n with Not_found -> [] in 39 | Hashtbl.remove edges n; 40 | List.iter 41 | (fun target -> 42 | let c = Hashtbl.find edge_count target - 1 in 43 | Hashtbl.replace edge_count target c; 44 | if c = 0 then Queue.add target q) 45 | l 46 | done; 47 | if Hashtbl.length edges <> 0 48 | then ( 49 | Format.eprintf "Dependency loop!@."; 50 | exit 1); 51 | List.rev !res 52 | 53 | let _ = 54 | let ch = open_in Sys.argv.(1) in 55 | let lst = ref [] in 56 | (try 57 | while true do 58 | let l = input_line ch in 59 | let l = Str.split space_re l in 60 | match l with 61 | | target :: ":" :: deps -> lst := (target, deps) :: !lst 62 | | _ -> assert false 63 | done 64 | with End_of_file -> ()); 65 | let lst = sort !lst in 66 | let files = Hashtbl.create 128 in 67 | for i = 2 to Array.length Sys.argv - 1 do 68 | Hashtbl.add files (chop Sys.argv.(i)) Sys.argv.(i) 69 | done; 70 | List.iter 71 | (fun f -> 72 | try Format.printf "%s@." (Hashtbl.find files f) with Not_found -> ()) 73 | lst 74 | -------------------------------------------------------------------------------- /tools/sort_deps.ml: -------------------------------------------------------------------------------- 1 | (* Ocsigen-start 2 | * http://www.ocsigen.org/ocsigen-start 3 | * 4 | * Copyright (C) Université Paris Diderot, CNRS, INRIA, Be Sport. 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, with linking exception; 9 | * either version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 | *) 20 | #load "str.cma" 21 | 22 | let space_re = Str.regexp " +" 23 | let edges = Hashtbl.create 128 24 | let edge_count = Hashtbl.create 128 25 | 26 | let chop s = 27 | try 28 | let i = String.rindex s '.' in 29 | String.sub s 0 i 30 | with Not_found -> s 31 | 32 | let add_edge target dep = 33 | if target <> dep 34 | then ( 35 | Hashtbl.replace edges dep 36 | (target :: (try Hashtbl.find edges dep with Not_found -> [])); 37 | Hashtbl.replace edge_count target 38 | (1 + try Hashtbl.find edge_count target with Not_found -> 0); 39 | if not (Hashtbl.mem edge_count dep) then Hashtbl.add edge_count dep 0) 40 | 41 | let sort l = 42 | let res = ref [] in 43 | List.iter 44 | (fun (target, deps) -> 45 | let target = chop target in 46 | if not (Hashtbl.mem edge_count target) 47 | then Hashtbl.add edge_count target 0; 48 | List.iter (fun dep -> add_edge target (chop dep)) deps) 49 | l; 50 | let q = Queue.create () in 51 | Hashtbl.iter 52 | (fun target count -> if count = 0 then Queue.add target q) 53 | edge_count; 54 | while not (Queue.is_empty q) do 55 | let n = Queue.take q in 56 | res := n :: !res; 57 | let l = try Hashtbl.find edges n with Not_found -> [] in 58 | Hashtbl.remove edges n; 59 | List.iter 60 | (fun target -> 61 | let c = Hashtbl.find edge_count target - 1 in 62 | Hashtbl.replace edge_count target c; 63 | if c = 0 then Queue.add target q) 64 | l 65 | done; 66 | if Hashtbl.length edges <> 0 67 | then 68 | Hashtbl.iter 69 | (fun k0 deps -> 70 | let h = Hashtbl.create 42 in 71 | let rec follow acc k = 72 | if k0 = k 73 | then ( 74 | Format.eprintf "Dependency loop: %s@." (String.concat " -> " acc); 75 | exit 1) 76 | else if Hashtbl.mem h k 77 | then () 78 | else ( 79 | Hashtbl.add h k (); 80 | try List.iter (follow (k :: acc)) (Hashtbl.find edges k) 81 | with Not_found -> ()) 82 | in 83 | List.iter (follow [k0]) deps) 84 | edges; 85 | List.rev !res 86 | 87 | let _ = 88 | let ch = open_in Sys.argv.(1) in 89 | let lst = ref [] in 90 | (try 91 | while true do 92 | let l = input_line ch in 93 | let l = Str.split space_re l in 94 | match l with 95 | | target :: ":" :: deps -> lst := (target, deps) :: !lst 96 | | _ -> assert false 97 | done 98 | with End_of_file -> ()); 99 | let lst = sort !lst in 100 | let files = Hashtbl.create 128 in 101 | for i = 2 to Array.length Sys.argv - 1 do 102 | Hashtbl.add files (chop Sys.argv.(i)) Sys.argv.(i) 103 | done; 104 | List.iter 105 | (fun f -> 106 | try Format.printf "%s@." (Hashtbl.find files f) with Not_found -> ()) 107 | lst 108 | --------------------------------------------------------------------------------