(* * Some builtin functions. * * \begin{doc} * \chapter{Build functions and utilities} * \label{chapter:build} * \cutname{omake-build.html} * \end{doc} * *) include Omake_pos.Make (struct let name = "Omake_builtin_rule" end) (* * These targets are decribed in doc/src/omake-rules.tex * * \begin{doc} * \section{Builtin .PHONY targets} * * The complete set of builtin \verb+.PHONY+ targets include the following. * * \begin{description} * \item[.PHONY] Declares new phony targets (Section~\ref{target:.PHONY}). * \item[.DEFAULT] Declare the default build targets (Section~\ref{target:.DEFAULT}). * \item[.SUBDIRS] Include a directory as part of the project (Section~\ref{target:.SUBDIRS}). * \item[.SCANNER] Define a dependency scanner (Section~\ref{target:.SUBDIRS}). * \item[.INCLUDE] Include a file (Section~\ref{target:.INCLUDE}). * \item[.ORDER] Define a file-dependency ordering rule (Section~\ref{target:.ORDER}). * \item[.BUILD\_BEGIN] Commands to be executed at the beginning of a build. * \item[.BUILD\_SUCCESS] Commands to be executed if the build is successful. * \item[.BUILD\_FAILURE] Commands to be executed if the build fails. * \end{description} * * \targetlabelref{.BUILD_BEGIN}{.BUILD\_BEGIN} * \targetlabelref{.BUILD_SUCCESS}{.BUILD\_SUCCESS} * \targetlabelref{.BUILD_FAILURE}{.BUILD\_FAILURE} * * The \verb+.BUILD+ targets can be used to specify commands to be executed at * the beginning and end of the build. The \verb+.BUILD_BEGIN+ target is built * at the beginning of a project build, and one of \verb+.BUILD_FAILURE+ or * \verb+.BUILD_SUCCESS+ is executed when the build terminates. * * For example, the following set of rules simply print additional messages * about the status of the build. * * \begin{verbatim} * .BUILD_BEGIN: * echo Build starting * * .BUILD_SUCCESS: * echo The build was successful * * .BUILD_FAILURE: * println($"The build failed: $(length $(find-build-targets Failed)) targets could not be built") * \end{verbatim} * * Another common use is to define notifications to be performed when * the build completes. For example, the following rule will create * a new X terminal displaying the summary of the build * (using the \hypervarx{BUILD_SUMMARY}{BUILD\_SUMMARY}). * * \begin{verbatim} * .BUILD_FAILURE: * xterm -e vi $(BUILD_SUMMARY) * \end{verbatim} * * If you do not wish to add these rules directly to your project (which * is probably a good idea if you work with others), you can * define them in your \verb+.omakerc+ (see Section~\ref{section:.omakerc}). * * The \hyperfun{find-build-targets} * is useful for obtaining a firther summary of the build. Note that * when output diversions are in effect (with the \verb+--output-*+ options --- see Chapter~\ref{chapter:options}), * any output produced by the commands is copied to a file. The name of the * file is specified by the \verb+output-file+ field of the \hyperobj{Target}. * You may find this useful in defining custom build summaries. * \end{doc} *) let phony_targets = [".PHONY"; ".DEFAULT"; ".SUBDIRS"; ".SCANNER"; ".INCLUDE"; ".ORDER"; ".BUILD_BEGIN"; ".BUILD_SUCCESS"; ".BUILD_FAILURE"] (************************************************************************ * Set options. * * \begin{doc} * \section{Options and versioning} * \fun{OMakeFlags} * * \begin{verbatim} * OMakeFlags(options) * options : String * \end{verbatim} * * The \verb+OMakeFlags+ function is used to set \verb+omake+ options from * within \File{OMakefile}s. The options have exactly the same format as * options on the command line. * * For example, the following code displays the progress bar unless * the \verb+VERBOSE+ environment variable is defined. * * \begin{verbatim} * if $(not $(defined-env VERBOSE)) * OMakeFlags(-S --progress) * export * \end{verbatim} * \end{doc} *) let set_options venv pos loc args _ = let pos = string_pos "OMakeFlags" pos in match args with [arg] -> let argv = Omake_value.strings_of_value venv pos arg in let venv = Omake_env.venv_set_options venv loc pos argv in venv, Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * Version checking. * * \begin{doc} * \fun{OMakeVersion} * * \begin{verbatim} * OMakeVersion(version1) * OMakeVersion(version1, version2) * version1, version2 : String * \end{verbatim} * * The \verb+OMakeVersion+ function is used for version checking * in \File{OMakefile}s. It takes one or two arguments. * * In the one argument form, if the \Prog{omake} version number * is less than \verb+<version1>+, * then an exception is raised. In the two argument form, * the version must lie between \verb+version1+ and \verb+version2+. * * \fun{cmp-versions} * \begin{verbatim} * $(cmp-versions version1, version2) * version1, version2 : String * \end{verbatim} * * The \verb+cmp-versions\+ functions can be used to compare arbitrary version strings. * It returns 0 when the two version strings are equal, a negative number when the first * string represents an earlier version, and a positive number otherwise. * \end{doc} *) let split_int = let rec split_int_aux i s = match String.length s with 0 -> i, s | l -> begin match s.[0] with '0'..'9' as c -> split_int_aux (i * 10 + (Char.code c - 48)) (String.sub s 1 (l - 1)) | _ -> i, s end in split_int_aux 0 let rec compare_versions v1 v2 = match String.length v1, String.length v2 with 0, 0 -> 0 | 0, _ -> -1 | _, 0 -> 1 | l1, l2 -> begin match v1.[0],v2.[0] with '0'..'9', '0'..'9' -> let i1, s1 = split_int v1 in let i2, s2 = split_int v2 in begin match i1 - i2 with 0 -> compare_versions s1 s2 | i -> i end | c1, c2 when c1 = c2 -> compare_versions (String.sub v1 1 (l1 - 1)) (String.sub v2 1 (l2 - 1)) | c1, c2 -> Char.code c1 - Char.code c2 end let check_version venv pos loc args = let pos = string_pos "check_version" pos in let version = Omake_magic.version in let check lowest highest = if compare_versions version lowest < 0 then raise (Omake_value_type.OmakeFatalErr (loc_pos loc pos, LazyError (fun out -> Format.fprintf out "@[<0>This version of OMake is too old,@ you need to upgrade to at least version@ %s;@ current OMake version is@ %s.@ You should be able to download the latest version of OMake from http://omake.metaprl.org/download.html@]" lowest version))); match highest with Some highest -> if compare_versions version highest > 0 then raise (Omake_value_type.OmakeFatalErr (loc_pos loc pos, LazyError (fun out -> Format.fprintf out "@[<0>This version of OMake is too new or the given file is too old.@ This file accepts versions@ %s-%s;@ current OMake version is@ %s@]" lowest highest version))) | None -> () in match args with [lowest] -> let lowest = Lm_string_util.trim (Omake_value.string_of_value venv pos lowest) in check lowest None; Omake_value_type.ValString version | [lowest; highest] -> let lowest = Lm_string_util.trim (Omake_value.string_of_value venv pos lowest) in let highest = Lm_string_util.trim (Omake_value.string_of_value venv pos highest) in check lowest (Some highest); ValString version | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (1,2), List.length args))) let cmp_version venv pos loc args = let pos = string_pos "cmp_version" pos in match args with [v1; v2] -> let v1 = Lm_string_util.trim (Omake_value.string_of_value venv pos v1) in let v2 = Lm_string_util.trim (Omake_value.string_of_value venv pos v2) in Omake_value_type.ValInt (compare_versions v1 v2) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * Add the command-line vars. * * \begin{doc} * \fun{DefineCommandVars} * * \begin{verbatim} * DefineCommandVars() * \end{verbatim} * * The \verb+DefineCommandVars+ function redefines the variables passed on * the commandline. Variables definitions are passed on the command line * in the form \verb+name=value+. This function is primarily for internal * use by \Prog{omake} to define these variables for the first time. * \end{doc} *) let define_command_vars venv pos loc args kargs = let pos = string_pos "DefineCommandVars" pos in match args, kargs with [], [] | [_], [] -> Omake_builtin.venv_add_command_defs venv, Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (0, 1), List.length args))) (* * Table of built-in functions. *) let () = let builtin_funs = [true, "OMakeVersion", check_version, Omake_ir.ArityRange (1, 2); true, "cmp-versions", cmp_version, ArityExact 2; ] in let builtin_kfuns = [true, "OMakeFlags", set_options, Omake_ir.ArityExact 1; true, "DefineCommandVars", define_command_vars, ArityRange (0, 1); ] in let builtin_rules = [true, [".PHONY"], phony_targets] in let builtin_info = { Omake_builtin_type.builtin_empty with builtin_funs = builtin_funs; builtin_kfuns = builtin_kfuns; builtin_rules = builtin_rules; phony_targets = phony_targets } in Omake_builtin.register_builtin builtin_info