(*
* 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