From 56d1b42dce4d715ad7ed2510b6285b0ed1204547 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Fri, 12 Dec 2025 21:32:52 -0600 Subject: [PATCH 01/15] using working URLs in README.md archive.org shows that pbwiki redirected to pbworks, which still works, but is so stale that a link to the (empty) github wiki still makes more sense IMO --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a85454c..3518a0e 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Examples are given in the `examples/` subdirectory. Installation: see INSTALL file -Documentation: http://ocaml.pbwiki.com/Ocamlscript +Documentation: https://github.com/ocaml-community/ocamlscript/wiki -Primary download site: http://mjambon.com/ocamlscript.html +Primary download site: https://github.com/ocaml-community/ocamlscript Authors: David Mentre and Martin Jambon From 1c91af5e2b95c1f7e936d2d435735ab1ac3317bf Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Fri, 12 Dec 2025 21:59:20 -0600 Subject: [PATCH 02/15] required for Topdirs after ocaml/ocaml#11979 --- main.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/main.ml b/main.ml index 288526a..fd1c357 100644 --- a/main.ml +++ b/main.ml @@ -251,6 +251,7 @@ let write_header ~pos ~source ~source_option ~verbose ~prog_file lines = fprintf oc "\ #%i %S;; (* Opam installations of findlib place topfind in a different directory *) +#directory \"+compiler-libs\";; let () = try Topdirs.dir_directory (Sys.getenv \"OCAML_TOPLEVEL_PATH\") with Not_found -> () From b34cfe6447e37619b69d9dfdb3d3fec644cb83f4 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Fri, 12 Dec 2025 22:00:04 -0600 Subject: [PATCH 03/15] fix hello-world to run in current ocamlscript --- examples/hello.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hello.ml b/examples/hello.ml index 0aa52fc..417ae33 100644 --- a/examples/hello.ml +++ b/examples/hello.ml @@ -1 +1 @@ -let hello () = print_endline "Hello from hello.ml" +let () = print_endline "Hello from hello.ml" From eee5cd6bf0c584c893a343c557dd455cc3847ffd Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:32:04 -0600 Subject: [PATCH 04/15] thin-ocamlscript rewrite --- .github/workflows/build.yml | 71 --- .ocamlformat | 15 + Changes | 95 --- INSTALL | 37 -- LICENSE | 26 - META.in | 6 - Makefile | 142 ----- OCamlMakefile | 1154 ---------------------------------- README.md | 104 ++- TODO | 3 - bin/dune | 4 + bin/main.ml | 25 + common.ml | 6 - common.mli | 33 - dune-project | 18 + examples/Makefile | 8 - examples/README | 57 -- examples/calc.ml | 14 - examples/factorial.ml | 31 +- examples/hello-web.ml | 19 - examples/hello.ml | 2 + examples/lexer.mll | 18 - examples/no-merlin-errors.ml | 15 - examples/parser.mly | 25 - examples/pp.mll | 12 - examples/revised.ml | 16 +- examples/with-includes.ml | 3 - lib/dune | 3 + lib/script.ml | 50 ++ main.ml | 557 ---------------- ocaml.ml | 165 ----- ocaml.mli | 70 --- ocamlscript-help.txt | 72 --- opam | 20 - pipeline.ml | 200 ------ pipeline.mli | 45 -- std.ml | 5 - tests/-full test | 41 -- tests/Makefile | 17 - tests/fulltest | 46 -- tests/unpack | 4 - thin-ocamlscript.opam | 33 + utils.ml | 4 - utils.mli | 8 - 44 files changed, 265 insertions(+), 3034 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .ocamlformat delete mode 100644 Changes delete mode 100644 INSTALL delete mode 100644 LICENSE delete mode 100644 META.in delete mode 100644 Makefile delete mode 100644 OCamlMakefile delete mode 100644 TODO create mode 100644 bin/dune create mode 100644 bin/main.ml delete mode 100644 common.ml delete mode 100644 common.mli create mode 100644 dune-project delete mode 100644 examples/Makefile delete mode 100644 examples/README delete mode 100644 examples/calc.ml mode change 100644 => 100755 examples/factorial.ml delete mode 100755 examples/hello-web.ml mode change 100644 => 100755 examples/hello.ml delete mode 100644 examples/lexer.mll delete mode 100644 examples/no-merlin-errors.ml delete mode 100644 examples/parser.mly delete mode 100644 examples/pp.mll mode change 100644 => 100755 examples/revised.ml delete mode 100644 examples/with-includes.ml create mode 100644 lib/dune create mode 100644 lib/script.ml delete mode 100644 main.ml delete mode 100644 ocaml.ml delete mode 100644 ocaml.mli delete mode 100644 ocamlscript-help.txt delete mode 100644 opam delete mode 100644 pipeline.ml delete mode 100644 pipeline.mli delete mode 100644 std.ml delete mode 100755 tests/-full test delete mode 100644 tests/Makefile delete mode 100755 tests/fulltest delete mode 100755 tests/unpack create mode 100644 thin-ocamlscript.opam delete mode 100644 utils.ml delete mode 100644 utils.mli diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 327515b..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,71 +0,0 @@ ---- -name: Build -on: - push: - branches: - - master # forall push/merge in master - pull_request: - branches: - - "**" # forall submitted Pull Requests - -jobs: - build: - strategy: - fail-fast: false - matrix: - os: - - macos-latest - - ubuntu-latest - - windows-latest - ocaml-version: - - 4.08.x - - 4.09.x - - 4.10.x - - 4.11.x - - 4.12.x - - 4.13.x - - runs-on: ${{ matrix.os }} - - steps: - - name: Prepare git - run: | - git config --global core.autocrlf false - git config --global init.defaultBranch master - - - name: Checkout code - uses: actions/checkout@v2 - - - name: Use OCaml ${{ matrix.ocaml-version }} - uses: ocaml/setup-ocaml@v2 - with: - ocaml-compiler: ${{ matrix.ocaml-version }} - - - name: Install dependencies - run: opam install --deps-only . - - - name: List installed packages - run: opam list - - - name: Build locally - run: opam exec -- make - - - name: Upload Linux/macOS artifact - if: runner.os != 'Windows' - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.os }}-${{ matrix.ocaml-version }}-ocamlscript - path: ocamlscript - - - name: Upload Windows artifact - if: runner.os == 'Windows' - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.os }}-${{ matrix.ocaml-version }}-ocamlscript.exe - path: ocamlscript.exe - - - name: Build, test, and install package - run: opam install -t . - - - name: Uninstall package - run: opam uninstall . diff --git a/.ocamlformat b/.ocamlformat new file mode 100644 index 0000000..8191b13 --- /dev/null +++ b/.ocamlformat @@ -0,0 +1,15 @@ +profile = conventional + +leading-nested-match-parens = false +space-around-variants = false +space-around-arrays = false +space-around-lists = false +space-around-records = false +break-infix = fit-or-vertical +break-separators = after +break-cases = fit-or-vertical +cases-exp-indent = 2 +exp-grouping = preserve +if-then-else = fit-or-vertical +let-and = sparse +type-decl = sparse diff --git a/Changes b/Changes deleted file mode 100644 index 884777c..0000000 --- a/Changes +++ /dev/null @@ -1,95 +0,0 @@ -VERSION 2 -========= - -2014-12-27: release 2.0.4 -- fixes for opam and recent installs of camlp4 - -2012-02-03: release 2.0.3 -- no user-visible behavior (new way of creating the source tar.gz) - -2008-02-05: release 2.0.2 -- supports OCaml/Camlp4 3.10.1 - -2007-04-07: release 2.0.1 -- added support for OCaml/Camlp4 3.10.0+beta - -2007-03-04: release 2.0.0 -- small change in the interface of the Pipeline module (new_cmd -> command) -- added OCaml.pp (any preprocessor) - and Ocaml.ppsrcloc (location formatter) variables - -2006-07-25: prerelease 1.99.5 -- added support for ocamlyacc - -2006-07-14: prerelease 1.99.4 -- added Ocaml.sources variable that can hold additional source files, e.g. - Ocaml.sources := ["../thing1.ml"] - -2006-06-24: pre-release 1.99.3 -- separation of examples and tests -- added some examples - -2006-06-07: pre-release 1.99.2 -- replaced Sys.command by create_process so that arguments - with special characters are not subject to shell interpretation - -2006-06-05: pre-release 1.99.1 -- important bugfix (scripts with shebang line and no "--" line) -- not dependent of micmatch_pcre not pcre anymore - -2006-06-03: pre-release 1.99.0, nothing really changed since last time - -2006-27-03 -This is an experimental extension of ocamlscript, which is -incompatible with ocamlscript 1.1. - -Current features: -- each script must have two parts, which are separated by a line starting - with "--" and followed by any number of blanks. -- the first part gives instructions for the compilation of the second part, - which is the program itself (see "fulltest" file for an example). -- the first part can be empty or can contain some OCaml code which - makes a "compile" function visible at the end of the module. -- by default, the Ocamlscript.Ocamlopt module is opened, which makes - a "compile" function available. -- the signature of this "compile" function is the following: - val compile : string -> string -> int - Its arguments are (1) the name of the source file to compile and (2) the - name of the executable which must be produced. These names are chosen by - ocamlscript, not by the script author. -- the Ocamlopt module provides several hooks which can be used to - specify which libraries to use and other compilation options. It - uses the ocamlfind (Findlib) frontend to ocamlopt. The parameters - are currently initialized as follows: - let packs = ref [] (* findlib packages *) - let use_camlp4 = ref true (* by default camlp4 is used *) - let revised = ref false (* use this to use the revised syntax *) - let ocamlflags = ref [] (* any options that you may want to pass - to ocamlopt *) - let ppopt = ref [] (* any options that you may want to pass - to camlp4o or camlp4r *) - let verbose = ref false (* that's for debugging *) - -- Any definition of a function named "compile" will override the - default one. Ocamlscript can be used to compile programs in any - language which accepts the same line directives as OCaml (it should - however be possible to make it more flexible in the future). - For now, it should be possible to define "compile" functions - for ocamllex or ocaml+twt (and include them in the Ocamlscript library). - - - -VERSION 1 -========= - -* version 1.1 (2005-10-03): - - - the temporary copy of the original script contains proper reference - to original source so line number and script name is correct in case - of error (suggestion of Mike Lin); - - - change license to a BSD-like one. Apparently Public Domain imposes - restriction of Software redistribution on a different medium. The new - license should allow to use the software for about any use. - -* version 1.0 (2005-08-23): original release. diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 91b9239..0000000 --- a/INSTALL +++ /dev/null @@ -1,37 +0,0 @@ - Ocamlscript version 1.99 - ======================== - -Disclaimer: ----------- -This is a preview release of ocamlscript 2. It is provided for curious -programmers who are interested in testing the new system. Several -incompatible changes might take place between this pre-release and -the 2.0 release line. Be ready to adapt the header of your scripts -when you upgrade! - - -Installation: ------------- - -Requirements: - * OCaml version 3.10 or higher, - * Camlp4 (including command camlp4orf), - * GNU make, - * Findlib (ocamlfind) - -This version has been developed on Linux and will probably not work on the -Win32 port of OCaml, and maybe others. Any help to make that work is -appreciated. - -Compilation: - make - -Installation: - make install - -Uninstallation: - make uninstall - -Examples are available in the "examples" subdirectory. - -For more information on ocamlscript, see the README file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index c276064..0000000 --- a/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright 2005 David MENTRE -Copyright 2006 Martin Jambon - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/META.in b/META.in deleted file mode 100644 index f9bd4f5..0000000 --- a/META.in +++ /dev/null @@ -1,6 +0,0 @@ -name = "ocamlscript" -description = "Runtime library for ocamlscript" - -requires = "unix" -archive(byte) = "ocamlscript.cmo" -archive(native) = "ocamlscript.cmx" diff --git a/Makefile b/Makefile deleted file mode 100644 index 894e94d..0000000 --- a/Makefile +++ /dev/null @@ -1,142 +0,0 @@ -VERSION = 3.0.0 -RESULT = ocamlscript -SOURCES = \ - version.ml pipeline.mli pipeline.ml common.mli common.ml \ - utils.mli utils.ml ocaml.mli ocaml.ml std.ml - -CAMLP4 := $(shell ocamlfind query camlp4) - -STDBIN = $(shell dirname `which ocamlfind`) -ifndef PREFIX - PREFIX = $(shell dirname $(STDBIN)) -endif -export PREFIX - -ifndef BINDIR - BINDIR = $(PREFIX)/bin -endif -export BINDIR - -PACKS = unix str -# PP = camlp4o -I . -parser pa_opt.cmo -# export PP - -# CAMLP4_VARIANTS = pa_opt.ml -OCAMLFLAGS = -dtypes - - -.PHONY: init default common bytelib optlib optexe bytelib optlib \ - install uninstall test tests examples ocamldoc version meta archive - -default: common bytelib optlib optexe - -# GODI targets -.PHONY: all opt -all: common bytelib byteexe -opt: common optlib optexe -### - -common: version.ml - # ocamlc -pp 'camlp4orf -loc _loc' -c \ - # -I $(CAMLP4) pa_opt310.ml && \ - # cp pa_opt310.cmo pa_opt.cmo && \ - # cp pa_opt310.cmi pa_opt.cmi - # ocamlc -pp 'camlp4orf -loc _loc' -c \ - # -I $(CAMLP4) pa_tryfinally310.ml && \ - # cp pa_tryfinally310.cmo pa_tryfinally.cmo && \ - # cp pa_tryfinally310.cmi pa_tryfinally.cmi - -byteexe: bytelib - ocamlfind ocamlc -o ocamlscript.byte \ - -package '$(PACKS)' -linkpkg $(OCAMLFLAGS) \ - ocamlscript.cmo main.ml -optexe: optlib - ocamlfind ocamlopt -o ocamlscript \ - -package '$(PACKS)' -linkpkg $(OCAMLFLAGS) \ - ocamlscript.cmx main.ml -bytelib: pabc - touch bc.done -optlib: panc - touch nc.done -install: - test -f ocamlscript$(EXE) && \ - install -m 0755 ocamlscript$(EXE) $(BINDIR)/ocamlscript$(EXE) || : - test -f ocamlscript.byte$(EXE) && \ - install -m 0755 ocamlscript.byte$(EXE) \ - $(BINDIR)/ocamlscript.byte$(EXE) || : - ocamlfind install ocamlscript META \ - ocamlscript.cmi \ - `test -f bc.done && echo ocamlscript.cmo` \ - `test -f nc.done && echo ocamlscript.cmx ocamlscript.o` -uninstall: - rm -f $(BINDIR)/ocamlscript$(EXE) $(BINDIR)/ocamlscript.byte$(EXE) - ocamlfind remove ocamlscript - -test: tests -tests: - cd tests && $(MAKE) -examples: - cd examples && $(MAKE) - -ocamldoc: - ocamldoc -d ocamldoc -html pipeline.mli common.mli utils.mli ocaml.mli - -.PHONY: version -version: version.ml -version.ml: Makefile - echo 'let version = "$(VERSION)"' > version.ml - echo 'version = "$(VERSION)"' > META - cat META.in >> META - -.PHONY: help -help: - ./ocamlscript --help > ocamlscript-help.txt - - -archive: all version help - rm -rf /tmp/ocamlscript /tmp/ocamlscript-$(VERSION) && \ - cp -r . /tmp/ocamlscript && \ - cd /tmp/ocamlscript && \ - $(MAKE) clean && \ - rm -f *~ ocamlscript*.tar* && \ - cp ocamldoc/* $$WWW/ocamlscript-doc && \ - cd /tmp && cp -r ocamlscript ocamlscript-$(VERSION) && \ - tar czf ocamlscript.tar.gz ocamlscript && \ - tar cjf ocamlscript.tar.bz2 ocamlscript && \ - tar czf ocamlscript-$(VERSION).tar.gz \ - ocamlscript-$(VERSION) && \ - tar cjf ocamlscript-$(VERSION).tar.bz2 ocamlscript-$(VERSION) - mv /tmp/ocamlscript.tar.gz /tmp/ocamlscript.tar.bz2 . - mv /tmp/ocamlscript-$(VERSION).tar.gz \ - /tmp/ocamlscript-$(VERSION).tar.bz2 . - cp ocamlscript.tar.gz ocamlscript.tar.bz2 $$WWW/ - cp ocamlscript-$(VERSION).tar.gz ocamlscript-$(VERSION).tar.bz2 $$WWW/ - cp LICENSE $$WWW/ocamlscript-license.txt - echo 'let ocamlscript_version = "$(VERSION)"' \ - > $$WWW/ocamlscript-version.ml - cp Changes $$WWW/ocamlscript-changes.txt - cp ocamlscript-help.txt $$WWW/ - rm -rf $$WWW/ocamlscript-examples - cd examples && $(MAKE) clean - cp -r examples $$WWW/ocamlscript-examples - -TRASH = \ - bc.done nc.done main.cm* main.o main.annot \ - ocamlscript$(EXE) ocamlscript.byte$(EXE) \ - *~ \ - version.ml META - - -clean:: - cd tests && $(MAKE) clean - cd examples && $(MAKE) clean - rm -rf ocamldoc *.cmi *.cmo - -OCAML_VERSION = $(shell ocamlc -v | head -1 | \ - sed -e 's/.*version \([0-9]\.[0-9][0-9]\).*/\1/') - -OCAMLBCFLAGS = -for-pack Ocamlscript -OCAMLNCFLAGS = -for-pack Ocamlscript - -OCAMLMAKEFILE = OCamlMakefile -include $(OCAMLMAKEFILE) diff --git a/OCamlMakefile b/OCamlMakefile deleted file mode 100644 index 27875aa..0000000 --- a/OCamlMakefile +++ /dev/null @@ -1,1154 +0,0 @@ -########################################################################### -# OCamlMakefile -# Copyright (C) 1999-2004 Markus Mottl -# -# For updates see: -# http://www.ocaml.info/home/ocaml_sources.html -# -# $Id: OCamlMakefile,v 1.72 2005/12/09 15:30:50 mottl Exp $ -# -########################################################################### - -# Modified by damien for .glade.ml compilation - -# Set these variables to the names of the sources to be processed and -# the result variable. Order matters during linkage! - -ifndef SOURCES - SOURCES := foo.ml -endif -export SOURCES - -ifndef RES_CLIB_SUF - RES_CLIB_SUF := _stubs -endif -export RES_CLIB_SUF - -ifndef RESULT - RESULT := foo -endif -export RESULT - -export LIB_PACK_NAME - -ifndef DOC_FILES - DOC_FILES := $(filter %.mli, $(SOURCES)) -endif -export DOC_FILES - -export BCSUFFIX -export NCSUFFIX - -ifndef TOPSUFFIX - TOPSUFFIX := .top -endif -export TOPSUFFIX - -# Eventually set include- and library-paths, libraries to link, -# additional compilation-, link- and ocamlyacc-flags -# Path- and library information needs not be written with "-I" and such... -# Define THREADS if you need it, otherwise leave it unset (same for -# USE_CAMLP4)! - -export THREADS -export VMTHREADS -export ANNOTATE -export USE_CAMLP4 - -export INCDIRS -export LIBDIRS -export EXTLIBDIRS -export RESULTDEPS -export OCAML_DEFAULT_DIRS - -export LIBS -export CLIBS - -export OCAMLFLAGS -export OCAMLNCFLAGS -export OCAMLBCFLAGS - -export OCAMLLDFLAGS -export OCAMLNLDFLAGS -export OCAMLBLDFLAGS - -ifndef OCAMLCPFLAGS - OCAMLCPFLAGS := a -endif - -export OCAMLCPFLAGS - -export PPFLAGS - -export YFLAGS -export IDLFLAGS - -export OCAMLDOCFLAGS - -export OCAMLFIND_INSTFLAGS - -export DVIPSFLAGS - -export STATIC - -# Add a list of optional trash files that should be deleted by "make clean" -export TRASH - -#################### variables depending on your OCaml-installation - -ifdef MINGW - export MINGW - WIN32 := 1 - CFLAGS_WIN32 := -mno-cygwin -endif -ifdef MSVC - export MSVC - WIN32 := 1 - ifndef STATIC - CPPFLAGS_WIN32 := -DCAML_DLL - endif - CFLAGS_WIN32 += -nologo - EXT_OBJ := obj - EXT_LIB := lib - ifeq ($(CC),gcc) - # work around GNU Make default value - ifdef THREADS - CC := cl -MT - else - CC := cl - endif - endif - ifeq ($(CXX),g++) - # work around GNU Make default value - CXX := $(CC) - endif - CFLAG_O := -Fo -endif -ifdef WIN32 - EXT_CXX := cpp - EXE := .exe -endif - -ifndef EXT_OBJ - EXT_OBJ := o -endif -ifndef EXT_LIB - EXT_LIB := a -endif -ifndef EXT_CXX - EXT_CXX := cc -endif -ifndef EXE - EXE := # empty -endif -ifndef CFLAG_O - CFLAG_O := -o # do not delete this comment (preserves trailing whitespace)! -endif - -export CC -export CXX -export CFLAGS -export CXXFLAGS -export LDFLAGS -export CPPFLAGS - -ifndef RPATH_FLAG - RPATH_FLAG := -R -endif -export RPATH_FLAG - -ifndef MSVC -ifndef PIC_CFLAGS - PIC_CFLAGS := -fPIC -endif -ifndef PIC_CPPFLAGS - PIC_CPPFLAGS := -DPIC -endif -endif - -export PIC_CFLAGS -export PIC_CPPFLAGS - -BCRESULT := $(addsuffix $(BCSUFFIX), $(RESULT)) -NCRESULT := $(addsuffix $(NCSUFFIX), $(RESULT)) -TOPRESULT := $(addsuffix $(TOPSUFFIX), $(RESULT)) - -ifndef OCAMLFIND - OCAMLFIND := ocamlfind -endif -export OCAMLFIND - -ifndef OCAMLC - OCAMLC := ocamlc -endif -export OCAMLC - -ifndef OCAMLOPT - OCAMLOPT := ocamlopt -endif -export OCAMLOPT - -ifndef OCAMLMKTOP - OCAMLMKTOP := ocamlmktop -endif -export OCAMLMKTOP - -ifndef OCAMLCP - OCAMLCP := ocamlcp -endif -export OCAMLCP - -ifndef OCAMLDEP - OCAMLDEP := ocamldep -endif -export OCAMLDEP - -ifndef OCAMLLEX - OCAMLLEX := ocamllex -endif -export OCAMLLEX - -ifndef OCAMLYACC - OCAMLYACC := ocamlyacc -endif -export OCAMLYACC - -ifndef OCAMLMKLIB - OCAMLMKLIB := ocamlmklib -endif -export OCAMLMKLIB - -ifndef OCAML_GLADECC - OCAML_GLADECC := lablgladecc2 -endif -export OCAML_GLADECC - -ifndef OCAML_GLADECC_FLAGS - OCAML_GLADECC_FLAGS := -endif -export OCAML_GLADECC_FLAGS - -ifndef CAMELEON_REPORT - CAMELEON_REPORT := report -endif -export CAMELEON_REPORT - -ifndef CAMELEON_REPORT_FLAGS - CAMELEON_REPORT_FLAGS := -endif -export CAMELEON_REPORT_FLAGS - -ifndef CAMELEON_ZOGGY - CAMELEON_ZOGGY := camlp4o pa_zog.cma pr_o.cmo -endif -export CAMELEON_ZOGGY - -ifndef CAMELEON_ZOGGY_FLAGS - CAMELEON_ZOGGY_FLAGS := -endif -export CAMELEON_ZOGGY_FLAGS - -ifndef OXRIDL - OXRIDL := oxridl -endif -export OXRIDL - -ifndef CAMLIDL - CAMLIDL := camlidl -endif -export CAMLIDL - -ifndef CAMLIDLDLL - CAMLIDLDLL := camlidldll -endif -export CAMLIDLDLL - -ifndef NOIDLHEADER - MAYBE_IDL_HEADER := -header -endif -export NOIDLHEADER - -export NO_CUSTOM - -ifndef CAMLP4 - CAMLP4 := camlp4 -endif -export CAMLP4 - -ifndef REAL_OCAMLFIND - ifdef PACKS - ifndef CREATE_LIB - ifdef THREADS - PACKS += threads - endif - endif - empty := - space := $(empty) $(empty) - comma := , - ifdef PREDS - PRE_OCAML_FIND_PREDICATES := $(subst $(space),$(comma),$(PREDS)) - PRE_OCAML_FIND_PACKAGES := $(subst $(space),$(comma),$(PACKS)) - OCAML_FIND_PREDICATES := -predicates $(PRE_OCAML_FIND_PREDICATES) - # OCAML_DEP_PREDICATES := -syntax $(PRE_OCAML_FIND_PREDICATES) - OCAML_FIND_PACKAGES := $(OCAML_FIND_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) - OCAML_DEP_PACKAGES := $(OCAML_DEP_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) - else - OCAML_FIND_PACKAGES := -package $(subst $(space),$(comma),$(PACKS)) - OCAML_DEP_PACKAGES := - endif - OCAML_FIND_LINKPKG := -linkpkg - REAL_OCAMLFIND := $(OCAMLFIND) - endif -endif - -export OCAML_FIND_PACKAGES -export OCAML_DEP_PACKAGES -export OCAML_FIND_LINKPKG -export REAL_OCAMLFIND - -ifndef OCAMLDOC - OCAMLDOC := ocamldoc -endif -export OCAMLDOC - -ifndef LATEX - LATEX := latex -endif -export LATEX - -ifndef DVIPS - DVIPS := dvips -endif -export DVIPS - -ifndef PS2PDF - PS2PDF := ps2pdf -endif -export PS2PDF - -ifndef OCAMLMAKEFILE - OCAMLMAKEFILE := OCamlMakefile -endif -export OCAMLMAKEFILE - -ifndef OCAMLLIBPATH - OCAMLLIBPATH := \ - $(shell $(OCAMLC) 2>/dev/null -where || echo /usr/local/lib/ocaml) -endif -export OCAMLLIBPATH - -ifndef OCAML_LIB_INSTALL - OCAML_LIB_INSTALL := $(OCAMLLIBPATH)/contrib -endif -export OCAML_LIB_INSTALL - -########################################################################### - -#################### change following sections only if -#################### you know what you are doing! - -# delete target files when a build command fails -.PHONY: .DELETE_ON_ERROR -.DELETE_ON_ERROR: - -# for pedants using "--warn-undefined-variables" -export MAYBE_IDL -export REAL_RESULT -export CAMLIDLFLAGS -export THREAD_FLAG -export RES_CLIB -export MAKEDLL -export ANNOT_FLAG -export C_OXRIDL -export SUBPROJS -export CFLAGS_WIN32 -export CPPFLAGS_WIN32 - -INCFLAGS := - -SHELL := /bin/sh - -MLDEPDIR := ._d -BCDIDIR := ._bcdi -NCDIDIR := ._ncdi - -FILTER_EXTNS := %.mli %.ml %.mll %.mly %.idl %.oxridl %.c %.$(EXT_CXX) %.rep %.zog %.glade - -FILTERED := $(filter $(FILTER_EXTNS), $(SOURCES)) -SOURCE_DIRS := $(filter-out ./, $(sort $(dir $(FILTERED)))) - -FILTERED_REP := $(filter %.rep, $(FILTERED)) -DEP_REP := $(FILTERED_REP:%.rep=$(MLDEPDIR)/%.d) -AUTO_REP := $(FILTERED_REP:.rep=.ml) - -FILTERED_ZOG := $(filter %.zog, $(FILTERED)) -DEP_ZOG := $(FILTERED_ZOG:%.zog=$(MLDEPDIR)/%.d) -AUTO_ZOG := $(FILTERED_ZOG:.zog=.ml) - -FILTERED_GLADE := $(filter %.glade, $(FILTERED)) -DEP_GLADE := $(FILTERED_GLADE:%.glade=$(MLDEPDIR)/%.d) -AUTO_GLADE := $(FILTERED_GLADE:.glade=.ml) - -FILTERED_ML := $(filter %.ml, $(FILTERED)) -DEP_ML := $(FILTERED_ML:%.ml=$(MLDEPDIR)/%.d) - -FILTERED_MLI := $(filter %.mli, $(FILTERED)) -DEP_MLI := $(FILTERED_MLI:.mli=.di) - -FILTERED_MLL := $(filter %.mll, $(FILTERED)) -DEP_MLL := $(FILTERED_MLL:%.mll=$(MLDEPDIR)/%.d) -AUTO_MLL := $(FILTERED_MLL:.mll=.ml) - -FILTERED_MLY := $(filter %.mly, $(FILTERED)) -DEP_MLY := $(FILTERED_MLY:%.mly=$(MLDEPDIR)/%.d) $(FILTERED_MLY:.mly=.di) -AUTO_MLY := $(FILTERED_MLY:.mly=.mli) $(FILTERED_MLY:.mly=.ml) - -FILTERED_IDL := $(filter %.idl, $(FILTERED)) -DEP_IDL := $(FILTERED_IDL:%.idl=$(MLDEPDIR)/%.d) $(FILTERED_IDL:.idl=.di) -C_IDL := $(FILTERED_IDL:%.idl=%_stubs.c) -ifndef NOIDLHEADER - C_IDL += $(FILTERED_IDL:.idl=.h) -endif -OBJ_C_IDL := $(FILTERED_IDL:%.idl=%_stubs.$(EXT_OBJ)) -AUTO_IDL := $(FILTERED_IDL:.idl=.mli) $(FILTERED_IDL:.idl=.ml) $(C_IDL) - -FILTERED_OXRIDL := $(filter %.oxridl, $(FILTERED)) -DEP_OXRIDL := $(FILTERED_OXRIDL:%.oxridl=$(MLDEPDIR)/%.d) $(FILTERED_OXRIDL:.oxridl=.di) -AUTO_OXRIDL := $(FILTERED_OXRIDL:.oxridl=.mli) $(FILTERED_OXRIDL:.oxridl=.ml) $(C_OXRIDL) - -FILTERED_C_CXX := $(filter %.c %.$(EXT_CXX), $(FILTERED)) -OBJ_C_CXX := $(FILTERED_C_CXX:.c=.$(EXT_OBJ)) -OBJ_C_CXX := $(OBJ_C_CXX:.$(EXT_CXX)=.$(EXT_OBJ)) - -PRE_TARGETS += $(AUTO_MLL) $(AUTO_MLY) $(AUTO_IDL) $(AUTO_OXRIDL) $(AUTO_ZOG) $(AUTO_REP) $(AUTO_GLADE) - -ALL_DEPS := $(DEP_ML) $(DEP_MLI) $(DEP_MLL) $(DEP_MLY) $(DEP_IDL) $(DEP_OXRIDL) $(DEP_ZOG) $(DEP_REP) $(DEP_GLADE) - -MLDEPS := $(filter %.d, $(ALL_DEPS)) -MLIDEPS := $(filter %.di, $(ALL_DEPS)) -BCDEPIS := $(MLIDEPS:%.di=$(BCDIDIR)/%.di) -NCDEPIS := $(MLIDEPS:%.di=$(NCDIDIR)/%.di) - -ALLML := $(filter %.mli %.ml %.mll %.mly %.idl %.oxridl %.rep %.zog %.glade, $(FILTERED)) - -IMPLO_INTF := $(ALLML:%.mli=%.mli.__) -IMPLO_INTF := $(foreach file, $(IMPLO_INTF), \ - $(basename $(file)).cmi $(basename $(file)).cmo) -IMPLO_INTF := $(filter-out %.mli.cmo, $(IMPLO_INTF)) -IMPLO_INTF := $(IMPLO_INTF:%.mli.cmi=%.cmi) - -IMPLX_INTF := $(IMPLO_INTF:.cmo=.cmx) - -INTF := $(filter %.cmi, $(IMPLO_INTF)) -IMPL_CMO := $(filter %.cmo, $(IMPLO_INTF)) -IMPL_CMX := $(IMPL_CMO:.cmo=.cmx) -IMPL_ASM := $(IMPL_CMO:.cmo=.asm) -IMPL_S := $(IMPL_CMO:.cmo=.s) - -OBJ_LINK := $(OBJ_C_IDL) $(OBJ_C_CXX) -OBJ_FILES := $(IMPL_CMO:.cmo=.$(EXT_OBJ)) $(OBJ_LINK) - -EXECS := $(addsuffix $(EXE), \ - $(sort $(TOPRESULT) $(BCRESULT) $(NCRESULT))) -ifdef WIN32 - EXECS += $(BCRESULT).dll $(NCRESULT).dll -endif - -CLIB_BASE := $(RESULT)$(RES_CLIB_SUF) -ifneq ($(strip $(OBJ_LINK)),) - RES_CLIB := lib$(CLIB_BASE).$(EXT_LIB) -endif - -ifdef WIN32 -DLLSONAME := $(CLIB_BASE).dll -else -DLLSONAME := dll$(CLIB_BASE).so -endif - -NONEXECS := $(INTF) $(IMPL_CMO) $(IMPL_CMX) $(IMPL_ASM) $(IMPL_S) \ - $(OBJ_FILES) $(PRE_TARGETS) $(BCRESULT).cma $(NCRESULT).cmxa \ - $(NCRESULT).$(EXT_LIB) $(BCRESULT).cmi $(BCRESULT).cmo \ - $(NCRESULT).cmi $(NCRESULT).cmx $(NCRESULT).o \ - $(RES_CLIB) $(IMPL_CMO:.cmo=.annot) \ - $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(LIB_PACK_NAME).cmx $(LIB_PACK_NAME).o - -ifndef STATIC - NONEXECS += $(DLLSONAME) -endif - -ifndef LIBINSTALL_FILES - LIBINSTALL_FILES := $(RESULT).mli $(RESULT).cmi $(RESULT).cma \ - $(RESULT).cmxa $(RESULT).$(EXT_LIB) $(RES_CLIB) - ifndef STATIC - ifneq ($(strip $(OBJ_LINK)),) - LIBINSTALL_FILES += $(DLLSONAME) - endif - endif -endif - -export LIBINSTALL_FILES - -ifdef WIN32 - # some extra stuff is created while linking DLLs - NONEXECS += $(BCRESULT).$(EXT_LIB) $(BCRESULT).exp $(NCRESULT).exp $(CLIB_BASE).exp $(CLIB_BASE).lib -endif - -TARGETS := $(EXECS) $(NONEXECS) - -# If there are IDL-files -ifneq ($(strip $(FILTERED_IDL)),) - MAYBE_IDL := -cclib -lcamlidl -endif - -ifdef USE_CAMLP4 - CAMLP4PATH := \ - $(shell $(CAMLP4) -where 2>/dev/null || echo /usr/local/lib/camlp4) - INCFLAGS := -I $(CAMLP4PATH) - CINCFLAGS := -I$(CAMLP4PATH) -endif - -DINCFLAGS := $(INCFLAGS) $(SOURCE_DIRS:%=-I %) $(OCAML_DEFAULT_DIRS:%=-I %) -INCFLAGS := $(DINCFLAGS) $(INCDIRS:%=-I %) -CINCFLAGS += $(SOURCE_DIRS:%=-I%) $(INCDIRS:%=-I%) $(OCAML_DEFAULT_DIRS:%=-I%) - -ifndef MSVC -CLIBFLAGS += $(SOURCE_DIRS:%=-L%) $(LIBDIRS:%=-L%) \ - $(EXTLIBDIRS:%=-L%) $(EXTLIBDIRS:%=-Wl,$(RPATH_FLAG)%) \ - $(OCAML_DEFAULT_DIRS:%=-L%) -endif - -ifndef PROFILING - INTF_OCAMLC := $(OCAMLC) -else - ifndef THREADS - INTF_OCAMLC := $(OCAMLCP) -p $(OCAMLCPFLAGS) - else - # OCaml does not support profiling byte code - # with threads (yet), therefore we force an error. - ifndef REAL_OCAMLC - $(error Profiling of multithreaded byte code not yet supported by OCaml) - endif - INTF_OCAMLC := $(OCAMLC) - endif -endif - -ifndef MSVC -COMMON_LDFLAGS := $(LDFLAGS:%=-ccopt %) $(SOURCE_DIRS:%=-ccopt -L%) \ - $(LIBDIRS:%=-ccopt -L%) $(EXTLIBDIRS:%=-ccopt -L%) \ - $(EXTLIBDIRS:%=-ccopt -Wl,$(RPATH_FLAG)%) \ - $(OCAML_DEFAULT_DIRS:%=-ccopt -L%) -else -COMMON_LDFLAGS := -ccopt "/link -NODEFAULTLIB:LIBC $(LDFLAGS:%=%) $(SOURCE_DIRS:%=-LIBPATH:%) \ - $(LIBDIRS:%=-LIBPATH:%) $(EXTLIBDIRS:%=-LIBPATH:%) \ - $(OCAML_DEFAULT_DIRS:%=-LIBPATH:%) " -endif - -CLIBS_OPTS := $(CLIBS:%=-cclib -l%) -ifdef MSVC - ifndef STATIC - # MSVC libraries do not have 'lib' prefix - CLIBS_OPTS := $(CLIBS:%=-cclib %.lib) - endif -endif - -ifneq ($(strip $(OBJ_LINK)),) - ifdef CREATE_LIB - OBJS_LIBS := -cclib -l$(CLIB_BASE) $(CLIBS_OPTS) $(MAYBE_IDL) - else - OBJS_LIBS := $(OBJ_LINK) $(CLIBS_OPTS) $(MAYBE_IDL) - endif -else - OBJS_LIBS := $(CLIBS_OPTS) $(MAYBE_IDL) -endif - -# If we have to make byte-code -ifndef REAL_OCAMLC - BYTE_OCAML := y - - # EXTRADEPS is added dependencies we have to insert for all - # executable files we generate. Ideally it should be all of the - # libraries we use, but it's hard to find the ones that get searched on - # the path since I don't know the paths built into the compiler, so - # just include the ones with slashes in their names. - EXTRADEPS := $(addsuffix .cma,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) - SPECIAL_OCAMLFLAGS := $(OCAMLBCFLAGS) - - REAL_OCAMLC := $(INTF_OCAMLC) - - REAL_IMPL := $(IMPL_CMO) - REAL_IMPL_INTF := $(IMPLO_INTF) - IMPL_SUF := .cmo - - DEPFLAGS := - MAKE_DEPS := $(MLDEPS) $(BCDEPIS) - - ifdef CREATE_LIB - override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) - override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) - ifndef STATIC - ifneq ($(strip $(OBJ_LINK)),) - MAKEDLL := $(DLLSONAME) - ALL_LDFLAGS := -dllib $(DLLSONAME) - endif - endif - endif - - ifndef NO_CUSTOM - ifneq "$(strip $(OBJ_LINK) $(THREADS) $(MAYBE_IDL) $(CLIBS))" "" - ALL_LDFLAGS += -custom - endif - endif - - ALL_LDFLAGS += $(INCFLAGS) $(OCAMLLDFLAGS) $(OCAMLBLDFLAGS) \ - $(COMMON_LDFLAGS) $(LIBS:%=%.cma) - CAMLIDLDLLFLAGS := - - ifdef THREADS - ifdef VMTHREADS - THREAD_FLAG := -vmthread - else - THREAD_FLAG := -thread - endif - ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) - ifndef CREATE_LIB - ifndef REAL_OCAMLFIND - ALL_LDFLAGS := unix.cma threads.cma $(ALL_LDFLAGS) - endif - endif - endif - -# we have to make native-code -else - EXTRADEPS := $(addsuffix .cmxa,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) - ifndef PROFILING - SPECIAL_OCAMLFLAGS := $(OCAMLNCFLAGS) - PLDFLAGS := - else - SPECIAL_OCAMLFLAGS := -p $(OCAMLNCFLAGS) - PLDFLAGS := -p - endif - - REAL_IMPL := $(IMPL_CMX) - REAL_IMPL_INTF := $(IMPLX_INTF) - IMPL_SUF := .cmx - - override CPPFLAGS := -DNATIVE_CODE $(CPPFLAGS) - - DEPFLAGS := -native - MAKE_DEPS := $(MLDEPS) $(NCDEPIS) - - ALL_LDFLAGS := $(PLDFLAGS) $(INCFLAGS) $(OCAMLLDFLAGS) \ - $(OCAMLNLDFLAGS) $(COMMON_LDFLAGS) - CAMLIDLDLLFLAGS := -opt - - ifndef CREATE_LIB - ALL_LDFLAGS += $(LIBS:%=%.cmxa) - else - override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) - override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) - endif - - ifdef THREADS - THREAD_FLAG := -thread - ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) - ifndef CREATE_LIB - ifndef REAL_OCAMLFIND - ALL_LDFLAGS := unix.cmxa threads.cmxa $(ALL_LDFLAGS) - endif - endif - endif -endif - -export MAKE_DEPS - -ifdef ANNOTATE - ANNOT_FLAG := -dtypes -else -endif - -ALL_OCAMLCFLAGS := $(THREAD_FLAG) $(ANNOT_FLAG) $(OCAMLFLAGS) \ - $(INCFLAGS) $(SPECIAL_OCAMLFLAGS) - -ifdef make_deps - -include $(MAKE_DEPS) - PRE_TARGETS := -endif - -########################################################################### -# USER RULES - -# Call "OCamlMakefile QUIET=" to get rid of all of the @'s. -QUIET=@ - -# generates byte-code (default) -byte-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ - REAL_RESULT="$(BCRESULT)" make_deps=yes -bc: byte-code - -byte-code-nolink: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ - REAL_RESULT="$(BCRESULT)" make_deps=yes -bcnl: byte-code-nolink - -top: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(TOPRESULT) \ - REAL_RESULT="$(BCRESULT)" make_deps=yes - -# generates native-code - -native-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ - REAL_RESULT="$(NCRESULT)" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - make_deps=yes -nc: native-code - -native-code-nolink: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ - REAL_RESULT="$(NCRESULT)" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - make_deps=yes -ncnl: native-code-nolink - -# generates byte-code libraries -byte-code-library: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(BCRESULT).cma \ - REAL_RESULT="$(BCRESULT)" \ - CREATE_LIB=yes \ - make_deps=yes -bcl: byte-code-library - -# generates native-code libraries -native-code-library: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(NCRESULT).cmxa \ - REAL_RESULT="$(NCRESULT)" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - CREATE_LIB=yes \ - make_deps=yes -ncl: native-code-library - -ifdef WIN32 -# generates byte-code dll -byte-code-dll: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(BCRESULT).dll \ - REAL_RESULT="$(BCRESULT)" \ - make_deps=yes -bcd: byte-code-dll - -# generates native-code dll -native-code-dll: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(NCRESULT).dll \ - REAL_RESULT="$(NCRESULT)" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - make_deps=yes -ncd: native-code-dll -endif - -# generates byte-code with debugging information -debug-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ - REAL_RESULT="$(BCRESULT)" make_deps=yes \ - OCAMLFLAGS="-g $(OCAMLFLAGS)" \ - OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" -dc: debug-code - -debug-code-nolink: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ - REAL_RESULT="$(BCRESULT)" make_deps=yes \ - OCAMLFLAGS="-g $(OCAMLFLAGS)" \ - OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" -dcnl: debug-code-nolink - -# generates byte-code libraries with debugging information -debug-code-library: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(BCRESULT).cma \ - REAL_RESULT="$(BCRESULT)" make_deps=yes \ - CREATE_LIB=yes \ - OCAMLFLAGS="-g $(OCAMLFLAGS)" \ - OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" -dcl: debug-code-library - -# generates byte-code for profiling -profiling-byte-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ - REAL_RESULT="$(BCRESULT)" PROFILING="y" \ - make_deps=yes -pbc: profiling-byte-code - -# generates native-code - -profiling-native-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ - REAL_RESULT="$(NCRESULT)" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - PROFILING="y" \ - make_deps=yes -pnc: profiling-native-code - -# generates byte-code libraries -profiling-byte-code-library: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(BCRESULT).cma \ - REAL_RESULT="$(BCRESULT)" PROFILING="y" \ - CREATE_LIB=yes \ - make_deps=yes -pbcl: profiling-byte-code-library - -# generates native-code libraries -profiling-native-code-library: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(RES_CLIB) $(NCRESULT).cmxa \ - REAL_RESULT="$(NCRESULT)" PROFILING="y" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - CREATE_LIB=yes \ - make_deps=yes -pncl: profiling-native-code-library - -# packs byte-code objects -pack-byte-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT).cmo \ - REAL_RESULT="$(BCRESULT)" \ - PACK_LIB=yes make_deps=yes -pabc: pack-byte-code - -# packs native-code objects -pack-native-code: $(PRE_TARGETS) - $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ - $(NCRESULT).cmx $(NCRESULT).o \ - REAL_RESULT="$(NCRESULT)" \ - REAL_OCAMLC="$(OCAMLOPT)" \ - PACK_LIB=yes make_deps=yes -panc: pack-native-code - -# generates HTML-documentation -htdoc: doc/$(RESULT)/html - -# generates Latex-documentation -ladoc: doc/$(RESULT)/latex - -# generates PostScript-documentation -psdoc: doc/$(RESULT)/latex/doc.ps - -# generates PDF-documentation -pdfdoc: doc/$(RESULT)/latex/doc.pdf - -# generates all supported forms of documentation -doc: htdoc ladoc psdoc pdfdoc - -########################################################################### -# LOW LEVEL RULES - -$(REAL_RESULT): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) $(RESULTDEPS) - $(REAL_OCAMLFIND) $(REAL_OCAMLC) \ - $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ - $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ - $(REAL_IMPL) - -nolink: $(REAL_IMPL_INTF) $(OBJ_LINK) - -ifdef WIN32 -$(REAL_RESULT).dll: $(REAL_IMPL_INTF) $(OBJ_LINK) - $(CAMLIDLDLL) $(CAMLIDLDLLFLAGS) $(OBJ_LINK) $(CLIBS) \ - -o $@ $(REAL_IMPL) -endif - -%$(TOPSUFFIX): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) - $(REAL_OCAMLFIND) $(OCAMLMKTOP) \ - $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ - $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ - $(REAL_IMPL) - -.SUFFIXES: .mli .ml .cmi .cmo .cmx .cma .cmxa .$(EXT_OBJ) \ - .mly .di .d .$(EXT_LIB) .idl %.oxridl .c .$(EXT_CXX) .h .so \ - .rep .zog .glade - -ifndef STATIC -ifdef MINGW -$(DLLSONAME): $(OBJ_LINK) - $(CC) $(CFLAGS) $(CFLAGS_WIN32) $(OBJ_LINK) -shared -o $@ \ - -Wl,--whole-archive $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/lib%.a))) \ - $(OCAMLLIBPATH)/ocamlrun.a \ - -Wl,--export-all-symbols \ - -Wl,--no-whole-archive -else -ifdef MSVC -$(DLLSONAME): $(OBJ_LINK) - link /NOLOGO /DLL /OUT:$@ $(OBJ_LINK) \ - $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/%.lib))) \ - $(OCAMLLIBPATH)/ocamlrun.lib - -else -$(DLLSONAME): $(OBJ_LINK) - $(OCAMLMKLIB) $(INCFLAGS) $(CLIBFLAGS) \ - -o $(CLIB_BASE) $(OBJ_LINK) $(CLIBS:%=-l%) \ - $(OCAMLMKLIB_FLAGS) -endif -endif -endif - -ifndef LIB_PACK_NAME -$(RESULT).cma: $(REAL_IMPL_INTF) $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) - $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) \ - $(OBJS_LIBS) -o $@ $(OCAMLBLDFLAGS) $(REAL_IMPL) - -$(RESULT).cmxa $(RESULT).$(EXT_LIB): $(REAL_IMPL_INTF) $(EXTRADEPS) $(RESULTDEPS) - $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(ALL_LDFLAGS) $(OBJS_LIBS) \ - $(OCAMLNLDFLAGS) -o $@ $(REAL_IMPL) -else -ifdef BYTE_OCAML -$(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo: $(REAL_IMPL_INTF) - $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack -o $(LIB_PACK_NAME).cmo $(REAL_IMPL) -else -$(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx: $(REAL_IMPL_INTF) - $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack -o $(LIB_PACK_NAME).cmx $(REAL_IMPL) -endif - -$(RESULT).cma: $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) - $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) \ - $(OBJS_LIBS) -o $@ $(OCAMLBLDFLAGS) $(LIB_PACK_NAME).cmo - -$(RESULT).cmxa $(RESULT).$(EXT_LIB): $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx $(EXTRADEPS) $(RESULTDEPS) - $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(ALL_LDFLAGS) $(OBJS_LIBS) \ - $(OCAMLNLDFLAGS) -o $@ $(LIB_PACK_NAME).cmx -endif - -$(RES_CLIB): $(OBJ_LINK) -ifndef MSVC - ifneq ($(strip $(OBJ_LINK)),) - $(AR) rcs $@ $(OBJ_LINK) - endif -else - ifneq ($(strip $(OBJ_LINK)),) - lib -nologo -debugtype:cv -out:$(RES_CLIB) $(OBJ_LINK) - endif -endif - -.mli.cmi: $(EXTRADEPS) - $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ - if [ -z "$$pp" ]; then \ - echo $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c $(THREAD_FLAG) $(ANNOT_FLAG) \ - $(OCAMLFLAGS) $(INCFLAGS) $<; \ - $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c $(THREAD_FLAG) $(ANNOT_FLAG) \ - $(OCAMLFLAGS) $(INCFLAGS) $<; \ - else \ - echo $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c -pp \"$$pp $(PPFLAGS)\" $(THREAD_FLAG) $(ANNOT_FLAG) \ - $(OCAMLFLAGS) $(INCFLAGS) $<; \ - $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c -pp "$$pp $(PPFLAGS)" $(THREAD_FLAG) $(ANNOT_FLAG) \ - $(OCAMLFLAGS) $(INCFLAGS) $<; \ - fi - -.ml.cmi .ml.$(EXT_OBJ) .ml.cmx .ml.cmo: $(EXTRADEPS) - $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ - if [ -z "$$pp" ]; then \ - echo $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c $(ALL_OCAMLCFLAGS) $<; \ - $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c $(ALL_OCAMLCFLAGS) $<; \ - else \ - echo $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c -pp \"$$pp $(PPFLAGS)\" $(ALL_OCAMLCFLAGS) $<; \ - $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ - -c -pp "$$pp $(PPFLAGS)" $(ALL_OCAMLCFLAGS) $<; \ - fi - -ifdef PACK_LIB -$(REAL_RESULT).cmo $(REAL_RESULT).cmx $(REAL_RESULT).o: $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) - $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack $(ALL_LDFLAGS) \ - $(OBJS_LIBS) -o $@ $(REAL_IMPL) -endif - -.PRECIOUS: %.ml -%.ml: %.mll - $(OCAMLLEX) $< - -.PRECIOUS: %.ml %.mli -%.ml %.mli: %.mly - $(OCAMLYACC) $(YFLAGS) $< - $(QUIET)pp=`sed -n -e 's/.*(\*pp \([^*]*\) \*).*/\1/p;q' $<`; \ - if [ ! -z "$$pp" ]; then \ - mv $*.ml $*.ml.temporary; \ - echo "(*pp $$pp $(PPFLAGS)*)" > $*.ml; \ - cat $*.ml.temporary >> $*.ml; \ - rm $*.ml.temporary; \ - mv $*.mli $*.mli.temporary; \ - echo "(*pp $$pp $(PPFLAGS)*)" > $*.mli; \ - cat $*.mli.temporary >> $*.mli; \ - rm $*.mli.temporary; \ - fi - - -.PRECIOUS: %.ml -%.ml: %.rep - $(CAMELEON_REPORT) $(CAMELEON_REPORT_FLAGS) -gen $< - -.PRECIOUS: %.ml -%.ml: %.zog - $(CAMELEON_ZOGGY) $(CAMELEON_ZOGGY_FLAGS) -impl $< > $@ - -.PRECIOUS: %.ml -%.ml: %.glade - $(OCAML_GLADECC) $(OCAML_GLADECC_FLAGS) $< > $@ - -.PRECIOUS: %.ml %.mli -%.ml %.mli: %.oxridl - $(OXRIDL) $< - -.PRECIOUS: %.ml %.mli %_stubs.c %.h -%.ml %.mli %_stubs.c %.h: %.idl - $(CAMLIDL) $(MAYBE_IDL_HEADER) $(IDLFLAGS) \ - $(CAMLIDLFLAGS) $< - $(QUIET)if [ $(NOIDLHEADER) ]; then touch $*.h; fi - -.c.$(EXT_OBJ): - $(OCAMLC) -c -cc "$(CC)" -ccopt "$(CFLAGS) \ - $(CPPFLAGS) $(CPPFLAGS_WIN32) \ - $(CFLAGS_WIN32) $(CINCFLAGS) $(CFLAG_O)$@ " $< - -.$(EXT_CXX).$(EXT_OBJ): - $(CXX) -c $(CXXFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ - -I'$(OCAMLLIBPATH)' \ - $< $(CFLAG_O)$@ - -$(MLDEPDIR)/%.d: %.ml - $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi - $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ - if [ -z "$$pp" ]; then \ - echo $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ - $(DINCFLAGS) $< \> $@; \ - $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ - $(DINCFLAGS) $< > $@; \ - else \ - echo $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ - -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ - $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ - -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ - fi - -$(BCDIDIR)/%.di $(NCDIDIR)/%.di: %.mli - $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi - $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ - if [ -z "$$pp" ]; then \ - echo $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< \> $@; \ - $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< > $@; \ - else \ - echo $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ - -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ - $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ - -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ - fi - -doc/$(RESULT)/html: $(DOC_FILES) - rm -rf $@ - mkdir -p $@ - $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ - if [ -z "$$pp" ]; then \ - echo $(OCAMLDOC) -html -d $@ $(OCAMLDOCFLAGS) $(INCFLAGS) $(DOC_FILES); \ - $(OCAMLDOC) -html -d $@ $(OCAMLDOCFLAGS) $(INCFLAGS) $(DOC_FILES); \ - else \ - echo $(OCAMLDOC) -pp \"$$pp $(PPFLAGS)\" -html -d $@ $(OCAMLDOCFLAGS) \ - $(INCFLAGS) $(DOC_FILES); \ - $(OCAMLDOC) -pp "$$pp $(PPFLAGS)" -html -d $@ $(OCAMLDOCFLAGS) \ - $(INCFLAGS) $(DOC_FILES); \ - fi - -doc/$(RESULT)/latex: $(DOC_FILES) - rm -rf $@ - mkdir -p $@ - $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ - if [ -z "$$pp" ]; then \ - echo $(OCAMLDOC) -latex $(OCAMLDOCFLAGS) $(INCFLAGS) \ - $(DOC_FILES) -o $@/doc.tex; \ - $(OCAMLDOC) -latex $(OCAMLDOCFLAGS) $(INCFLAGS) $(DOC_FILES) \ - -o $@/doc.tex; \ - else \ - echo $(OCAMLDOC) -pp \"$$pp $(PPFLAGS)\" -latex $(OCAMLDOCFLAGS) \ - $(INCFLAGS) $(DOC_FILES) -o $@/doc.tex; \ - $(OCAMLDOC) -pp "$$pp $(PPFLAGS)" -latex $(OCAMLDOCFLAGS) \ - $(INCFLAGS) $(DOC_FILES) -o $@/doc.tex; \ - fi - -doc/$(RESULT)/latex/doc.ps: doc/$(RESULT)/latex - cd doc/$(RESULT)/latex && \ - $(LATEX) doc.tex && \ - $(LATEX) doc.tex && \ - $(DVIPS) $(DVIPSFLAGS) doc.dvi -o $(@F) - -doc/$(RESULT)/latex/doc.pdf: doc/$(RESULT)/latex/doc.ps - cd doc/$(RESULT)/latex && $(PS2PDF) $( +$ dune install --release # with dune + +$ dune build --release # dune, manual install +$ mv _build/default/bin/main.exe ~/bin/thin-ocamlscript + +$ cat lib/script.ml > oscript.ml # manual, single-file +$ sed 1d bin/main.ml >> oscript.ml # skip 'open' +$ vi oscript.ml # make it print "recompiling! ..." +$ ocamlfind ocamlopt -O3 -package unix -linkpkg -o oscript oscript.ml +``` + +# very brief examples + +```ocaml +#! /usr/bin/env -S thin-ocamlscript -package unix -linkpkg -- +let () = Printf.printf "%.0f\n" (Unix.time ()) +``` + +```ocaml +#! /usr/bin/env -S thin-ocamlscript -package ppx_deriving.show -linkpkg -- +type args = string array [@@deriving show] +let () = Format.printf "%a\n" pp_args Sys.argv +``` + +examples/ has replications of ocamlscript's examples + +# env + +`env -S` and `--` are required for reasonable behavior. Without the latter your +script cannot itself receive a `--`. With `env` and no `-S` you can't pass any +arguments to ocamlfind, and you'll still have the `--` limitation on the +script's own arguments. + +Even if you'd rather not search through PATH to find thin-ocamlscript, you need +`env -S`, and in this case it should be an absolute path or it'll depend on the +caller's working directory: + +```ocaml +#! /usr/bin/env -S /opt/ocaml/bin/thin-ocamlscript -package unix -linkpkg -- +(* ... *) +``` + +# files + +```ocaml +let tmpname script = script ^ ".tmp.ml" +let exename script = script ^ ".exe" + +let cleanup script = + List.iter + (fun ext -> + try Unix.unlink (script ^ ext) with + | Unix.Unix_error (Unix.ENOENT, _, _) -> () + | exn -> raise exn) + [".tmp.ml"; ".tmp.o"; ".tmp.cmx"; ".tmp.cmi"] +``` + +The exename is compiled if not newer than the script. The tmpname is deleted +after compilation. There is no protection against you doing something placing a +hello.ml script next to a hello.tmp.ml that you would regret losing. + +# defects + +1. Editors do not understand that the shebang line affects the environment of the script, and will e.g. complain about an "Unbound module Unix" in the first example. For this reason I think a 'dune-ocamlscript' (ocamlscript-dune?) that unpacks/repacks a script from an ephemeral dune repo would make for a generally more pleasant experience. + +2. The name's too long. It should probably also be 'ocamlscript-thin' but to me that suggests a relationship with ocamlscript. + +# design + +A temporary file is required by ocamlc/ocamlopt objecting to #! lines. + +The temporary file ending in .ml is required by camlp4. + +The .ml.exe is required by shell tab completion: if it was only .exe, then +hello.ml and hello.exe in the same dir would pause completion at 'hello.'. + +To not use hidden files for the executable is advised to help differentiate it +from malware. + +I preferred to avoid /tmp for compilation for some subtle reasons, including +that sensitive scripts would not accidentally scape filesystem security on the +script, think /root/bin/ping-secret-api.ml diff --git a/TODO b/TODO deleted file mode 100644 index b11f79b..0000000 --- a/TODO +++ /dev/null @@ -1,3 +0,0 @@ -* move documentation from pbwiki to github: - http://ocaml.pbworks.com/w/page/5867969/Ocamlscript - http://ocaml.pbworks.com/w/page/5867970/OcamlscriptDoc diff --git a/bin/dune b/bin/dune new file mode 100644 index 0000000..a74158f --- /dev/null +++ b/bin/dune @@ -0,0 +1,4 @@ +(executable + (public_name thin-ocamlscript) + (name main) + (libraries unix thin_ocamlscript)) diff --git a/bin/main.ml b/bin/main.ml new file mode 100644 index 0000000..9621cd7 --- /dev/null +++ b/bin/main.ml @@ -0,0 +1,25 @@ +open Thin_ocamlscript.Script + +let split_args a = + let len = Array.length a in + match Array.find_index (( = ) "--") a with + | None -> ([||], a.(1), Array.sub a 1 (len - 1)) + | Some i -> + (Array.sub a 1 (i - 1), a.(i + 1), Array.sub a (i + 1) (len - i - 1)) + +let () = + if Array.length Sys.argv = 1 then ( + Printf.printf + "usage: #! /usr/bin/env -S %s -package unix -linkpkg --\n\ + The -- should always be present and always at the end of the line\n\ + Arguments are to ocamlfind ocamlopt\n\ + For more, https://github.com/jrfondren/thin-ocamlscript\n" + (Filename.basename Sys.argv.(0)); + exit 1); + let oargs, script, sargs = split_args Sys.argv in + let exe = exename script in + if needs_recompile script exe then + Fun.protect + ~finally:(fun () -> cleanup script) + (fun () -> recompile oargs script); + Unix.execv exe sargs diff --git a/common.ml b/common.ml deleted file mode 100644 index 1c4b646..0000000 --- a/common.ml +++ /dev/null @@ -1,6 +0,0 @@ -let extra_args = ref ([] : string list) -let trash = ref ([] : string list) -let verbose = ref false -let script_dir = ref (Sys.getcwd ()) -let compile : (string -> string -> int) ref = - ref (fun source result -> failwith "Compile.compile is unset") diff --git a/common.mli b/common.mli deleted file mode 100644 index 473970b..0000000 --- a/common.mli +++ /dev/null @@ -1,33 +0,0 @@ -(** Options that are shared by all compilation modules. *) - -(** All arguments which are not valid options for ocamlscript - but are not arguments of the script either. - Typically it would be the case of unix.cmxa in - "#!/usr/bin/ocamlscript unix.cmxa". - It is the responsibility of the "compile" function to handle these - arguments. The default "compile" command (Ocamlscript.Ocaml.compile) - simply passes these arguments to ocamlopt. -*) -val extra_args : string list ref - -(** runtime trash which may contain the name of the executable itself, - for self-removal, in case it is a temporary file (e.g. generated from - standard input). *) -val trash : string list ref - -(** If this option is true, ocamlscript prints some debugging information - to stdout. *) -val verbose : bool ref - -(** [script_dir] is meant to hold the absolute path to the directory which - contains the script, or just the current directory at the time when - ocamlscript was started if the script is not read from a file. *) -val script_dir : string ref - -(** The function which is used to compile the program. - [compile source result] reads the source code from file [source] and - writes the executable to file [result]. This function - should return 0 if it succeeds, and 1 or another code otherwise. - Its default value is [Ocamlscript.Ocaml.compile]. -*) -val compile : (string -> string -> int) ref diff --git a/dune-project b/dune-project new file mode 100644 index 0000000..85a1027 --- /dev/null +++ b/dune-project @@ -0,0 +1,18 @@ +(lang dune 3.20) +(name thin-ocamlscript) +(generate_opam_files true) +(source + (github jrfondren/thin-ocamlscript)) + +(authors "Julian Fondren ") +(maintainers "Julian Fondren ") +(license MIT) +(documentation https://github.com/jrfondren/thin-ocamlscript) + +(package + (name thin-ocamlscript) + (synopsis "OCaml as a scripting language with ocamlfind") + (description "Similar to ocamlscript, but configuration is restricted to the shebang line.") + (depends + (ocaml (>= 5.2)) ;; or 5.1 which introduced Array.find_index + (ocamlfind (>= 1.9.8)))) ;; latest diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 5c7a251..0000000 --- a/examples/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -.PHONY: default clean -default: - ../ocamlscript factorial.ml 100 - ../ocamlscript revised.ml - ../ocamlscript with-includes.ml - echo "1+1" | ../ocamlscript calc.ml -clean: - $(RM) *~ *.exe diff --git a/examples/README b/examples/README deleted file mode 100644 index aa3076e..0000000 --- a/examples/README +++ /dev/null @@ -1,57 +0,0 @@ -This directory contains a few examples that should work on every platform -where ocamlscript can be installed, except hello-web.ml which requires -the ocamlnet library. - -One attractive feature on Unix-like systems is auto-executable -scripts, i.e. scripts that start with a shebang (#!) line. -The interpretation of this line is OS-dependent, so we avoid using it -in most examples. - -* factorial.ml: computes the exact factorial of integers using the num library. - Run it with: ocamlscript factorial.ml - or: ocamlscript factorial.ml 123 - -* with-includes.ml: - this program shows how to use other source file(s) than the - main script. - Run it with: ocamlscript with-includes.ml - Note that the last modification date of - the other source file(s) is not checked by - ocamlscript. If you want to force a recompilation, use - the -f option. - -* calc.ml: a complete example of using multiple source files. - lexer.mll, parser.mly and calc.ml were copied from the Objective - Caml reference manual (chapter on ocamllex and ocamlyacc), - and calc.ml was adapted to work with ocamlscript. - Now it compiles and runs in one command: - ocamlscript calc.ml - instead of 4: - ocamllex lexer.mll - ocamlyacc parser.mly - ocamlopt -o calc parser.mli parser.ml lexer.ml calc.ml - ./calc - -* revised.ml: a program in Camlp4 revised syntax - -* pp.ml: shows how to use a custom preprocessor - -* hello-web.ml: a minimalistic CGI program which prints a webpage - saying only "Yes", "No", "Other" or "Undefined" - depending on whether the CGI variable "foo" has value - 1, 0, some other defined value, or is undefined. - It requires the ocamlnet library and can be tested - from the command-line. - It recommended to compile and install such programs, - although installing the script directly into your - cgi-bin works too, as long as httpd has write access - to your cgi-bin directory and has access to the OCaml tools. - - Compilation: - ocamlscript -c hello-web.ml - - Installation: - cp hello-web.ml.exe /path/to/cgi-bin/ - - Testing: - http://localhost/cgi-bin/hello-web.ml.exe?foo=1 diff --git a/examples/calc.ml b/examples/calc.ml deleted file mode 100644 index 39016d1..0000000 --- a/examples/calc.ml +++ /dev/null @@ -1,14 +0,0 @@ -Ocaml.sources := ["parser.mly"; "lexer.mll"] --- -(* Calculator example from the Objective Caml reference manual *) - -(* File calc.ml *) -let _ = - try - let lexbuf = Lexing.from_channel stdin in - while true do - let result = Parser.main Lexer.token lexbuf in - print_int result; print_newline(); flush stdout - done - with Lexer.Eof -> - exit 0 diff --git a/examples/factorial.ml b/examples/factorial.ml old mode 100644 new mode 100755 index e09d363..03d3acc --- a/examples/factorial.ml +++ b/examples/factorial.ml @@ -1,28 +1,31 @@ -Ocaml.packs := ["num"] --- +#! /usr/bin/env -S thin-ocamlscript -package num -linkpkg -- + open Printf open Big_int let rec fac n = - if le_big_int n zero_big_int then unit_big_int - else mult_big_int n (fac (pred_big_int n)) + if le_big_int n zero_big_int then + unit_big_int + else + mult_big_int n (fac (pred_big_int n)) let rec get_int attempt = - try + try printf "Please enter a number: "; read_int () with _ -> - if attempt >= 3 then - (printf "Bye.\n"; - exit 1) - else - (printf "This is not a number. Please try again.\n"; - get_int (attempt + 1)) + if attempt >= 3 then ( + printf "Bye.\n"; + exit 1) + else ( + printf "This is not a number. Please try again.\n"; + get_int (attempt + 1)) let _ = - let x = + let x = match Sys.argv with - [| _; s |] -> (try int_of_string s with _ -> get_int 2) - | _ -> get_int 1 in + | [|_; s|] -> ( try int_of_string s with _ -> get_int 2) + | _ -> get_int 1 + in let y = fac (big_int_of_int x) in printf "%i! = %s\n" x (string_of_big_int y) diff --git a/examples/hello-web.ml b/examples/hello-web.ml deleted file mode 100755 index 58b6ce7..0000000 --- a/examples/hello-web.ml +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env ocamlscript -Ocaml.packs := ["cgi"] --- -let get actobj = - try - (match (actobj#argument "foo")#value with - "0" -> "No" - | "1" -> "Yes" - | _ -> "Other") - with Not_found -> "Undefined" - -let print (actobj : Netcgi.std_activation) s = - actobj#set_header ~content_type:"text/plain" (); - actobj#output#output_string s; - actobj#output#commit_work () - -let _ = - let actobj = new Netcgi.std_activation () in - print actobj (get actobj) diff --git a/examples/hello.ml b/examples/hello.ml old mode 100644 new mode 100755 index 417ae33..1ecb395 --- a/examples/hello.ml +++ b/examples/hello.ml @@ -1 +1,3 @@ +#! /usr/bin/env -S thin-ocamlscript -- + let () = print_endline "Hello from hello.ml" diff --git a/examples/lexer.mll b/examples/lexer.mll deleted file mode 100644 index af7e068..0000000 --- a/examples/lexer.mll +++ /dev/null @@ -1,18 +0,0 @@ -(* Calculator example from the Objective Caml reference manual *) - -(* File lexer.mll *) -{ -open Parser (* The type token is defined in parser.mli *) -exception Eof -} -rule token = parse - [' ' '\t'] { token lexbuf } (* skip blanks *) - | ['\n' ] { EOL } - | ['0'-'9']+ as lxm { INT(int_of_string lxm) } - | '+' { PLUS } - | '-' { MINUS } - | '*' { TIMES } - | '/' { DIV } - | '(' { LPAREN } - | ')' { RPAREN } - | eof { raise Eof } diff --git a/examples/no-merlin-errors.ml b/examples/no-merlin-errors.ml deleted file mode 100644 index ff84a94..0000000 --- a/examples/no-merlin-errors.ml +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ocamlscript -let open Ocamlscript.Std in (** Inclues Ocamlscript and special (--) operator *) -begin - Ocaml.packs := ["cmdliner"] -end --- -(* ^^^ opened as infix operator here, returning a unit. - * Must be on its own line! *) -() (* need to close out the -- operator *) - -let f x y = x + 1 (* can parse basic staements after the () *) - -let arg_info = Cmdliner.Arg.info (* ensure packs are present *) - -let () = print_endline "look ma, no merlin errors!" diff --git a/examples/parser.mly b/examples/parser.mly deleted file mode 100644 index 3b40538..0000000 --- a/examples/parser.mly +++ /dev/null @@ -1,25 +0,0 @@ -/* Calculator example from the Objective Caml reference manual */ - -/* File parser.mly */ -%token INT -%token PLUS MINUS TIMES DIV -%token LPAREN RPAREN -%token EOL -%left PLUS MINUS /* lowest precedence */ -%left TIMES DIV /* medium precedence */ -%nonassoc UMINUS /* highest precedence */ -%start main /* the entry point */ -%type main -%% -main: - expr EOL { $1 } -; -expr: - INT { $1 } - | LPAREN expr RPAREN { $2 } - | expr PLUS expr { $1 + $3 } - | expr MINUS expr { $1 - $3 } - | expr TIMES expr { $1 * $3 } - | expr DIV expr { $1 / $3 } - | MINUS expr %prec UMINUS { - $2 } -; diff --git a/examples/pp.mll b/examples/pp.mll deleted file mode 100644 index fd714cd..0000000 --- a/examples/pp.mll +++ /dev/null @@ -1,12 +0,0 @@ -(* Using ocamllex as if it were not supported by default, - to show how to use a custom preprocessor. *) -Ocaml.pp := - Some (fun file -> - let o = if not (Filename.check_suffix file ".mll") then "main.ml" - else Filename.chop_extension file ^ ".ml" in - Pipeline.command ["ocamllex"; file; "-o"; o; "-q"], [o]) --- -rule token = parse - _ { () } -| eof { () } -{ print_endline "Hello from pp.ml using ocamllex" } diff --git a/examples/revised.ml b/examples/revised.ml old mode 100644 new mode 100755 index 29ea353..3148029 --- a/examples/revised.ml +++ b/examples/revised.ml @@ -1,18 +1,8 @@ -(* Using Camlp4 revised syntax *) - -(* Camlp4 is used by default *) -(* Ocaml.use_camlp4 := true *) - -(* this selects the revised syntax: *) -Ocaml.revised := true --- - -(* This is the program in the revised syntax: *) - -value hello () = +#! /usr/bin/env -S thin-ocamlscript -package camlp4 -syntax camlp4r -linkpkg -- +value hello () = do { print_endline "Hello!"; print_endline "Goodbye!" } ; -value _ = hello () +value __ = hello () ; diff --git a/examples/with-includes.ml b/examples/with-includes.ml deleted file mode 100644 index 1234d2f..0000000 --- a/examples/with-includes.ml +++ /dev/null @@ -1,3 +0,0 @@ -Ocaml.sources := [ "hello.ml" ] --- -Hello.hello () diff --git a/lib/dune b/lib/dune new file mode 100644 index 0000000..8c0c360 --- /dev/null +++ b/lib/dune @@ -0,0 +1,3 @@ +(library + (name thin_ocamlscript) + (libraries unix)) diff --git a/lib/script.ml b/lib/script.ml new file mode 100644 index 0000000..6662155 --- /dev/null +++ b/lib/script.ml @@ -0,0 +1,50 @@ +let tmpname script = script ^ ".tmp.ml" +let exename script = script ^ ".exe" + +let cleanup script = + List.iter + (fun ext -> + try Unix.unlink (script ^ ext) with + | Unix.Unix_error (Unix.ENOENT, _, _) -> () + | exn -> raise exn) + [".tmp.ml"; ".tmp.o"; ".tmp.cmx"; ".tmp.cmi"] + +let needs_recompile source exe = + (not (Sys.file_exists exe)) + || (Unix.stat exe).st_mtime <= (Unix.stat source).st_mtime + +let recompile flags source = + let buflen = 4096 in + let perm = (Unix.stat source).st_perm in + let oflags = Out_channel.[Open_wronly; Open_creat; Open_trunc; Open_binary] in + In_channel.with_open_bin source (fun inp -> + Out_channel.with_open_gen oflags perm (tmpname source) (fun out -> + (* drop first line only if it's #! *) + let line = In_channel.input_line inp |> Option.get in + if not @@ String.starts_with ~prefix:"#!" line then + Printf.fprintf out "%s\n" line; + + (* underlying I/O buffers; all this does is avoid line-handling *) + let buffer = Bytes.make buflen '\000' in + let rec loop () = + match In_channel.input inp buffer 0 buflen with + | 0 -> () + | n when n = buflen -> + Out_channel.output_bytes out buffer; + loop () + | n -> + Out_channel.output_bytes out (Bytes.sub buffer 0 n); + loop () + in + loop ())); + let args = + Array.append + [|"ocamlfind"; "ocamlopt"|] + (Array.append flags [|"-o"; exename source; "-impl"; tmpname source|]) + in + let pid = + Unix.create_process "ocamlfind" args Unix.stdin Unix.stdout Unix.stderr + in + match Unix.waitpid [] pid with + | p, Unix.WEXITED 0 when p = pid -> () + | _, _ -> failwith ("attempted: " ^ String.concat " " (Array.to_list args)) diff --git a/main.ml b/main.ml deleted file mode 100644 index fd1c357..0000000 --- a/main.ml +++ /dev/null @@ -1,557 +0,0 @@ -(* - ocamlscript: a utility to have shell-like optimised scripts in OCaml - - Copyright 2005 David MENTRE - Copyright 2006 Martin Jambon -*) - -open Printf -open Ocamlscript - -module Opt = -struct - let set what opt s = - match !opt with - | Some _ -> failwith (sprintf "%s is already set" what) - | None -> opt := Some s - - let help = ref false (* help and exit *) - let c = ref false (* compile only *) - let f = ref false (* force recompile *) - let debug = ref false (* additional stdout output *) - let version = ref false (* show version and exit *) - let from = ref (None : [`File of string | `String of string | `Stdin] option) - let o = ref None (* choose a name for the compiled executable *) - let vm = ref None (* possible bytecode interpreter *) - let extra_args = ref [] - - let help_message = "\ -Usage: ocamlscript [ PACKED_OPTIONS [ OPTIONS ] [ -- ] [SCRIPTNAME] [ARGS] ] - -Ocamlscript normally reads the source code of a program from a file, looks -if a compiled executable exists for this program. If it exists and if it -is more recent than the source file, the executable is executed immediately, -otherwise it is updated by executing compilation instructions that can -be specified in the program file. - -A typical self-executable script looks as follows: - - #!/usr/bin/env ocamlscript - (* this is the compilation section, in OCaml *) - Ocaml.packs := [\"unix\"; \"micmatch_pcre\"] (* Findlib packages *) - -- - (* this is the program section *) - let _ = - ... - - -Structure of the command line: - -PACKED_OPTIONS: - the first argument of ocamlscript. It is either unpacked into - several arguments that are passed to ocamlscript or into a script name - if this name doesn't start with \"-\". Double-quotes can be used - to enclose arguments that contain whitespace or double-quotes. - Double-quotes must be doubled. For instance, the following - self-executable script would be compiled into an executable named - Hello \"World\": - #!/usr/bin/ocamlscript -o \"Hello \"\"World\"\"\" - print_endline \"Hello \"World\"\" - - Important note: on some Unix systems, the whole - '-o \"Hello \"\"World\"\"\"' string is passed as a single argument - to ocamlscript. This is why the first argument must be unpacked, - even if ocamlscript is called explicitely from the command line. - -OPTIONS: - any number of arguments in this section are treated like options - to ocamlscript until a either a non-option is encountered, which is - understood as the script name (SCRIPTNAME) or \"--\" which stops - the list of arguments that are passed to ocamlscript. - -Ocamlscript supports the following options: - -- marks the end of ocamlscript arguments - -help displays a help message and exit - --help same as -help - -c compile only - -o EXEC_NAME specify a name for the executable - (required if the program is not read from a file) - -e PROGRAM execute the code given here instead of reading it from a file - -f force recompilation which is otherwise based on last modification dates - -debug print messages about what ocamlscript is doing - -version prints the version identifier to stdout and exit - - read program from stdin instead of a file - -vm VIRTUAL_MACHINE run the executable using this virtual machine (e.g. - ocamlrun) - -\"--\": passed as an argument to ocamlscript in the PACKED_OPTIONS argument - or in the OPTIONS argument marks the end of the arguments that - are passed to ocamlscript. Arguments that follow will be - interpreted as arguments of the script. - Arguments that follow \"--\" in the PACKED_OPTIONS argument - will be passed as arguments to the final executable. The first - argument that follows \"--\" in the OPTIONS command line arguments - is treated as the script name, unless the program is read from - another source, as specified by options \"-e\" (a string) or \"-\" - (standard input). - - -For a full documentation on the structure of the compilation section, go to -ocamlscript's website (http://martin.jambon.free.fr/ocamlscript.html). -" -end - -(* more generic than .opt since we can compile with other commands than - ocamlopt *) -let bin_suffix = ".exe" - -let bin_name src = - match !Opt.o with - | Some s -> s - | None -> src ^ bin_suffix - -let obin_name src = - match !Opt.o with - | Some s -> s - | None -> - match src with - | `Stdin | `String _ -> failwith "please specify a name \ - for the executable with -o" - | `File s -> s ^ bin_suffix - -let source_name = function - | None -> assert false - | Some s -> s - -let ( // ) = Filename.concat - -let endline = if Sys.os_type = "Win32" then "\r\n" else "\n" -let output_line oc s = output_string oc s; output_string oc endline - -(* based on last modification date only. - Doesn't handle dependencies toward runtime things that might change - incompatibly (DLLs, bytecode version, ...). - ocamlscript -f can be used to force recompilation in these cases. *) -let needs_recompile ?log = function - | `Stdin | `String _ -> true - | `File source -> - let bin = bin_name source in - not (Sys.file_exists bin) || - (Unix.stat bin).Unix.st_mtime <= (Unix.stat source).Unix.st_mtime - -(* -RE sep = "--" blank* eos -*) -(* -RE_STR "--" blank* eos -*) -let sep = Str.regexp "--[\t ]*$" -let is_sep s = Str.string_match sep s 0 - -let rec split_list accu is_sep = function - | [] -> `One (List.rev accu) - | hd :: tl -> - if is_sep hd then `Two (List.rev accu, tl) - else split_list (hd :: accu) is_sep tl - -let read_locstyle = function - | "ocaml" -> `Ocaml - | "blank" -> `Blank - | "none" -> `None - | _ -> failwith "invalid locstyle directive" - -(* let process_directives lines = - * let style = ref `Ocaml in - * let ocaml_lines = - * List.map - * (function - * | / "#" blank* "locstyle" blank* - * (lower ['_'alnum*] as x) blank* ";;"? blank* eol / as s -> - * style := read_locstyle x; - * String.make (String.length s) ' ' - * | s -> s) - * lines in - * (ocaml_lines, !style) *) - -let process_directives = - let micmatch_1 = - Str.regexp - "#[\t ]*locstyle[\t ]*\\([a-z][0-9A-Z_a-z]*\\)[\t ]*\\(;;\\)?[\t ]*$" - in - fun lines -> - let style = ref `Ocaml in - let ocaml_lines = - List.map - (fun micmatch_any_target -> - let micmatch_match_target_1 = micmatch_any_target in - (try - match micmatch_match_target_1 with - | micmatch_1_target as s when true -> - if Str.string_match micmatch_1 micmatch_1_target 0 then - let x = Str.matched_group 1 micmatch_1_target in - fun () -> - style := read_locstyle x; - String.make (String.length s) ' ' - else raise Exit - | _ -> raise Exit - with - Exit -> - let s = micmatch_match_target_1 in fun () -> s) - ()) - lines - in - ocaml_lines, !style - -(* let split_lines lines = - * let lines1, lines2 = split_list [] is_sep lines in - * let pos1, header = - * match lines1 with - * | / "#!" / :: header -> (2, header) - * | _ -> (1, lines1) in - * let pos2 = List.length lines1 + 2 in - * (pos1, header, pos2, lines2) *) - -let split_lines lines = - let test s = String.length s >= 2 && s.[0] = '#' && s.[1] = '!' in - let lines1, lines2 = - match split_list [] is_sep lines with - | `One (s :: prog) when test s -> [s], prog - | `One prog -> [], prog - | `Two (a, b) -> (a, b) in - let (pos1, header) = - match lines1 with - | s :: header when test s -> 2, header - | _ -> 1, lines1 in - let pos2 = List.length lines1 + 2 in - (pos1, header, pos2, lines2) - -let get_dir file = - let dir = Filename.dirname file in - if Filename.is_relative dir then Filename.concat (Sys.getcwd ()) dir - else dir - -let write_header ~pos ~source ~source_option ~verbose ~prog_file lines = - let bin = obin_name source_option in - let extra_args = - match !Opt.extra_args with - | [] -> "" - | l -> - sprintf "Ocamlscript.Common.extra_args := [ %s];;\n" - (String.concat "; " (List.map (fun s -> sprintf "%S" s) l)) in - let trash, script_dir = - match source_option with - | `Stdin - | `String _ -> (sprintf "Ocamlscript.Common.trash := \ - %S :: !Ocamlscript.Common.trash;;\n" - bin, - Sys.getcwd ()) - | `File script_name -> "", get_dir script_name in - - let file, oc = Filename.open_temp_file "meta" ".ml" in - fprintf oc "\ -#%i %S;; -(* Opam installations of findlib place topfind in a different directory *) -#directory \"+compiler-libs\";; -let () = - try Topdirs.dir_directory (Sys.getenv \"OCAML_TOPLEVEL_PATH\") - with Not_found -> () -;; -#use \"topfind\";; -#require \"ocamlscript\";; -Ocamlscript.Common.verbose := %s;; -Ocamlscript.Common.script_dir := %S;; -%s%sOcamlscript.Common.compile := Ocamlscript.Ocaml.compile;; -open Ocamlscript;; -open Utils;; -#%i %S;;\n" - pos source verbose script_dir extra_args trash pos source; - - List.iter (output_line oc) lines; - - fprintf oc "\ -let _ = exit (!Ocamlscript.Common.compile %S %S);;\n" prog_file bin; - close_out oc; - file - - -let write_body ~pos ~source ~locstyle lines = - let file, oc = Filename.open_temp_file "prog" ".ml" in - (match locstyle with - | `Ocaml -> fprintf oc "#%i %S;;\n" pos source - | `Blank -> for i = 1 to pos - 1 do output_string oc endline done - | `None -> ()); - List.iter (output_line oc) lines; - close_out oc; - file - -module Text = -struct - exception Internal_exit - - let iter_lines_of_channel f ic = - try - while true do - let line = - try input_line ic - with End_of_file -> raise Internal_exit in - f line - done - with Internal_exit -> () - - let iter_lines_of_file f file = - let ic = open_in file in - try - iter_lines_of_channel f ic; - close_in ic - with exn -> - close_in_noerr ic; - raise exn - - let lines_of_channel ic = - let l = ref [] in - iter_lines_of_channel (fun line -> l := line :: !l) ic; - List.rev !l - - let lines_of_file file = - let l = ref [] in - iter_lines_of_file (fun line -> l := line :: !l) file; - List.rev !l -end - - -let split_file = - let newline = Str.regexp "\r?\n" in - fun ?log source_option -> - let source, lines = - match source_option with - | `Stdin -> "", Text.lines_of_channel stdin - | `String s -> "", (Str.split newline) s - | `File file -> file, Text.lines_of_file file in - - let pos1, unprocessed_header, pos2, body = split_lines lines in - let header, locstyle = process_directives unprocessed_header in - - let verbose = if log = None then "false" else "true" in - - let prog_file = - write_body ~pos:pos2 ~source ~locstyle body in - let meta_file = - write_header - ~pos:pos1 ~source ~source_option ~verbose ~prog_file header in - (meta_file, prog_file) - - -let compile_script ?log source_option = - let meta_name, prog_name = split_file ?log source_option in - Fun.protect (fun () -> Pipeline.run_command (Pipeline.command ["ocaml"; meta_name])) - ~finally:(fun () -> - (* comment out for debugging: *) - Pipeline.remove meta_name; - Pipeline.remove prog_name - ) - -let absolute path = - if Filename.is_relative path then - Sys.getcwd () // path - else path - -let option0 ?(refuse_input = false) x = - let result = ref `Yes in - (match x with - | "--" -> result := `Stop - | "-help" - | "--help" -> Opt.help := true - | "-c" -> Opt.c := true - | "-f" -> Opt.f := true - | "-debug" -> Opt.debug := true - | "-version" -> Opt.version := true - | "-" -> - if refuse_input then - failwith "option - is disabled in this context" - else - Opt.set "source" Opt.from `Stdin - | _ -> result := `No); - !result - -let option1 ?(refuse_input = false) x y = - let result = ref true in - (match x with - | "-o" -> Opt.set "executable name" Opt.o y - | "-vm" -> Opt.set "virtual machine" Opt.vm y - | "-e" -> - if refuse_input then - failwith "option -e is disabled in this context" - else - Opt.set "source" Opt.from (`String y) - | _ -> result := false); - !result - -let start_option1 = - function - | "-o" - | "-vm" - | "-e" -> true - | _ -> false - -let optionx = function - | "" -> false - | s when s.[0] = '-' -> failwith (sprintf "%s is not a valid option" s) - | _ -> false - -let other_arg x = - Opt.extra_args := x :: !Opt.extra_args - - -let process_ocamlscript_args ?refuse_input ?(accept_non_option = false) l = - let rec loop = function - | x :: rest as l -> - begin - match option0 x with - | `Stop -> (None, true, rest) - | `Yes -> loop rest - | `No -> - match l with - | x :: y :: rest when option1 ?refuse_input x y -> loop rest - | x :: rest -> - if start_option1 x then - (Some x, false, rest) - else if optionx x then - loop rest - else if accept_non_option then - (other_arg x; loop rest) - else (None, false, l) - | [] -> assert false - end - | [] -> (None, false, []) in - loop l - - -let unquote s = - let buf = Buffer.create (String.length s) in - let i = ref 0 in - let len = String.length s in - while !i < len do - match s.[!i] with - | '"' -> Buffer.add_char buf '"'; i := !i + 2 - | c -> Buffer.add_char buf c; i := !i + 1 - done; - Buffer.contents buf - -(* -let tokenize_args = - COLLECT '"' (([^'"']|"\"\"")* as x := unquote) '"' - | ([^space '"']+ as x) -> x -*) -(* - RE_STR '"' ([^'"']|"\"\"")* '"' | [^space '"']+ -*) -let tokenize_args = - let token = Str.regexp "\"\\([^\"]\\|\"\"\\)*\"\\|[^ \"]+" in - fun s -> - List.fold_right - (fun x accu -> - match x with - | Str.Delim s -> - (if s <> "" && s.[0] = '"' then - unquote (String.sub s 1 (String.length s - 2)) - else s) :: accu - | _ -> accu) - (Str.full_split token s) [] - - -let guess_arg1 s = - match tokenize_args s with - | [s'] when String.length s' >= 1 && s'.[0] <> '-' -> `Script_name - | l -> - `Ocamlscript_args (process_ocamlscript_args - ~refuse_input:true - ~accept_non_option:true l) - -(* name of Sys.argv.(0) in the final process (execution of the binary) - depending on where the source program comes from: - - from a file: the name of the source file - - from stdin: - sh: sh - perl: - - python: "" - ocamlscript: "" - - from a string: - sh -c: sh - perl -e: -e - python -c: -c - ocamlscript -e: -e -*) - -let main () = - let script_path_option, script_args = - match Array.to_list Sys.argv with - | ocamlscript :: (arg1 :: other_args as l) -> - (match guess_arg1 arg1 with - | `Script_name -> (`File (absolute arg1), l) - | `Ocamlscript_args (opt1, stopped, hardcoded_script_args) -> - let command_line_script_args = - let continued_args = - match opt1 with - | None -> other_args - | Some o1 -> o1 :: other_args in - if stopped then continued_args - else - let opt1', stopped', command_line_script_args = - process_ocamlscript_args continued_args in - (match opt1' with - | None -> () - | Some x -> failwith - (sprintf "%s option expects an argument" x)); - command_line_script_args in - match !Opt.from with - | Some `Stdin -> - (`Stdin, - "" :: - (hardcoded_script_args @ command_line_script_args)) - | Some (`String s) -> - (`String s, - "-e" :: - (hardcoded_script_args @ command_line_script_args)) - | Some (`File s) -> assert false - | None -> - match command_line_script_args with - | [] -> - Opt.set "source" Opt.from `Stdin; - (`Stdin, - "" :: hardcoded_script_args) - | script_name :: l -> - Opt.set "source" Opt.from (`File script_name); - (`File (absolute script_name), - script_name :: (hardcoded_script_args @ l))) - | [_] | [] -> - Opt.set "source" Opt.from `Stdin; - (`Stdin, [""]) in - - if !Opt.help then - print_string Opt.help_message - else if !Opt.version then - print_endline Version.version - else - let bin = obin_name script_path_option in - let log = if !Opt.debug then Some stdout else None in - let compilation_status = - if !Opt.f || needs_recompile script_path_option then - let status = compile_script ?log script_path_option in - Pipeline.maybe_log log "compilation exit status: %i\n%!" status; - status - else 0 in - - if compilation_status = 0 && not !Opt.c then - let real_bin, real_args = - match !Opt.vm with - | None -> bin, script_args - | Some vm -> vm, (bin :: List.tl script_args) in - Unix.execv real_bin (Array.of_list real_args) - else (* includes the case where there is non-writeable executable *) - exit compilation_status - -let _ = - try main () - with Failure s -> - eprintf "ocamlscript: %s\n%!" s; - exit 2 diff --git a/ocaml.ml b/ocaml.ml deleted file mode 100644 index a1824c6..0000000 --- a/ocaml.ml +++ /dev/null @@ -1,165 +0,0 @@ -open Printf -open Pipeline -open Utils - - -let camlp4o = ref "camlp4o" -let camlp4r = ref "camlp4r" -let ocamllex = ref "ocamllex" -let ocamlyacc = ref "ocamlyacc" -let ocamlc = ref "ocamlc" -let ocamlopt = ref "ocamlopt" -let ocamlfind = ref "ocamlfind" - -let packs = ref [] (* findlib packages *) -let sources = ref [] (* extra sources *) -let use_ocamllex = ref false (* preprocess with ocamllex before camlp4 *) -let use_camlp4 = ref true (* by default camlp4 is used *) -let use_ocamlc = ref false (* by default we want native code *) -let use_ocamlfind = ref false (* used only if necessary *) -let revised = ref false (* use this to use the revised syntax *) -let ocamlflags = Common.extra_args (* any options that you may want to pass - to ocamlopt *) -let ppopt = ref [] (* any options that you may want to pass - to camlp4o or camlp4r *) - -type pp = string -> (Pipeline.command * string list) - -let pp : pp option ref = ref None (* additional preprocessor *) -let ppsrcloc = ref None (* non-standard source location generator *) - - - -let exe s = - match Sys.os_type with - | "Win32" | "Cygwin"-> - if Filename.check_suffix s ".exe" then s - else s ^ ".exe" - | "Unix" | _ -> s - -let import path = - let src = !+ path in - let dst = Filename.basename src in - let head = - match !pp, !ppsrcloc, !revised with - | Some _, Some f, _ -> f src - | _, _, false -> sprintf "#1 %S;;\n" src - | _, _, true -> sprintf "#1 %S;\n" src in - Pipeline.copy_file ~head src dst - -(* let ocamllex_command input = - * if !use_ocamllex then - * Some ((fun () -> ()), - * new_cmd [!ocamllex; input; - * "-o"; "ocamlscript_ocamllex_out.ml"; "-q"], - * "ocamlscript_ocamllex_out.ml") - * else None *) - -let file_kind file = - if Filename.check_suffix file ".mli" then `Mli - else if Filename.check_suffix file ".ml" then `Ml - else if Filename.check_suffix file ".mll" then `Mll - else if Filename.check_suffix file ".mly" then `Mly - else - try - let prefix = Filename.chop_extension file in - let len = String.length file - String.length prefix in - `Ext (String.sub file (String.length file - len) len) - with Invalid_argument _ -> - `Unknown - -let extra_command file = - match file_kind file with - | `Mli | `Ml -> ([], [file]) - | `Mll -> - ([command [!ocamllex; file; "-q"]], - [(Filename.chop_extension file) ^ ".ml"]) - | `Mly -> - let p = Filename.chop_extension file in - ([command [!ocamlyacc; file]], - [p ^ ".mli"; p ^ ".ml"]) - | `Ext s -> - failwith (sprintf "don't know how to handle %s files: %s" s file) - | `Unknown -> - failwith (sprintf "don't know how to handle this file: %s" file) - -let pp_command file = - match !pp with - | None -> [], [file] - | Some f -> - let cmd, files = f file in - ([cmd], files) - - -let extra_commands sources = - let input_files1 = List.map Filename.basename sources in - let cmds1, input_files2 = List.split (List.map pp_command input_files1) in - let cmds2, files = - List.split (List.map extra_command (List.flatten input_files2)) in - (List.flatten (cmds1 @ cmds2), List.flatten files) - - -let ocaml_command input = - let really_use_ocamlfind = - match !use_ocamlfind, !packs with - | true, _ | _, _ :: _ -> true - | _ -> false in - let compiler = - if really_use_ocamlfind then - if !use_ocamlc then [!ocamlfind; "ocamlc"] - else [!ocamlfind; "ocamlopt"] - else if !use_ocamlc then [!ocamlc] - else [!ocamlopt] in - - let flags = !ocamlflags in - let camlp4_stuff = - if !use_camlp4 then - let syntax, camlp4 = - if !revised then "camlp4r", !camlp4r - else "camlp4o", !camlp4o in - let ppoptions = - if !ppopt = [] then [] - else - if really_use_ocamlfind then - List.flatten (List.map (fun s -> ["-ppopt"; s]) !ppopt) - else !ppopt in - if really_use_ocamlfind then - "-syntax" :: syntax :: ppoptions - else - let space = function | "" -> "" | s -> " " ^ s in - ["-pp"; sprintf "'%s%s'" camlp4 (space (String.concat " " ppoptions))] - else [] in - let packages = - if really_use_ocamlfind then - ["-linkpkg"; "-package"; - String.concat "," - (if !use_camlp4 && not (List.mem "camlp4" !packs) then - "camlp4" :: !packs - else !packs) ] - else [] in - - let extra_sources = !sources in - let init () = List.iter import extra_sources in - let all_sources = extra_sources @ [input] in - - let xcommands, all_ml_files = extra_commands all_sources in - - let args = compiler @ "-o" :: "prog" :: - flags @ camlp4_stuff @ packages @ all_ml_files in - (init, xcommands, command args, exe "prog") - - -let compile source result = - let internal_input = - if !use_ocamllex then "ocamlscript_main.mll" - else "ocamlscript_main.ml" in - - let before, xcommands, main_command, internal_output = - ocaml_command internal_input in - let input = [internal_input, source] in - let output = [internal_output, exe result] in - let log = if !Common.verbose then Some stdout else None in - run ?log ~before ~input ~output - { input = [internal_input]; - output = [internal_output]; - commands = xcommands @ [main_command] } diff --git a/ocaml.mli b/ocaml.mli deleted file mode 100644 index c907fb8..0000000 --- a/ocaml.mli +++ /dev/null @@ -1,70 +0,0 @@ -(** Default compilation module: settings for the Ocaml compilers *) - -(** the name of the camlp4o command; default: "camlp4o" *) -val camlp4o : string ref - -(** the name of the camlp4r command; default: "camlp4r" *) -val camlp4r : string ref - -(** the name of the ocamllex command; default: "ocamllex" *) -val ocamllex : string ref - -(** the name of the ocamlyacc command; default: "ocamlyacc" *) -val ocamlyacc : string ref - -(** the name of the ocamlc command; default: "ocamlc" *) -val ocamlc : string ref - -(** the name of the ocamlopt command; default: "ocamlopt" *) -val ocamlopt : string ref - -(** the name of the ocamlfind command; default: "ocamlfind" *) -val ocamlfind : string ref - -(** Specific Findlib/ocamlfind packages to use *) -val packs : string list ref - -(** Extra source files (processed with camlp4 but not ocamllex). - They can be referenced either by an absolute path or by a path relative - to the script directory. - They are compiled and linked in like regular OCaml compilation units. *) -val sources : string list ref - -(** whether to use ocamllex or not; default: false *) -val use_ocamllex : bool ref - -(** whether to use camlp4 preprocessing or not; default: true *) -val use_camlp4 : bool ref - -(** whether to use ocamlc instead of ocamlopt; default: false *) -val use_ocamlc : bool ref - -(** whether to use ocamlfind even if [!packs] is empty; default: false *) -val use_ocamlfind : bool ref - -(** whether the revised syntax of OCaml should be used; default: false *) -val revised : bool ref - -(** any other options to pass to the compiler *) -val ocamlflags : string list ref - -(** any other options to pass to the preprocessor *) -val ppopt : string list ref - -(** type of a preprocessor: takes an input file and returns a command and - the list of files to be processed further. *) -type pp = string -> (Pipeline.command * string list) - -(** optional preprocessor that comes before all other preprocessors *) -val pp : pp option ref - -(** the function which takes the source file path and generates the string - the will be inserted at the beginning of files when they are copied. - The default is set to follow OCaml's standard syntax, - like "#1 \"file.ml\";;\n" for instance. *) -val ppsrcloc : (string -> string) option ref - - -(** the compilation function which is used - to set [Ocamlscript.Common.compile] *) -val compile : string -> string -> int diff --git a/ocamlscript-help.txt b/ocamlscript-help.txt deleted file mode 100644 index 13cc46d..0000000 --- a/ocamlscript-help.txt +++ /dev/null @@ -1,72 +0,0 @@ -Usage: ocamlscript [ PACKED_OPTIONS [ OPTIONS ] [ -- ] [SCRIPTNAME] [ARGS] ] - -Ocamlscript normally reads the source code of a program from a file, looks -if a compiled executable exists for this program. If it exists and if it -is more recent than the source file, the executable is executed immediately, -otherwise it is updated by executing compilation instructions that can -be specified in the program file. - -A typical self-executable script looks as follows: - - #!/usr/bin/env ocamlscript - (* this is the compilation section, in OCaml *) - Ocaml.packs := ["unix"; "micmatch_pcre"] (* Findlib packages *) - -- - (* this is the program section *) - let _ = - ... - - -Structure of the command line: - -PACKED_OPTIONS: - the first argument of ocamlscript. It is either unpacked into - several arguments that are passed to ocamlscript or into a script name - if this name doesn't start with "-". Double-quotes can be used - to enclose arguments that contain whitespace or double-quotes. - Double-quotes must be doubled. For instance, the following - self-executable script would be compiled into an executable named - Hello "World": - #!/usr/bin/ocamlscript -o "Hello ""World""" - print_endline "Hello "World"" - - Important note: on some Unix systems, the whole - '-o "Hello ""World"""' string is passed as a single argument - to ocamlscript. This is why the first argument must be unpacked, - even if ocamlscript is called explicitely from the command line. - -OPTIONS: - any number of arguments in this section are treated like options - to ocamlscript until a either a non-option is encountered, which is - understood as the script name (SCRIPTNAME) or "--" which stops - the list of arguments that are passed to ocamlscript. - -Ocamlscript supports the following options: - -- marks the end of ocamlscript arguments - -help displays a help message and exit - --help same as -help - -c compile only - -o EXEC_NAME specify a name for the executable - (required if the program is not read from a file) - -e PROGRAM execute the code given here instead of reading it from a file - -f force recompilation which is otherwise based on last modification dates - -debug print messages about what ocamlscript is doing - -version prints the version identifier to stdout and exit - - read program from stdin instead of a file - -vm VIRTUAL_MACHINE run the executable using this virtual machine (e.g. - ocamlrun) - -"--": passed as an argument to ocamlscript in the PACKED_OPTIONS argument - or in the OPTIONS argument marks the end of the arguments that - are passed to ocamlscript. Arguments that follow will be - interpreted as arguments of the script. - Arguments that follow "--" in the PACKED_OPTIONS argument - will be passed as arguments to the final executable. The first - argument that follows "--" in the OPTIONS command line arguments - is treated as the script name, unless the program is read from - another source, as specified by options "-e" (a string) or "-" - (standard input). - - -For a full documentation on the structure of the compilation section, go to -ocamlscript's website (http://martin.jambon.free.fr/ocamlscript.html). diff --git a/opam b/opam deleted file mode 100644 index 5b2a5d8..0000000 --- a/opam +++ /dev/null @@ -1,20 +0,0 @@ -opam-version: "2.0" -synopsis: "Tool which compiles OCaml scripts into native code" -authors: "Martin Jambon" -maintainer: [ - "martin@mjambon.com" - "Francois Berenger " -] -homepage: "https://github.com/mjambon/ocamlscript" -bug-reports: "https://github.com/mjambon/ocamlscript/issues" -dev-repo: "git://github.com/mjambon/ocamlscript" -depends: [ - "ocaml" {>= "4.08"} # because of Fun.protect - "ocamlfind" -] -build: [make] -install: [make "install"] -# url { -# src: "https://github.com/mjambon/ocamlscript/archive/XXX" -# checksum: "md5=XXX" -# } diff --git a/pipeline.ml b/pipeline.ml deleted file mode 100644 index c3cdf37..0000000 --- a/pipeline.ml +++ /dev/null @@ -1,200 +0,0 @@ - -open Printf -open Unix - -(* - - input: list of files which are copied from the base directory - to a temporary directory - - output: list of files which are copied from the temporary directory - to the base directory - - the current directory is set to the temporary directory during - the execution of the pipeline -*) - -type command = { args : string list; - stdin : string option; - stdout : string option } - -type pipeline = { input : string list; - output : string list; - commands : command list } - -let ( // ) = Filename.concat -let ( @@ ) a b = if Filename.is_relative b then a // b else b - -let command ?stdin ?stdout args = { args = args; - stdin = stdin; - stdout = stdout } - -let prng = Random.State.make_self_init ();; - -let temporary_directory = - match Sys.os_type with - | "Unix" | "Cygwin" -> (try Sys.getenv "TMPDIR" with Not_found -> "/tmp") - | "Win32" -> (try Sys.getenv "TEMP" with Not_found -> ".") - | _ -> assert false - -let temp_file_name prefix suffix = - let rnd = (Random.State.bits prng) land 0xFFFFFF in - temporary_directory // (sprintf "%s%06x%s" prefix rnd suffix) - -let temp_dir prefix suffix = - let rec try_name counter = - let name = temp_file_name prefix suffix in - try - mkdir name 0o700; - name - with Unix_error _ as e -> - if counter >= 1000 then raise e else try_name (counter + 1) - in try_name 0 - -let stderr_out_chan = Unix.out_channel_of_descr stderr - -let maybe_log maybe_out fmt = - (match maybe_out with - | Some out -> fprintf out - | None -> ifprintf stderr_out_chan) fmt - -(* rm -rf *) -let rec remove ?log file = - try - let st = stat file in - match st.st_kind with - | S_DIR -> - Array.iter (fun name -> remove (file // name)) (Sys.readdir file); - maybe_log log "remove directory %S\n%!" file; - rmdir file - | S_REG - | S_CHR - | S_BLK - | S_LNK - | S_FIFO - | S_SOCK -> - maybe_log log "remove file %S\n%!" file; - Sys.remove file - with e -> () - - -(* like Sys.command, but without shell interpretation *) -let array_command ?stdin ?stdout prog args = - let real_stdin, close_stdin = - match stdin with - | None -> Unix.stdin, false - | Some file -> Unix.openfile file [Unix.O_RDONLY] 0, true in - let real_stdout, close_stdout = - match stdout with - | None -> Unix.stdout, false - | Some file -> - Unix.openfile file - [Unix.O_WRONLY; Unix.O_CREAT; Unix.O_TRUNC] 0o600, true in - let pid = Unix.create_process prog args real_stdin real_stdout Unix.stderr in - let pid', process_status = Unix.waitpid [] pid in - assert (pid = pid'); - if close_stdin then - (try Unix.close real_stdin with _ -> ()); - if close_stdout then - (try Unix.close real_stdout with _ -> ()); - match process_status with - | Unix.WEXITED n -> n - | Unix.WSIGNALED _ -> 2 (* like OCaml's uncaught exceptions *) - | Unix.WSTOPPED _ -> - (* only possible if the call was done using WUNTRACED - or when the child is being traced *) - assert false - -let concat_cmd cmd = String.concat " " cmd.args - -let run_command ?log cmd = - match cmd.args with - | [] -> - maybe_log log "empty command\n%!"; 0 - | prog :: _ -> - maybe_log log "%s: %s\n%!" prog (concat_cmd cmd); - let status = - array_command ?stdin:cmd.stdin ?stdout:cmd.stdout - prog (Array.of_list cmd.args) in - maybe_log log "exit status %i\n%!" status; - status - -let exec ?log cmd = - maybe_log log "%s\n%!" (concat_cmd cmd); - let status = run_command cmd in - maybe_log log "exit status %i\n%!" status; - status - -let copy_file ?log ?(head = "") ?(tail = "") ?(force = false) src dst = - maybe_log log "copy %S to %S\n%!" src dst; - if not force && Sys.file_exists dst then - invalid_arg - (sprintf "Pipeline.copy_file: destination file %s already exists" dst); - let ic = open_in_bin src in - Fun.protect (fun () -> - let oc = open_out_bin dst in - Fun.protect (fun () -> - try - output_string oc head; - while true do - output_char oc (input_char ic) - done - with End_of_file -> output_string oc tail - ) - ~finally:(fun () -> - close_out_noerr oc; - let perm = (stat src).st_perm in - chmod dst perm - ) - ) - ~finally:(fun () -> close_in_noerr ic) - -let copy_files ?log ?force src dst l = - List.iter - (fun (src_name, dst_name) -> - copy_file ?log ?force (src @@ src_name) (dst @@ dst_name)) - l - -let match_files names settings = - let tbl = Hashtbl.create 10 in - List.iter (fun id -> Hashtbl.replace tbl id None) names; - List.iter (fun (id, s) -> Hashtbl.replace tbl id (Some s)) settings; - let pairs = - Hashtbl.fold (fun id opt l -> - let x = - match opt with - | None -> (id, id) - | Some s -> (id, s) in - x :: l) tbl [] in - pairs - -let flip (a, b) = (b, a) - -let run ?log - ?(before = fun () -> ()) ?(after = fun () -> ()) - ?(input = []) ?(output = []) p = - let rec loop l = - match l with - | [] -> 0 - | cmd :: rest when cmd.stdin = None && cmd.stdout = None -> - (try - let status = exec ?log cmd in - if status = 0 then loop rest - else status - with _ -> 127) - | _ -> failwith "IO redirections: not implemented" in - let dir = temp_dir "ocamlpipeline" "" in - Fun.protect - (fun () -> - let base = Sys.getcwd () in - maybe_log log "change directory %S\n%!" dir; - Sys.chdir dir; - before (); - copy_files ?log base dir (List.map flip (match_files p.input input)); - let status = loop p.commands in - maybe_log log "change directory %S\n%!" base; - after (); - Sys.chdir base; - maybe_log log "command pipeline exits with status %i\n%!" status; - if status = 0 then - copy_files ?log ~force:true dir base (match_files p.output output); - status - ) - ~finally:(fun () -> remove ?log dir) diff --git a/pipeline.mli b/pipeline.mli deleted file mode 100644 index 337b308..0000000 --- a/pipeline.mli +++ /dev/null @@ -1,45 +0,0 @@ -(** Portable command pipeline *) - -type command - -(** A given pipeline always executes the same commands from a given temporary - directory. The input files have been copied into the directory by the time - the execution starts. After execution, the output files are copied - to some specified location and then the directory is removed. *) -type pipeline = { - input : string list; (** internal names of the input files *) - output : string list; (** internal names of the result files *) - commands : command list; (** commands which operate on the - internal file names *) -} - -(** [command ?stdin ?stdout l] creates a command which redirects stdin and - stdout to the given files, if these options are used. - For example, [command ~stdout:"bigfile" ["cat"; "file1"; "file2"]] - is equivalent to the shell command [cat file1 file2 > bigfile]. *) -val command : ?stdin:string -> ?stdout:string -> string list -> command - -(** [run ~input ~output pip] executes the given pipeline [pip] by - instanciating the internal file names used in the pipeline using - input and output files. - For example, a pipeline [pip] which works on one input file named file1 - and on one output file named file2 would be executed using - [run ~input:["file1", any_input_file] - ~output:["file2", any_output_file] pip]. - The [log] option can be used to collect information for debugging. *) -val run : - ?log:out_channel -> - ?before:(unit -> unit) -> - ?after:(unit -> unit) -> - ?input:(string * string) list -> - ?output:(string * string) list -> pipeline -> int - -(**/**) - -val remove : ?log:out_channel -> string -> unit -val copy_file : - ?log:out_channel -> ?head:string -> ?tail:string -> ?force:bool -> - string -> string -> unit -val run_command : ?log:out_channel -> command -> int - -val maybe_log : out_channel option -> ('a, out_channel, unit) format -> 'a diff --git a/std.ml b/std.ml deleted file mode 100644 index 9e9839a..0000000 --- a/std.ml +++ /dev/null @@ -1,5 +0,0 @@ -module Common = Common -module Pipeline = Pipeline -module Ocaml = Ocaml -module Utils = Utils -let (--) _ _ = () diff --git a/tests/-full test b/tests/-full test deleted file mode 100755 index f218322..0000000 --- a/tests/-full test +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env ocamlscript -(*-*- tuareg -*-*) -Ocaml.use_ocamllex := true;; -Ocaml.packs := ["camlp4.macro"];; -Ocaml.ocamlflags := ["-rectypes"; "unix.cmxa"];; --- -(* ocamllex file *) -rule line f n = parse - [^'\n']* as x { newline (f n x) n lexbuf } -and newline f n = parse - '\n' { line f (n+1) lexbuf } - | eof { () } - | "" { assert false } -{ -open Printf - -(* testing -rectypes *) -type t = t array - -(* testing program arguments *) -let args = Array.to_list Sys.argv -let _ = - assert (args <> []); - printf "Running %s\n%!" - (String.concat " " (List.map (fun s -> sprintf "%S" s) args)) - -(* running ocamllex stuff *) -let prog_name = List.hd args -let _ = - let ic = open_in_bin prog_name in - let lexbuf = Lexing.from_channel ic in - let rec f n s = printf "%i " n; f in - line f 0 lexbuf; - printf "\n" - -(* testing camlp4 extension *) -let _ = printf "This should be the source file: %S\n" __FILE__ - -(* testing library *) -let _ = Unix.gettimeofday () -} diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 4ee9b08..0000000 --- a/tests/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: default clean -default: - @echo '*** These tests are for developers only! ***' - @echo -e '*** They might not work on your platform! ***\n\n' - cp ../ocamlscript . - ./unpack - './Hello "World"' - ./fulltest arg1 arg2 - ./ocamlscript -- '-full test' arg1 arg2 arg3 - ./ocamlscript -c -o hello -e 'print_endline "Hello"' - ./hello - echo 'Printf.printf "Goodbye %s\n" Sys.argv.(1)' \ - | ocamlscript - -o goodbye '$(USER)' - ./goodbye again -clean: - $(RM) -- ocamlscript *~ \ - "-full test.exe" fulltest.exe 'Hello "World"' hello goodbye diff --git a/tests/fulltest b/tests/fulltest deleted file mode 100755 index e85c4e5..0000000 --- a/tests/fulltest +++ /dev/null @@ -1,46 +0,0 @@ -#!./ocamlscript str.cmxa -- hello -(*-*- tuareg -*-*) -open Ocaml;; -use_ocamllex := true;; -packs := ["camlp4.macro"];; -ocamlflags := !ocamlflags @ ["-rectypes"; "unix.cmxa"];; --- - -(* ocamllex file *) -rule line f n = parse - [^'\n']* as x { newline (f n x) n lexbuf } -and newline f n = parse - '\n' { line f (n+1) lexbuf } - | eof { () } - | "" { assert false } -{ -open Printf - -(* testing shebang option *) -let _ = Str.regexp "abc" - -(* testing -rectypes *) -type t = t array - -(* testing program arguments *) -let args = Array.to_list Sys.argv -let _ = - assert (args <> []); - printf "Running %s\n%!" - (String.concat " " (List.map (fun s -> sprintf "%S" s) args)) - -(* running ocamllex stuff *) -let prog_name = List.hd args -let _ = - let ic = open_in_bin prog_name in - let lexbuf = Lexing.from_channel ic in - let rec f n s = printf "%i " n; f in - line f 0 lexbuf; - printf "\n" - -(* testing camlp4 extension *) -let _ = printf "This should be the source file: %S\n" __FILE__ - -(* testing library *) -let _ = Unix.gettimeofday () -} diff --git a/tests/unpack b/tests/unpack deleted file mode 100755 index afa2e6e..0000000 --- a/tests/unpack +++ /dev/null @@ -1,4 +0,0 @@ -#!./ocamlscript -debug -o "Hello ""World""" -(* the line above only works on Unix flavors that pass all the options as - a single argument argv[1] *) -let _ = print_endline "Hello \"World\"" diff --git a/thin-ocamlscript.opam b/thin-ocamlscript.opam new file mode 100644 index 0000000..47c7f10 --- /dev/null +++ b/thin-ocamlscript.opam @@ -0,0 +1,33 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "OCaml as a scripting language with ocamlfind" +description: + "Similar to ocamlscript, but configuration is restricted to the shebang line." +maintainer: ["Julian Fondren "] +authors: ["Julian Fondren "] +license: "MIT" +homepage: "https://github.com/jrfondren/thin-ocamlscript" +doc: "https://github.com/jrfondren/thin-ocamlscript" +bug-reports: "https://github.com/jrfondren/thin-ocamlscript/issues" +depends: [ + "dune" {>= "3.20"} + "ocaml" {>= "5.2"} + "ocamlfind" {>= "1.9.8"} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/jrfondren/thin-ocamlscript.git" +x-maintenance-intent: ["(latest)"] diff --git a/utils.ml b/utils.ml deleted file mode 100644 index 63c698f..0000000 --- a/utils.ml +++ /dev/null @@ -1,4 +0,0 @@ -let ( // ) = Filename.concat -let ( !+ ) path = - if Filename.is_relative path then !Common.script_dir // path - else path diff --git a/utils.mli b/utils.mli deleted file mode 100644 index 019d63b..0000000 --- a/utils.mli +++ /dev/null @@ -1,8 +0,0 @@ -(** Handy utilities for file manipulation. - This module is automatically opened by ocamlscript. *) - -(** shortcut for portable file path concatenation *) -val ( // ) : string -> string -> string - -(** prefix operator to reference a file relatively to the script directory. *) -val ( !+ ) : string -> string From 1103908b3f8d79707fca4b7a999bdc146f3c2418 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:35:27 -0600 Subject: [PATCH 05/15] add README.md examples --- examples/deriving.ml | 3 +++ examples/now.ml | 2 ++ 2 files changed, 5 insertions(+) create mode 100755 examples/deriving.ml create mode 100755 examples/now.ml diff --git a/examples/deriving.ml b/examples/deriving.ml new file mode 100755 index 0000000..422d546 --- /dev/null +++ b/examples/deriving.ml @@ -0,0 +1,3 @@ +#! /usr/bin/env -S thin-ocamlscript -package ppx_deriving.show -linkpkg -- +type args = string array [@@deriving show] +let () = Format.printf "%a\n" pp_args Sys.argv diff --git a/examples/now.ml b/examples/now.ml new file mode 100755 index 0000000..3e69072 --- /dev/null +++ b/examples/now.ml @@ -0,0 +1,2 @@ +#! /usr/bin/env -S thin-ocamlscript -package unix -linkpkg -- +let () = Printf.printf "%.0f\n" (Unix.time ()) From e606949eae311fbb6f3827a02a07673ace57ccc7 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:36:51 -0600 Subject: [PATCH 06/15] add .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe31a3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.exe +/_build/ +thin-ocamlscript.install From 54ae03a685a5bbf8955423d8020e9eb8de31e0e2 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:37:40 -0600 Subject: [PATCH 07/15] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed21b13..80433e5 100644 --- a/README.md +++ b/README.md @@ -97,5 +97,5 @@ To not use hidden files for the executable is advised to help differentiate it from malware. I preferred to avoid /tmp for compilation for some subtle reasons, including -that sensitive scripts would not accidentally scape filesystem security on the +that sensitive scripts would not accidentally escape filesystem security on the script, think /root/bin/ping-secret-api.ml From 3c2056d439e9a7ec73fc64ceb0b7843512c4835b Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:42:13 -0600 Subject: [PATCH 08/15] fmt --- examples/deriving.ml | 2 ++ examples/now.ml | 1 + 2 files changed, 3 insertions(+) mode change 100755 => 100644 examples/deriving.ml mode change 100755 => 100644 examples/now.ml diff --git a/examples/deriving.ml b/examples/deriving.ml old mode 100755 new mode 100644 index 422d546..35c9269 --- a/examples/deriving.ml +++ b/examples/deriving.ml @@ -1,3 +1,5 @@ #! /usr/bin/env -S thin-ocamlscript -package ppx_deriving.show -linkpkg -- + type args = string array [@@deriving show] + let () = Format.printf "%a\n" pp_args Sys.argv diff --git a/examples/now.ml b/examples/now.ml old mode 100755 new mode 100644 index 3e69072..52e91d2 --- a/examples/now.ml +++ b/examples/now.ml @@ -1,2 +1,3 @@ #! /usr/bin/env -S thin-ocamlscript -package unix -linkpkg -- + let () = Printf.printf "%.0f\n" (Unix.time ()) From bac5533b8eccef90f898908f2d272cd7b70ec434 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:42:36 -0600 Subject: [PATCH 09/15] example without .ml --- examples/exists | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 examples/exists diff --git a/examples/exists b/examples/exists new file mode 100755 index 0000000..7d7a4cf --- /dev/null +++ b/examples/exists @@ -0,0 +1,12 @@ +#! /usr/bin/env -S thin-ocamlscript -package unix -linkpkg -- + +let () = + if Array.length Sys.argv <> 2 then ( + Printf.printf "usage: %s \n" Sys.argv.(0); + exit 1); + let file = Sys.argv.(1) in + try + Unix.access file [Unix.R_OK]; + print_endline ("file exists: " ^ file) + with Unix.Unix_error (Unix.ENOENT, _, _) -> + print_endline ("file doesn't exist: " ^ file) From b433944cc769b814760d4203e8f2098fa56a2e0b Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Sat, 20 Dec 2025 19:43:08 -0600 Subject: [PATCH 10/15] dune fmt removed +x --- examples/deriving.ml | 0 examples/now.ml | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 examples/deriving.ml mode change 100644 => 100755 examples/now.ml diff --git a/examples/deriving.ml b/examples/deriving.ml old mode 100644 new mode 100755 diff --git a/examples/now.ml b/examples/now.ml old mode 100644 new mode 100755 From deedfdb7b821739cfd702bdce41891e2603057ca Mon Sep 17 00:00:00 2001 From: jrfondren <41455523+jrfondren@users.noreply.github.com> Date: Sun, 21 Dec 2025 01:46:36 -0600 Subject: [PATCH 11/15] note WIP --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 80433e5..676b71a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +EDIT: actually, I'm going to support legacy ocamlscript instead of breaking completely with it. The thin-ocamlscript variant below that relies on ocamlfind will remain the default behavior. + thin-ocamlscript lets you use OCaml as a scripting language while only paying the compilation cost once per modification of a script, and while getting dependencies and preprossing through ocamlfind. From bb74234f29073d13c64b02a3e69acc39f83d4190 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Fri, 26 Dec 2025 14:35:03 -0600 Subject: [PATCH 12/15] add merlin note --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 80433e5..510baac 100644 --- a/README.md +++ b/README.md @@ -99,3 +99,26 @@ from malware. I preferred to avoid /tmp for compilation for some subtle reasons, including that sensitive scripts would not accidentally escape filesystem security on the script, think /root/bin/ping-secret-api.ml + +# editing with Merlin + +This generated code, which calls :MerlinUse on every package in +first line, improves the editing experience a great deal: + +``` +autocmd BufReadPost *.ml call SetupMerlinPackages() + +function! SetupMerlinPackages() + let first_line = getline(1) + let matches = matchlist(first_line, '-package \(\w\+\)') + let start = 0 + while 1 + let m = matchstr(first_line, '-package \zs\w\+', start) + if m == '' + break + endif + execute ':MerlinUse ' . m + let start = matchend(first_line, '-package \w\+', start) + endwhile +endfunction +``` From 38e7880ec2f6e1496cac99daa18fcd67bf9ff166 Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Thu, 1 Jan 2026 17:32:38 -0600 Subject: [PATCH 13/15] settle with thin-ocamlscript and only the ocamlfind mode --- README.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5fa64bb..c005700 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -EDIT: actually, I'm going to support legacy ocamlscript instead of breaking completely with it. The thin-ocamlscript variant below that relies on ocamlfind will remain the default behavior. - thin-ocamlscript lets you use OCaml as a scripting language while only paying the compilation cost once per modification of a script, and while getting dependencies and preprossing through ocamlfind. @@ -14,15 +12,17 @@ For altered behavior (e.g., tmp files in /tmp, Unix and Ezcurl loaded by default, the -vm or -debug flags from ocamlscript), the expectation is that you'll use a fork. +For a shorter name like "ocamlscript", you can rename the binary. + # installing ``` -$ opam install thin-ocamlscript # with opam +$ opam pin add thin-ocamlscript https://github.com/jrfondren/thin-ocamlscript.git -$ dune install --release # with dune +$ dune install --release -$ dune build --release # dune, manual install -$ mv _build/default/bin/main.exe ~/bin/thin-ocamlscript +$ dune build --release +$ mv _build/default/bin/main.exe ~/bin/ocamlscript $ cat lib/script.ml > oscript.ml # manual, single-file $ sed 1d bin/main.ml >> oscript.ml # skip 'open' @@ -30,7 +30,7 @@ $ vi oscript.ml # make it print "recompiling! ..." $ ocamlfind ocamlopt -O3 -package unix -linkpkg -o oscript oscript.ml ``` -# very brief examples +# examples ```ocaml #! /usr/bin/env -S thin-ocamlscript -package unix -linkpkg -- @@ -80,12 +80,6 @@ The exename is compiled if not newer than the script. The tmpname is deleted after compilation. There is no protection against you doing something placing a hello.ml script next to a hello.tmp.ml that you would regret losing. -# defects - -1. Editors do not understand that the shebang line affects the environment of the script, and will e.g. complain about an "Unbound module Unix" in the first example. For this reason I think a 'dune-ocamlscript' (ocamlscript-dune?) that unpacks/repacks a script from an ephemeral dune repo would make for a generally more pleasant experience. - -2. The name's too long. It should probably also be 'ocamlscript-thin' but to me that suggests a relationship with ocamlscript. - # design A temporary file is required by ocamlc/ocamlopt objecting to #! lines. @@ -104,7 +98,7 @@ script, think /root/bin/ping-secret-api.ml # editing with Merlin -This generated code, which calls :MerlinUse on every package in +This generated vimscript, which calls :MerlinUse on every package in first line, improves the editing experience a great deal: ``` From 03de1ded89ed3eb5b987e85b46a58e3ebfa5e0bc Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Thu, 1 Jan 2026 17:37:35 -0600 Subject: [PATCH 14/15] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c005700..a124b01 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ thin-ocamlscript lets you use OCaml as a scripting language while only paying the compilation cost once per modification of a script, and while getting -dependencies and preprossing through ocamlfind. +dependencies and preprocessing through ocamlfind. thin-ocamlscript is very similar to ocamlscript, but From 8678cd717b50b387ffcfa6c307f1b1a01f79df4b Mon Sep 17 00:00:00 2001 From: Julian Fondren Date: Thu, 1 Jan 2026 17:42:42 -0600 Subject: [PATCH 15/15] clean up: installing --- README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a124b01..38e2bfc 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,28 @@ For a shorter name like "ocamlscript", you can rename the binary. # installing +with opam: +``` +opam pin add thin-ocamlscript https://github.com/jrfondren/thin-ocamlscript.git ``` -$ opam pin add thin-ocamlscript https://github.com/jrfondren/thin-ocamlscript.git -$ dune install --release +cloning this repo and using dune: +``` +git clone https://github.com/jrfondren/thin-ocamlscript && cd thin-ocamlscript +dune install --release +``` -$ dune build --release -$ mv _build/default/bin/main.exe ~/bin/ocamlscript +using dune and manually placing the binary: +``` +dune build --release +mv _build/default/bin/main.exe ~/bin/ocamlscript +``` -$ cat lib/script.ml > oscript.ml # manual, single-file -$ sed 1d bin/main.ml >> oscript.ml # skip 'open' -$ vi oscript.ml # make it print "recompiling! ..." -$ ocamlfind ocamlopt -O3 -package unix -linkpkg -o oscript oscript.ml +catting a single .ml together and building that with ocamlfind: +``` +cat lib/script.ml > oscript.ml +sed 1d bin/main.ml >> oscript.ml +ocamlfind ocamlopt -O3 -package unix -linkpkg -o oscript oscript.ml ``` # examples