######################################################################## # Building OCaml programs. # # Copyright (C) 2003-2007 Jason Hickey and Mojave Group # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this file, to deal in the File without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the File, and to permit persons to whom the File # is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the File. # # THE FILE 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 AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # FILE OR THE USE OR OTHER DEALINGS IN THE FILE. open build/Common open configure/Configure ######################################################################## # OCaml section # # \begin{doc} # \section{Building OCaml code} # # \OMake{} provides extensive support for building OCaml code, including support for tools like # \verb+ocamlfind+, \verb+ocamlyacc+ and \verb+menhir+. In order to use the functions # defined in this section, you need to make sure the line # \begin{verbatim} # open build/OCaml # \end{verbatim} # is present in your \verb+OMakeroot+ file. # # \subsection{Autoconfiguration variables for OCaml compilation} # These variables will get defined based on the ``autoconf-style'' tests executed when you # run \OMake{} for the first time. You can use them to configure your project accordingly, # and you should not redefine them. # # You can use the \verb+--configure+ command line option (Section~\ref{option:--configure}) to force # re-execution of all the tests. # # \varlabel{OCAMLOPT_EXISTS}{OCAMLOPT\_EXISTS} True when \verb+ocamlopt+ (or \verb+ocamlopt.opt+) is # available on your machine. # \varlabel{OCAMLFIND_EXISTS}{OCAMLFIND\_EXISTS} True when the ocamlfind is available on your # machines. # \varlabel{OCAMLDEP_MODULES_AVAILABLE}{OCAMLDEP\_MODULES\_AVAILABLE} True when a version of # \verb+ocamldep+ that understands the \verb+-modules+ option is available on your machine. # \varlabel{MENHIR_AVAILABLE}{MENHIR\_AVAILABLE} True when the Menhir parser-generator is available # on your machine. # # \subsection{Configuration variables for OCaml compilation} # # The following variables can be redefined in your project. # \varlabel{USE_OCAMLFIND}{USE\_OCAMLFIND} Whether to use the \verb+ocamlfind+ utility (default \verb+false+) # \var{OCAMLC} The OCaml bytecode compiler (default \verb+ocamlc.opt+ if it exists # and \verb+USE_OCAMLFIND+ is not set, otherwise \verb+ocamlc+). # \var{OCAMLOPT} The OCaml native-code compiler (default \verb+ocamlopt.opt+ if it # exists and \verb+USE_OCAMLFIND+ is not set, otherwise \verb+ocamlopt+). # \var{CAMLP4} The \verb+camlp4+ preprocessor (default \verb+camlp4+). # \var{OCAMLLEX} The OCaml lexer generator (default \verb+ocamllex+). # \var{OCAMLLEXFLAGS} The flags to pass to \verb+ocamllex+ (default \verb+-q+). # \var{OCAMLYACC} The OCaml parser generator (default \verb+ocamlyacc+). # \var{OCAMLYACCFLAGS} Additional options to pass to \verb+$(OCAMLYACC)+. # \var{OCAMLDEP} The OCaml dependency analyzer (default \verb+ocamldep+). # \varlabel{OCAMLDEP_MODULES}{OCAMLDEP\_MODULES} The OCaml dependency analyzer that understands the # \verb+-module+ option (default \verb+ocamldep+, if \verb+ocamldep -modules+ works, or # \verb+ocamlrun ocamldep-omake+, if \verb+ocamlrun ocamldep-omake -modules+ works, and empty # when neither works). # \varlabel{OCAMLDEP_MODULES_ENABLED}{OCAMLDEP\_MODULES\_ENABLED} Instead of using \verb+OCAMLDEP+ # in a traditional \verb+make+-style fashion, run \verb+$(OCAMLDEP_MODULES) -modules+ and then # postprocess the output internally to discover all the relevant generated \verb+.ml+ and # \verb+.mli+ files. See Section~\ref{section:ocaml-generated-files} for more information on # interactions between \OMake, \verb+OCAMLDEP+ and generated files. This feature is currently # considered highly experimental and is disabled by default. # \var{OCAMLMKTOP} The OCaml toploop compiler (default \verb+ocamlmktop+). # \var{OCAMLLINK} The OCaml bytecode linker (default \verb+$(OCAMLC)+). # \var{OCAMLOPTLINK} The OCaml native-code linker (default \verb+$(OCAMLOPT)+). # \var{OCAMLINCLUDES} Search path to pass to the OCaml compilers (default \verb+.+). # The search path with the \verb+-I+ prefix is defined by the \verb+PREFIXED_OCAMLINCLUDES+ # variable. # \var{OCAMLFIND} The \verb+ocamlfind+ utility (default \verb+ocamlfind+ if # \verb+USE_OCAMLFIND+ is set, otherwise empty). # \var{OCAMLFINDFLAGS} The flags to pass to \verb+ocamlfind+ (default empty, \verb+USE_OCAMLFIND+ must be set). # \var{OCAMLPACKS} Package names to pass to \verb+ocamlfind+ (\verb+USE_OCAMLFIND+ must be set). # \varlabel{BYTE_ENABLED}{BYTE\_ENABLED} Flag indicating whether to use the bytecode compiler (default \verb+true+, when no \verb+ocamlopt+ found, \verb+false+ otherwise). # \varlabel{NATIVE_ENABLED}{NATIVE\_ENABLED} Flag indicating whether to use the native-code compiler (default \verb+true+, when ocamlopt is found, \verb+false+ otherwise). # Both \verb+BYTE_ENABLED+ and \verb+NATIVE_ENABLED+ can be set to true; # at least one should be set to true. # \varlabel{MENHIR_ENABLED}{MENHIR\_ENABLED} Define this as \verb+true+ if you wish to use # \verb+menhir+ instead of \verb+ocamlyacc+ (default \verb+false+). # \end{doc} # static. = OCAMLFIND_EXISTS = $(CheckProg ocamlfind) OCAMLC_OPT_EXISTS = $(CheckProg ocamlc.opt) OCAMLC_EXISTS = $(or $(OCAMLC_OPT_EXISTS), $(CheckProg ocamlc)) OCAMLOPT_OPT_EXISTS = $(CheckProg ocamlopt.opt) OCAMLOPT_EXISTS = $(or $(OCAMLOPT_OPT_EXISTS), $(CheckProg ocamlopt)) ConfMsgChecking(whether ocamlc understands the "z" warnings) OCAML_ACCEPTS_Z_WARNING = if $(OCAMLC_EXISTS) value $(ConfMsgYesNo $(shell-success-null ocamlc$(if $(OCAMLC_OPT_EXISTS), .opt) -w Az)) else ConfMsgResult($"FAILED - ocamlc not found") value false public.USE_OCAMLFIND = false public.OCAMLFIND = $`(if $(USE_OCAMLFIND), ocamlfind) public.OCAMLFINDFLAGS = public.LAZY_OCAMLFINDFLAGS = $`(if $(USE_OCAMLFIND), $(OCAMLFINDFLAGS)) public.OCAMLC = $(if $(OCAMLC_OPT_EXISTS), $`(if $(USE_OCAMLFIND), ocamlc, ocamlc.opt), ocamlc) public.OCAMLOPT = $(if $(OCAMLOPT_OPT_EXISTS), $`(if $(USE_OCAMLFIND), ocamlopt, ocamlopt.opt), ocamlopt) public.OCAMLDEP = ocamldep public.CAMLP4 = camlp4 public.OCAMLLEX = ocamllex public.OCAMLLEXFLAGS = -q public.OCAMLYACC = ocamlyacc public.OCAMLYACCFLAGS = public.OCAMLMKTOP = ocamlmktop public.OCAMLLINK = $`(OCAMLC) public.OCAMLOPTLINK = $`(OCAMLOPT) # # Include path # public.OCAMLINCLUDES[] = . public.PREFIXED_OCAMLINCLUDES = $`(mapprefix -I, $(OCAMLINCLUDES)) # # Packages # public.OCAMLPACKS[] = public.PREFIXED_OCAMLPACKS =\ $`(if $(and $(USE_OCAMLFIND) $(gt $(length $(OCAMLPACKS)), 0)),\ -package $(string $(concat \,, $(OCAMLPACKS))),\ $(EMPTY)) # # Compile native or byte code? # public.NATIVE_ENABLED = $(OCAMLOPT_EXISTS) public.BYTE_ENABLED = $(not $(OCAMLOPT_EXISTS)) # # Various options # # \begin{doc} # \subsection{OCaml command flags} # # The following variables specify \emph{additional} options to be passed to # the OCaml tools. # \var{OCAMLDEPFLAGS} Flags to pass to \verb+OCAMLDEP+ (but not to \verb+OCAMLDEP_MODULES+). # \var{OCAMLPPFLAGS} Flags to pass to \verb+CAMLP4+. # \var{OCAMLCFLAGS} Flags to pass to the byte-code compiler (default \verb+-g+). # \var{OCAMLOPTFLAGS} Flags to pass to the native-code compiler (default empty). # \var{OCAMLFLAGS} Flags to pass to either compiler (default \verb+-warn-error A+). # \varlabel{OCAML_BYTE_LINK_FLAGS}{OCAML\_BYTE\_LINK\_FLAGS} Flags to pass to the byte-code linker (default empty). # \varlabel{OCAML_NATIVE_LINK_FLAGS}{OCAML\_NATIVE\_LINK\_FLAGS} Flags to pass to the native-code linker (default empty). # \varlabel{OCAML_LINK_FLAGS}{OCAML\_LINK\_FLAGS} Flags to pass to either linker. # \varlabel{MENHIR_FLAGS}{MENHIR\_FLAGS} Additional flags to pass to \verb+menhir+. # \end{doc} # public.OCAMLDEPFLAGS = $`(if $(NATIVE_ENABLED), -native, $(EMPTY)) public.OCAMLPPFLAGS = public.OCAMLFLAGS = -warn-error A public.OCAMLCFLAGS = -g public.OCAMLOPTFLAGS = public.OCAMLCPPFLAGS = public.OCAML_LINK_FLAGS = $`(if $(and $(USE_OCAMLFIND) $(gt $(length $(OCAMLPACKS)), 0)), -linkpkg, $(EMPTY)) public.OCAML_BYTE_LINK_FLAGS = -custom public.OCAML_NATIVE_LINK_FLAGS = # # OCAML_LIBS contains libraries that are used as dependencies # OCAML_OTHER_LIBS contains other libraries (like unix.cma) # The lists do not include suffixes. # # OCAML_LINK_FLAGS contains extra linking information # # \begin{doc} # \subsection{Library variables} # # The following variables are used during linking. # # \varlabel{OCAML_LIBS}{OCAML\_LIBS} Libraries to pass to the linker. These libraries become dependencies # of the link step. # \varlabel{OCAML_OTHER_LIBS}{OCAML\_OTHER\_LIBS} Additional libraries to pass to the linker. These libraries are # \emph{not} included as dependencies to the link step. Typical use is for the OCaml # standard libraries like \verb+unix+ or \verb+str+. # \varlabel{OCAML_CLIBS}{OCAML\_CLIBS} C libraries to pass to the linker. # \varlabel{OCAML_LIB_FLAGS}{OCAML\_LIB\_FLAGS} Extra flags for the library linker. # \varlabel{ABORT_ON_DEPENDENCY_ERRORS}{ABORT\_ON\_DEPENDENCY\_ERRORS} # OCaml linker requires the OCaml files to be # listed in dependency order. Normally, all the functions presented in this section will automatically sort # the list of OCaml modules passed in as the \verb++ argument. However, this variable is # set to \verb+true+, the order of the files passed into these function will be left as is, but \OMake{} will # abort with an error message if the order is illegal. # # \end{doc} # public.OCAML_LIBS = public.OCAML_CLIBS = public.OCAML_OTHER_LIBS = public.OCAML_LIB_FLAGS = ######################################################################## # \begin{doc} # \subsection{Generated OCaml Files} # \label{section:ocaml-generated-files} # As of OCaml version 3.09.2, the standard \verb+ocamldep+ scanner is ``broken''. The main issue is # that it finds only those dependencies that already exist. If \verb+foo.ml+ contains a dependency # on \verb+Bar+, # \begin{verbatim} # foo.ml: # open Bar # \end{verbatim} # then the default \verb+ocamldep+ will only find the dependency if a file \verb+bar.ml+ or # \verb+bar.ml+ exists in the include path. It will not find (or print) the dependency if, for # example, only \verb+bar.mly+ exists at the time \verb+ocamldep+ is run, even though \verb+bar.ml+ # and \verb+bar.mli+ can be generated from \verb+bar.mly+. # # \OMake{} currently provides two methods for addressing this problem --- one that requires manually # specifying the generated files, and an experimental method for discovering such ``hidden'' # dependencies automatically. The # \hypervarx{OCAMLDEP_MODULES_ENABLED}{OCAMLDEP\_MODULES\_ENABLED} controls which method is # going to be used. When this variable is false, the manual specifications are expected and when it # is true, the automated discovery will be attempted. # # \twofuns{OCamlGeneratedFiles}{LocalOCamlGeneratedFiles} # \begin{verbatim} # OCamlGeneratedFiles(files) # LocalOCamlGeneratedFiles(files) # \end{verbatim} # # When the \hypervarx{OCAMLDEP_MODULES_ENABLED}{OCAMLDEP\_MODULES\_ENABLED} variable is set # to \verb+false+, the \verb+OCamlGeneratedFiles+ and \verb+LocalOCamlGeneratedFiles+ functions specify files # that need to be generated before any OCaml files are scanned for dependencies. For example, # if \verb+parser.ml+ and \verb+lexer.ml+ are both generated files, specify: # \begin{verbatim} # OCamlGeneratedFiles(parser.ml lexer.ml) # \end{verbatim} # # The \verb+OCamlGeneratedFiles+ function is \emph{global} --- its arguments will be generated # before any OCaml files anywhere in the project are scanned for dependencies. The # \verb+LocalOCamlGeneratedFiles+ function follows the normal scoping rules of OMake. # # These functions have no effect when the # \hypervarx{OCAMLDEP_MODULES_ENABLED}{OCAMLDEP\_MODULES\_ENABLED} is true. # # \subsubsection{Automatic discovery of generated files during dependency analysis} # Having to specify the generated files manualy when \OMake{} could discover them automatically is # obviously suboptimal. To address this, we try to use a custom \verb+ocamldep+ that \emph{only} # finds the free module names in a file. # # This functionality is experimental and is disabled by default for now. # Set the \hypervarx{OCAMLDEP_MODULES_ENABLED}{OCAMLDEP\_MODULES\_ENABLED} to \verb+true+ (or # to \verb+$(OCAMLDEP_MODULES_AVAILABLE)+) in your project to enable it. # # Note that the experimental \verb+ocamldep+ functionality this relies upon is not yet included in # the standard OCaml (it is expected to be a part of the upcoming OCaml 3.10 --- # see \url{http://caml.inria.fr/mantis/view.php?id=4047}). Temporarily, we # distribute a bytecode version \index{ocamldep-omake}\verb+ocamldep-omake+ of the appropriately # modified \verb+ocamldep+. The appropriate \verb+ocamldep+ will be discovered automatically --- see # and the \hypervarxn{OCAMLDEP_MODULES_AVAILABLE}{OCAMLDEP\_MODULES\_AVAILABLE} and # \hypervarxn{OCAMLDEP_MODULES}{OCAMLDEP\_MODULES} variables will be set accordingly. # \end{doc} # static. = OCAMLDEP_MODULES = ConfMsgChecking(if ocamldep understands -modules) OCAMLDEP_MODULES_AVAILABLE = $(ConfMsgYesNo $(shell-success-null ocamldep -modules)) if $(OCAMLDEP_MODULES_AVAILABLE) OCAMLDEP_MODULES = ocamldep export else ConfMsgChecking(for ocamldep-omake bytecode file) OCAMLDEP_OMAKE = $(find-in-path-optional $(PATH), ocamldep-omake) if $(OCAMLDEP_OMAKE) OCAMLDEP_OMAKE = $(file $(OCAMLDEP_OMAKE)) ConfMsgResult(found $(absname $(OCAMLDEP_OMAKE))) ConfMsgChecking(if ocamldep-omake runs) OCAMLDEP_MODULES_AVAILABLE = $(ConfMsgYesNo $(shell-success-null ocamlrun $(OCAMLDEP_OMAKE) -modules)) OCAMLDEP_MODULES = $(if $(OCAMLDEP_MODULES_AVAILABLE), ocamlrun $(OCAMLDEP_OMAKE)) export else ConfMsgResult(NOT FOUND) export OCAMLDEP_MODULES_AVAILABLE OCAMLDEP_MODULES public.OCAMLDEP_MODULES_ENABLED = false # public.OCAMLDEP_MODULES_ENABLED = $(OCAMLDEP_MODULES_AVAILABLE) .PHONY: OCamlGeneratedFilesTarget public.OCamlGeneratedFiles(files) = if $(OCAMLDEP_MODULES_ENABLED) # For now, we want to allow ``backwards-compatible'' projects. # eprintln($"WARNING: OCamlGeneratedFiles should not be used when OCAMLDEP_MODULES_ENABLED") # eprintln($" is set") else OCamlGeneratedFilesTarget: $(files) public.LocalOCamlGeneratedFiles(files) = if $(OCAMLDEP_MODULES_ENABLED) # For now, we want to allow ``backwards-compatible'' projects. # eprintln($"WARNING: OCamlGeneratedFiles should not be used when OCAMLDEP_MODULES_ENABLED") # eprintln($" is set") else .SCANNER: scan-ocaml-%: $(files) .SCANNER: %.cmi: $(files) .SCANNER: %.cmx %.cmo: $(files) export export # # The ocamldep -modules output has the following # form, where the indented lines are the free module names in foo.ml. # # foo.ml: # Bar # ... # # From this, we generate proper dependencies by finding the files # that can be built, using the find-targets-in-path-optional # function. # # # Print the dependencies for a ML file, based on the # .cmi files. # # If OCAMLDEP_PRESERVE_TARGETS is true, then the # ocamldep entries are taken literally (the suffix # is not replaced with .cmo/.cmx). # public.OCAMLDEP_PRESERVE_TARGETS = false public.PrintMLIDependencies(filename, cmideps) = if $(cmideps) private.base = $(string-escaped $(removesuffix $(filename))) println($"""$(base).cmi: $(string-escaped $(cmideps))""") public.PrintMLDependencies(filename, cmideps, cmxdeps) = private.base = $(string-escaped $(removesuffix $(filename))) private.esc = $' \' private.text = if $(cmideps) cmideps = $(string-escaped $(cmideps)) private.text = $""" $(base).cmo: $(cmideps) $(base).cmx $(base)$(EXT_OBJ):$(esc) $(cmideps)""" export text if $(cmxdeps) private.odeps = $(string-escaped $(addsuffix $(EXT_OBJ), $(removesuffix $(cmxdeps)))) if $(not $(text)) text = $"""$(base).cmx $(base)$(EXT_OBJ):""" export text += $"""$(esc) $(string-escaped $(cmxdeps))$(esc) $(odeps)""" export text # eprintln($(text)) println($(text)) public.PrintFileDependencies(filename, cmideps) = if $(cmideps) private.text = $"""$(string-escaped $(filename)): $(string-escaped $(cmideps))""" # eprintln($(text)) println($(text)) # # Given a set of literal dependencies, compute # the actual dependencies by finding the filenames # associated with each module. # public.PrintDependencies(filename, modules) = if $(filename) # # The module Foo can come from both foo.mli and Foo.mli # private.all_modules = $(set $(uncapitalize $(modules)) $(modules)) # # Find the .cmi files that can be built # private.cmideps = $(addsuffix .cmi, $(all_modules)) cmideps = $(set $(find-targets-in-path-optional $(OCAMLINCLUDES), $(cmideps))) # Now produce the dependencies if $(OCAMLDEP_PRESERVE_TARGETS) PrintFileDependencies($(filename), $(cmideps)) else switch($(suffix $(filename))) case .ml private.cmxdeps[] = if $(NATIVE_ENABLED) cmxdeps = $(addsuffix .cmx, $(all_modules)) cmxdeps = $(set $(find-targets-in-path-optional $(OCAMLINCLUDES), $(cmxdeps))) export PrintMLDependencies($(filename), $(cmideps), $(cmxdeps)) case .mli PrintMLIDependencies($(filename), $(cmideps)) default eprintln($"ocaml scanner: illegal filename $(filename)") exit(1) # # Post-process the output of ocamldep. # Use awk to process the input, find the targets that # exist, and then print the dependencies. # public.OCamlScannerPostproc() = # # Read the module names from the standard input # protected.filename = protected.modules[] = awk(b, $(stdin)) case $'^\(.*\):$' PrintDependencies($(filename), $(modules)) filename = $1 modules[] = export case $'^ \(.*\)' # Add the dependency modules[] += $1 export default eprintln(Unrecognized ocamldep output: $0) PrintDependencies($(filename), $(modules)) Shell. += ocamldep-postproc(argv) = if $(mem -preserve-targets, $(argv)) OCAMLDEP_PRESERVE_TARGETS = true export OCamlScannerPostproc() public.OCamlScanner(src_file) = if $(OCAMLDEP_MODULES_ENABLED) value $(OCAMLDEP_MODULES) -modules $(src_file) | ocamldep-postproc else value $(OCAMLFIND) $(OCAMLDEP) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLDEPFLAGS) $(PREFIXED_OCAMLINCLUDES) $(src_file) ######################################################################## # Generic build rules. # # The order of the %.cmi rules is important. # The most recent definition is used first, if it applies. # 1. The .cmi is generated from the .mli, if it exists # 2. Otherwise it is generated from the .ml # # In case 2, make sure to use the same command text that is used for # generating the .cmo or .cmx file. This will prevent the compiler # from being called twice: once to generate the .cmi file, and again # for the .cmo or .cmx file. # public.OCamlC() = value $(OCAMLFIND) $(OCAMLC) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS)\ $(OCAMLCFLAGS) $(OCAMLPPFLAGS) $(PREFIXED_OCAMLINCLUDES) public.OCamlOpt() = value $(OCAMLFIND) $(OCAMLOPT) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS)\ $(OCAMLOPTFLAGS) $(OCAMLPPFLAGS) $(PREFIXED_OCAMLINCLUDES) %.cmx: %.ml section rule if $(not $(NATIVE_ENABLED)) err. = extends $(UnbuildableException) message = $(string $"You are trying to build OCaml native code file: "%.cmx$" However, the NATIVE_ENABLED flag is not set. Include the following definition in your OMakefile if you really want to build this file. NATIVE_ENABLED = true") target = $(file %.cmx) raise $(err) elseif $(target-exists %.mli) %.cmx %$(EXT_OBJ): %.ml %.cmi :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $< elseif $(BYTE_ENABLED) %.cmx %.cmi %$(EXT_OBJ) %.cmo: %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< $(OCamlOpt) -c $< else %.cmx %.cmi %$(EXT_OBJ): %.ml :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $< %$(EXT_OBJ): %.ml section rule if $(not $(NATIVE_ENABLED)) err. = extends $(UnbuildableException) message = $(string $"You are trying to build OCaml native code file: "%$(EXT_OBJ)$" However, the NATIVE_ENABLED flag is not set. Include the following definition in your OMakefile if you really want to build this file. NATIVE_ENABLED = true") target = $(file %.cmx) raise $(err) elseif $(target-exists %.mli) %$(EXT_OBJ) %.cmx: %.ml %.cmi :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $< elseif $(BYTE_ENABLED) %$(EXT_OBJ) %.cmi %.cmx %.cmo: %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< $(OCamlOpt) -c $< else %$(EXT_OBJ) %.cmi %.cmx: %.ml :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $< %.cmo: %.ml section rule if $(not $(BYTE_ENABLED)) err. = extends $(UnbuildableException) message = $(string $"You are trying to build OCaml native code file: "%.cmo$" However, the BYTE_ENABLED flag is not set. Include the following definition in your OMakefile if you really want to build this file. BYTE_ENABLED = true") target = $(file %.cmx) raise $(err) elseif $(target-exists %.mli) %.cmo: %.ml %.cmi :scanner: scan-ocaml-%.ml $(OCamlC) -c $< elseif $(NATIVE_ENABLED) %.cmo %.cmi %.cmx %$(EXT_OBJ): %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< $(OCamlOpt) -c $< else %.cmo %.cmi: %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< %.cmi: %.ml section rule if $(BYTE_ENABLED) if $(NATIVE_ENABLED) %.cmi %.cmo %.cmx %$(EXT_OBJ): %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< $(OCamlOpt) -c $< else %.cmi %.cmo: %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< else %.cmi %.cmx %$(EXT_OBJ): %.ml :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $< %.cmi: %.mli :scanner: scan-ocaml-%.mli $(OCamlC) -c $< ######################################################################## # Parser generators # # # You can choose to use ocamlyacc or menhir for a parser # generator. The default is ocamlyacc. Define the # MENHIR_ENABLED as true if you would rather use menhir. # # Variables: # MENHIR : the name of the menhir executable # MENHIR_FLAGS : any additional options to pass to Menhir # MENHIR_AVAILABLE : the menhir executable is installed # MENHIR_RAW_DEPEND : menhir supports the --raw-depend option # public.MENHIR = menhir public.MENHIR_FLAGS = public.MENHIR_ENABLED = false static. = MENHIR_AVAILABLE = $(CheckProg $(MENHIR)) MENHIR_RAW_DEPEND = false if $(MENHIR_AVAILABLE) ConfMsgChecking(if $(MENHIR) supports the --raw-depend option) MENHIR_RAW_DEPEND = $(ConfMsgYesNo $(shell-success-null $(MENHIR) -help | grep $'^ *--raw-depend')) export # Menhir is being requested. Check that it is installed. public.MenhirCheck() = if $(not $(MENHIR_AVAILABLE)) eprintln($"""!!! You are asking to use Menhir, but it is not installed.""") eprintln($"""!!! See the Menhir home page for instructions on downloading.""") eprintln($"""!!! http://cristal.inria.fr/~fpottier/menhir/""") exit(1) # Compute the correct ocamlc and ocamldep options for Menhir. public.MenhirOCamlcCommand() = MenhirCheck() private.ocamlc[] =\ $(OCAMLFIND) $(OCAMLC) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS)\ $(OCAMLCFLAGS) $(OCAMLPPFLAGS) $(PREFIXED_OCAMLINCLUDES) value --ocamlc $"$(ocamlc)" public.MenhirOCamldepCommand(raw) = ocamldep = if $(raw) value $(OCAMLDEP) $(PREFIXED_OCAMLINCLUDES) $(OCAMLDEPFLAGS) -modules else value $(OCAMLDEP) $(PREFIXED_OCAMLINCLUDES) $(OCAMLDEPFLAGS) value --ocamldep $"$(ocamldep)" # Compute the Mendir dependency scanner command public.MenhirScannerCommand(base, src) = if $(MENHIR_ENABLED) if $(and $(MENHIR_RAW_DEPEND), $(OCAMLDEP_MODULES_ENABLED)) value $(MENHIR) $(MENHIR_FLAGS) $(MenhirOCamldepCommand true) $(base) --raw-depend $(src) | ocamldep-postproc -preserve-targets else value $(MENHIR) $(MENHIR_FLAGS) $(MenhirOCamldepCommand false) $(base) --depend $(src) # Use Menhir if MENHIR_ENABLED, ocamlyacc otherwise public.OCamlYaccCommand(src) = if $(MENHIR_ENABLED) value $(MENHIR) $(MENHIR_FLAGS) $(MenhirOCamlcCommand) $(src) else value $(OCAMLYACC) $(OCAMLYACCFLAGS) $(src) .SCANNER: scan-ocamlyacc-%.mly: %.mly :value: $(MENHIR_ENABLED) $(OCamlScannerTargets $&) $(MenhirScannerCommand $(EMPTY), $<) %.ml %.mli: %.mly :scanner: scan-ocamlyacc-%.mly $(OCamlYaccCommand $<) # # \begin{doc} # \subsection{Using the Menhir parser generator} # # Menhir is a parser generator that is mostly compatible with # \verb+ocamlyacc+, but with many improvements. A few of these # are listed here (excerpted from the Menhir home page # \url{http://cristal.inria.fr/~fpottier/menhir/}). # # \begin{itemize} # \item Menhir's explanations are believed to be understandable by mere humans. # \item Menhir allows grammar specifications to be split over multiple files. # It also allows several grammars to share a single set of tokens. # \item Menhir is able to produce parsers that are parameterized by Objective Caml modules. # \item [Added by jyh] With the \verb+--infer+ option, Menhir can typecheck the semantic actions # in your grammar at \emph{generation} time. # \end{itemize} # # What do you need to do to use Menhir instead of \verb+ocamlyacc+? # \begin{enumerate} # \item Place the following definition before the relevant section of your project # (or at the top of your project \verb+OMakefile+ if you want to use Menhir everywhere). # # \begin{verbatim} # MENHIR_ENABLED = true # \end{verbatim} # # \item Optionally, add any desired Menhir options to the \verb+MENHIR_FLAGS+ variable. # # \begin{verbatim} # MENHIR_FLAGS += --infer # \end{verbatim} # \end{enumerate} # # With this setup, any file with a \verb+.mly+ suffix will be compiled with Menhir. # # If your grammar is split across several files, you need to specify it explicitly, # using the \verb+MenhirMulti+ function. # # \begin{verbatim} # MenhirMulti(target, sources) # target : filename, without suffix # sources : the files that define the grammar, without suffixes # \end{verbatim} # # For example, if you want to generate the parser files \verb+parse.ml+ and \verb+parse.mli+, # from the grammar specified in files \verb+a.mly+ and \verb+b.mly+, you would use # the following. # # \begin{verbatim} # MenhirMulti(parse, a b) # \end{verbatim} # \end{doc} # public.MenhirMulti(target, sources) = MenhirCheck() private.sources = $(addsuffix .mly, $(sources)) # Menhir needs all the files for dependency analysis .SCANNER: scan-menhir-$(target).multi: $(sources) :value: $(OCamlScannerTargets $&) $(MenhirScannerCommand --base $(target), $+) # Set up the actual rule $(target).ml $(target).mli: $(sources) :scanner: scan-menhir-$(target).multi $(MENHIR) $(MENHIR_FLAGS) $(MenhirOCamlcCommand) --base $(target) $+ ######################################################################## # Other common generated files # %.ml %.mli: %.mlz ln-or-cp $< $*.ml ln-or-cp $< $*.mli %.ml: %.mll $(OCAMLLEX) $(OCAMLLEXFLAGS) $< %.ml: %.mlp %.h @rm -f $@ @echo "(* CAUTION: this is a generated file. If you edit it, all changes will be lost! *)" > $@ $(CPP) $(OCAMLCPPFLAGS) -imacros $*.h $*.mlp >> $@ @chmod 444 $@ # # Generic scanners # OCamlScannerTargets(files) = files[] = $(basename $(files)) files[] = $(if $(NATIVE_ENABLED), $(files), $(filter-out %.cmx, $(files))) files[] = $(if $(BYTE_ENABLED), $(files), $(filter-out %.cmo, $(files))) value $(find-targets-in-path-optional $(OCAMLINCLUDES), $(files)) $(NATIVE_ENABLED) $(BYTE_ENABLED) .SCANNER: scan-ocaml-%.mli: %.mli /.PHONY/OCamlGeneratedFilesTarget :value: $(OCamlScannerTargets $&) $(OCamlScanner $<) .SCANNER: scan-ocaml-%.ml: %.ml /.PHONY/OCamlGeneratedFilesTarget :exists: %.mli :value: $(OCamlScannerTargets $&) $(OCamlScanner $<) # # Default .SCANNER rules for backwards-compatibility. # .SCANNER: %.cmi: %.mli /.PHONY/OCamlGeneratedFilesTarget :value: $(OCamlScannerTargets $&) $(OCamlScanner $<) .SCANNER: %.cmx %.cmo %$(EXT_OBJ): %.ml /.PHONY/OCamlGeneratedFilesTarget :exists: %.mli :value: $(OCamlScannerTargets $&) $(OCamlScanner $<) # # Define a link order for OCaml files. # If a file depends on a %.cmi, it also depends on %.cmo # .ORDER: .OCAMLLINK .OCAMLLINK: %.cmi: %.cmo .OCAMLLINK: %.cmx: %.cmo public.ABORT_ON_DEPENDENCY_ERRORS = false OCamlLinkSort(nodes) = if $(ABORT_ON_DEPENDENCY_ERRORS) value $(file-check-sort .OCAMLLINK, $(nodes)) else value $(file-sort .OCAMLLINK, $(nodes)) # # Generic rule to build an ML library # # \begin{doc} # \fun{OCamlLibrary} # # The \verb+OCamlLibrary+ function builds an OCaml library. # # \verb+OCamlLibrary(, )+ # # The \verb++ and \verb++ are listed \emph{without} suffixes. # # This function returns the list of all the targets that it defines the rules # for (including the \verb+$(name)$(EXT_LIB)+ file when \verb+NATIVE_ENABLED+ is set). # # The following code builds the \verb+libfoo.cmxa+ library from the files \verb+foo.cmx+ # and \verb+bar.cmx+ (if \verb+NATIVE_ENABLED+ is set), and \verb+libfoo.cma+ from # \verb+foo.cmo+ and \verb+bar.cmo+ (if \verb+BYTE_ENABLED+ is set). # # \begin{verbatim} # OCamlLibrary(libfoo, foo bar) # \end{verbatim} # \end{doc} # public.OCamlLibrary(name, files) = # XXX: JYH: these variables should be marked private in 0.9.9 protected.name = $(file $(name)) protected.OFILES = $(addsuffix $(EXT_OBJ), $(files)) protected.CMOFILES = $(addsuffix .cmo, $(files)) protected.CMXFILES = $(addsuffix .cmx, $(files)) protected.CLIB = $(file $(name)$(EXT_LIB)) protected.BYTELIB = $(file $(name).cma) protected.NATIVELIB = $(file $(name).cmxa) # # Link commands # $(BYTELIB): $(CMOFILES) $(OCAMLFIND) $(OCAMLLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) $(OCAMLCFLAGS) \ $(OCAML_LIB_FLAGS) -a -o $@ $(OCamlLinkSort $(CMOFILES)) $(NATIVELIB) $(CLIB): $(CMXFILES) $(OFILES) $(OCAMLFIND) $(OCAMLOPTLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) $(OCAMLOPTFLAGS) \ $(OCAML_LIB_FLAGS) -a -o $(NATIVELIB) $(OCamlLinkSort $(CMXFILES)) return $(array $(if $(NATIVE_ENABLED), $(NATIVELIB)), $(if $(NATIVE_ENABLED), $(CLIB)), $(if $(BYTE_ENABLED), $(BYTELIB))) # # Generic rule to build an ML library # # \begin{doc} # \fun{OCamlPackage} # # The \verb+OCamlPackage+ function builds an OCaml package. # # \verb+OCamlPackage(, )+ # # The \verb++ and \verb++ are listed \emph{without} suffixes. # The \verb++ must have been compiled with the \verb+-for-pack + # flag to the OCaml compiler. # # This function returns the list of all the targets that it defines the rules # for (including the \verb+$(name)$(EXT_LIB)+ file when \verb+NATIVE_ENABLED+ is set). # # The following code builds the \verb+libfoo.cmx+ package from the files \verb+package.cmx+ # and \verb+bar.cmx+ (if \verb+NATIVE_ENABLED+ is set), and \verb+package.cmo+ from # \verb+foo.cmo+ and \verb+bar.cmo+ (if \verb+BYTE_ENABLED+ is set). # # \begin{verbatim} # OCamlPackage(package, foo bar) # \end{verbatim} # \end{doc} # public.OCamlPackage(name, files) = # XXX: JYH: these variables should be marked private in 0.9.9 protected.OFILES = $(addsuffix $(EXT_OBJ), $(files)) protected.CMOFILES = $(addsuffix .cmo, $(files)) protected.CMXFILES = $(addsuffix .cmx, $(files)) protected.OBJ = $(file $(name)$(EXT_OBJ)) protected.CMO = $(file $(name).cmo) protected.CMX = $(file $(name).cmx) protected.CMI = $(file $(name).cmi) protected.BYTE_TARGETS = $(CMO) protected.NATIVE_TARGETS = $(CMX) $(OBJ) # # Choose how to generate the .cmi # protected.TARGETS = if $(NATIVE_ENABLED) NATIVE_TARGETS += $(CMI) TARGETS = $(NATIVE_TARGETS) export else BYTE_TARGETS += $(CMI) TARGETS = $(BYTE_TARGETS) export # # Link commands # $(BYTE_TARGETS): $(CMOFILES) $(OCAMLFIND) $(OCAMLC) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) $(OCAMLCFLAGS) \ $(OCAML_LIB_FLAGS) -pack -o $(CMO) $(OCamlLinkSort $(CMOFILES)) $(NATIVE_TARGETS): $(CMXFILES) $(OFILES) $(OCAMLFIND) $(OCAMLOPTLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) $(OCAMLOPTFLAGS) \ $(OCAML_LIB_FLAGS) -pack -o $(CMX) $(OCamlLinkSort $(CMXFILES)) return $(TARGETS) # # If the interfaces are to be installed, # define this variable to be true. # public.INSTALL_INTERFACES = false # # Install the library # # \begin{doc} # \fun{OCamlLibraryCopy} # # The \verb+OCamlLibraryCopy+ function copies a library to an install location. # # \verb+OCamlLibraryCopy(, , , )+ # # The \verb++ specify additional interface files # to be copied if the \verb+INSTALL_INTERFACES+ variable is true. # \end{doc} # public.OCamlLibraryCopy(tag, lib, name, ifiles) = # # Copy interfaces # if $(INSTALL_INTERFACES) private.MLIFILES = $(filter-targets $(addsuffix .mli, $(ifiles))) private.CMIFILES = $(addsuffix .cmi, $(ifiles)) foreach(src, $(MLIFILES) $(CMIFILES)) $(lib)/$(basename $(src)): $(src) $(lib) :scanner: $(NOSCANNER) ln-or-cp $< $@ # Add to the install tag $(tag): $(file $(addprefix $(lib)/, $(basename $(MLIFILES) $(CMIFILES)))) # # Also install libraries # private.CLIB = $(file $(name)$(EXT_LIB)) private.BYTELIB = $(file $(name).cma) private.NATIVELIB = $(file $(name).cmxa) private.LIBCLIB = $(file $(lib)/$(name)$(EXT_LIB)) private.LIBBYTE = $(file $(lib)/$(name).cma) private.LIBNATIVE = $(file $(lib)/$(name).cmxa) # # Link libraries into lib directory # $(LIBBYTE): $(BYTELIB) ln-or-cp $< $@ $(LIBNATIVE): $(NATIVELIB) ln-or-cp $< $@ $(LIBCLIB): $(CLIB) ln-or-cp $< $@ # # Add dependencies to the target tag # public.FILES[] = if $(BYTE_ENABLED) $(tag): $(LIBBYTE) FILES[] += $(LIBBYTE) export if $(NATIVE_ENABLED) $(tag): $(LIBNATIVE) $(LIBCLIB) FILES[] += $(LIBNATIVE) $(LIBCLIB) export # # We often use them together # # \begin{doc} # \fun{OCamlLibraryInstall} # # The \verb+OCamlLibraryInstall+ function builds a library # and copies it to an install location in one step. # # \verb+OCamlLibraryInstall(, , , )+ # \end{doc} # public.OCamlLibraryInstall(tag, lib, name, files) = OCamlLibrary($(name), $(files)) return $(OCamlLibraryCopy $(tag), $(lib), $(name), $(files)) # # Generic rule to build an OCaml program # name: the name of the target, without a suffix # files: names of the object files, without suffixes # # Other variables: # OCAML_LIBS: OCaml libraries target depends on, without suffix # OCAML_CLIBS: C libraries we depend on, without suffix # OCAML_OTHER_LIBS: OCaml libraries, without dependencies, without suffix # OCAML_BYTE_LINK_FLAGS: additional flags for byte compiler # OCAML_NATIVE_LINK_FLAGS: additional flags for native-code compiler # OCAML_LINK_FLAGS: general additional options (usually the -cclib options) # # \begin{doc} # \fun{OCamlProgram} # # The \verb+OCamlProgram+ function builds an OCaml program. It returns the array with all # the targets for which it has defined the rules (\verb+$(name)$(EXE)+ and \verb+$(name).run+ # and/or \verb+$(name).opt+, depending on the \verb+NATIVE_ENABLED+ and \verb+BYTE_ENABLED+ # variables). # # \verb+OCamlProgram(, )+ # # Additional variables used: # \begin{description} # \item[\hypervarxn{OCAML_LIBS}{OCAML\_LIBS}] Additional libraries passed to the linker, without suffix. These files # become dependencies of the target program. # \item[\hypervarxn{OCAML_OTHER_LIBS}{OCAML\_OTHER\_LIBS}] Additional libraries passed to the linker, without suffix. These # files do \emph{not} become dependencies of the target program. # \item[\hypervarxn{OCAML_CLIBS}{OCAML\_CLIBS}] C libraries to pass to the linker. # \item[\hypervarxn{OCAML_BYTE_LINK_FLAGS}{OCAML\_BYTE\_LINK\_FLAGS}] Flags to pass to the bytecode linker. # \item[\hypervarxn{OCAML_NATIVE_LINK_FLAGS}{OCAML\_NATIVE\_LINK\_FLAGS}] Flags to pass to the native code linker. # \item[\hypervarxn{OCAML_LINK_FLAGS}{OCAML\_LINK\_FLAGS}] Flags to pass to both linkers. # \end{description} # \end{doc} # public.OCamlProgram(name, files) = # XXX: JYH: these variables should be marked private in 0.9.9 protected.CMOFILES = $(addsuffix .cmo, $(files)) protected.CMXFILES = $(addsuffix .cmx, $(files)) protected.OFILES = $(addsuffix $(EXT_OBJ), $(files)) protected.CMAFILES = $(addsuffix .cma, $(OCAML_LIBS)) protected.CMXAFILES = $(addsuffix .cmxa, $(OCAML_LIBS)) protected.ARFILES = $(addsuffix $(EXT_LIB), $(OCAML_LIBS)) protected.CMA_OTHER_FILES = $(addsuffix .cma, $(OCAML_OTHER_LIBS)) protected.CMXA_OTHER_FILES = $(addsuffix .cmxa, $(OCAML_OTHER_LIBS)) protected.CLIBS = $(addsuffix $(EXT_LIB), $(OCAML_CLIBS)) protected.name = $(file $(name)) protected.PROG = $(file $(name)$(EXE)) protected.BYTEPROG = $(file $(name).run) protected.OPTPROG = $(file $(name).opt) # # Rules to build byte-code and native targets # $(BYTEPROG): $(CMAFILES) $(CMOFILES) $(CLIBS) $(OCAMLFIND) $(OCAMLLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) $(OCAMLCFLAGS)\ $(PREFIXED_OCAMLINCLUDES) $(OCAML_BYTE_LINK_FLAGS)\ -o $@ $(CMA_OTHER_FILES) $(CMAFILES) $(OCamlLinkSort $(CMOFILES))\ $(CLIBS) $(OCAML_LINK_FLAGS) $(OPTPROG): $(CMXAFILES) $(ARFILES) $(CMXFILES) $(OFILES) $(CLIBS) $(OCAMLFIND) $(OCAMLOPTLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) $(OCAMLOPTFLAGS)\ $(PREFIXED_OCAMLINCLUDES) $(OCAML_NATIVE_LINK_FLAGS)\ -o $@ $(CMXA_OTHER_FILES) $(CMXAFILES) $(OCamlLinkSort $(CMXFILES))\ $(CLIBS) $(OCAML_LINK_FLAGS) # # Link the actual executables. # Always prefer native executables. # if $(NATIVE_ENABLED) $(PROG): $(OPTPROG) ln-or-cp $< $@ else $(PROG): $(BYTEPROG) ln-or-cp $< $@ return $(array $(PROG), $(if $(NATIVE_ENABLED), $(OPTPROG)), $(if $(BYTE_ENABLED), $(BYTEPROG))) # # Copy to $(BIN) directory # # \begin{doc} # \fun{OCamlProgramCopy} # # The \verb+OCamlProgramCopy+ function copies an OCaml program to an install location. # # \verb+OCamlProgramCopy(, , )+ # # Additional variables used: # \begin{description} # \item[NATIVE\_ENABLED] If the \hypervarx{NATIVE_ENABLED}{NATIVE\_ENABLED} is set, the native-code executable # is copied; otherwise the byte-code executable is copied. # \end{description} # \end{doc} # public.OCamlProgramCopy(tag, bin, name) = private.name = $(file $(name)) private.BYTEPROG = $(file $(name).run) private.OPTPROG = $(file $(name).opt) private.SRCNAME = $(if $(NATIVE_ENABLED), $(OPTPROG), $(BYTEPROG)) private.BINNAME = $(file $(bin)/$(basename $(name))$(EXE)) # # Link the actual executables. # Always prefer native executables. # $(BINNAME): $(SRCNAME) $(bin) ln-or-cp $< $@ # Add to phony tag. $(tag): $(BINNAME) return $(BINNAME) # # We often use them together # # \begin{doc} # \fun{OCamlProgramInstall} # # The \verb+OCamlProgramInstall+ function builds a programs and copies it to # an install location in one step. # # \verb+OCamlProgramInstall(, , , )+ # \end{doc} # public.OCamlProgramInstall(tag, bin, name, files) = OCamlProgram($(name), $(files)) return $(OCamlProgramCopy $(tag), $(bin), $(name)) # vim:tw=100:fo=tcq: