Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enabled_if #819

Merged
merged 2 commits into from
Jul 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ next
now allowed in toplevel position in the workspace file, or for individual
contexts. This feature requires `(dune lang 1.1)` (#1038, @rgrinberg)

- Add ``enabled_if`` field for aliases and tests. This field controls whether
the test will be ran using a boolean expression language. (#819, @rgrinberg)

1.0.1 (19/07/2018)
------------------

Expand Down
34 changes: 34 additions & 0 deletions doc/dune-files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,10 @@ The syntax is as follows:
- ``(locks (<lock-names>))`` specify that the action must be run while
holding the following locks. See the `Locks`_ section for more details.

- ``(enabled_if <blang expression>)`` specifies the boolean condition that must
be true for the tests to run. The condition is specified using the blang_, and
the field allows for variables_ to appear in the expressions.

The typical use of the ``alias`` stanza is to define tests:

.. code:: scheme
Expand Down Expand Up @@ -824,6 +828,36 @@ doesn't start by `-`, you can simply quote it: ``("x" y z)``.
Most fields using the ordered set language also support `Variables expansion`_.
Variables are expanded after the set language is interpreted.

.. _blang:

Boolean Language
----------------

The boolean language allows the user to define simple boolean expressions that
dune can evaluate. Here's a semi formal specification of the language:

.. code::

op := '=' | '<' | '>' | '<>' | '>=' | '<='

expr := (and <expr>+)
| (or <expr>+)
| (<op> <template> <template>)
| <template>

After an expression is evaluated, it must be exactly the string ``true`` or
``false`` to be considered as a boolean. Any other value will be treated as an
error.

Here's a simple example of a condition that expresses running on OSX and having
an flambda compiler with the help of variable expansion:

.. code:: scheme

(and %{ocamlc-config:flambda} (= %{ocamlc-config:system} macosx))

.. _variables:

Variables expansion
-------------------

Expand Down
45 changes: 45 additions & 0 deletions src/blang.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
open! Stdune

module Op = struct
type t =
| Eq
| Gt
| Gte
| Lte
| Lt
| Neq

let eval t (x : Ordering.t) =
match t, x with
| (Eq | Gte | Lte) , Eq
| (Lt | Lte) , Lt
| (Gt | Gte) , Gt -> true
| _, _ -> false
end

type 'a t =
| Expr of 'a
| And of 'a t list
| Or of 'a t list
| Compare of Op.t * 'a * 'a

type 'a expander =
{ f : 'value. mode:'value String_with_vars.Mode.t
-> 'a
-> Loc.t * 'value
}

let rec eval_bool t ~dir ~(f : 'a expander) =
match t with
| Expr a ->
begin match f.f ~mode:Single a with
| _, String "true" -> true
| _, String "false" -> false
| loc, _ -> Loc.fail loc "This value must be either true or false"
end
| And xs -> List.for_all ~f:(eval_bool ~f ~dir) xs
| Or xs -> List.exists ~f:(eval_bool ~f ~dir) xs
| Compare (op, x, y) ->
let ((_, x), (_, y)) = (f.f ~mode:Many x, f.f ~mode:Many y) in
Value.L.compare_vals ~dir x y
|> Op.eval op
25 changes: 25 additions & 0 deletions src/blang.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
open Stdune

module Op : sig
type t =
| Eq
| Gt
| Gte
| Lte
| Lt
| Neq
end

type 'a t =
| Expr of 'a
| And of 'a t list
| Or of 'a t list
| Compare of Op.t * 'a * 'a

type 'a expander =
{ f : 'value. mode:'value String_with_vars.Mode.t
-> 'a
-> Loc.t * 'value
}

val eval_bool : 'a t -> dir:Path.t -> f:'a expander -> bool
1 change: 1 addition & 0 deletions src/gen_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ module Gen(P : Install_rules.Params) = struct
; package = t.package
; deps = t.deps
; action = None
; enabled_if = t.enabled_if
} in
match test_kind (loc, s) with
| `Regular ->
Expand Down
73 changes: 59 additions & 14 deletions src/jbuild.ml
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ module Dep_conf = struct
; "universe" , return Universe
; "files_recursively_in",
(let%map () =
Syntax.renamed_in Stanza.syntax (1, 0) ~to_:"source_tree"
Syntax.renamed_in Stanza.syntax (1, 0) ~to_:"source_tree"
and x = sw in
Source_tree x)
; "source_tree",
Expand Down Expand Up @@ -378,6 +378,45 @@ module Preprocess = struct
| _ -> []
end

module Blang = struct
type 'a t = 'a Blang.t

open Blang
let ops =
[ "=", Op.Eq
; ">=", Gte
; "<=", Lt
; ">", Gt
; "<", Lt
; "<>", Neq
]

let t =
let ops =
List.map ops ~f:(fun (name, op) ->
( name
, (let%map x = String_with_vars.t
and y = String_with_vars.t
in
Compare (op, x, y))))
in
let t =
fix begin fun (t : String_with_vars.t Blang.t Sexp.Of_sexp.t) ->
if_list
~then_:(
[ "or", repeat t >>| (fun x -> Or x)
; "and", repeat t >>| (fun x -> And x)
] @ ops
|> sum)
~else_:(String_with_vars.t >>| fun v -> Expr v)
end
in
let%map () = Syntax.since Stanza.syntax (1, 1)
and t = t
in
t
end

module Per_module = struct
include Per_item.Make(Module.Name)

Expand Down Expand Up @@ -1336,15 +1375,15 @@ module Rule = struct
let dune_syntax =
peek_exn >>= function
| List (_, Atom (loc, A s) :: _) -> begin
match String.Map.find atom_table s with
| None ->
of_sexp_errorf loc ~hint:{ on = s
; candidates = String.Map.keys atom_table
}
"Unknown action or rule field."
| Some Field -> fields long_form
| Some Action -> short_form
end
match String.Map.find atom_table s with
| None ->
of_sexp_errorf loc ~hint:{ on = s
; candidates = String.Map.keys atom_table
}
"Unknown action or rule field."
| Some Field -> fields long_form
| Some Action -> short_form
end
| sexp ->
of_sexp_errorf (Sexp.Ast.loc sexp)
"S-expression of the form (<atom> ...) expected"
Expand Down Expand Up @@ -1500,6 +1539,7 @@ module Alias_conf = struct
; action : (Loc.t * Action.Unexpanded.t) option
; locks : String_with_vars.t list
; package : Package.t option
; enabled_if : String_with_vars.t Blang.t option
}

let alias_name =
Expand All @@ -1516,21 +1556,24 @@ module Alias_conf = struct
and action = field_o "action" (located Action.Unexpanded.t)
and locks = field "locks" (list String_with_vars.t) ~default:[]
and deps = field "deps" (Bindings.t Dep_conf.t) ~default:Bindings.empty
and enabled_if = field_o "enabled_if" Blang.t
in
{ name
; deps
; action
; package
; locks
; enabled_if
})
end

module Tests = struct
type t =
{ exes : Executables.t
; locks : String_with_vars.t list
; package : Package.t option
; deps : Dep_conf.t Bindings.t
{ exes : Executables.t
; locks : String_with_vars.t list
; package : Package.t option
; deps : Dep_conf.t Bindings.t
; enabled_if : String_with_vars.t Blang.t option
}

let gen_parse names =
Expand All @@ -1543,6 +1586,7 @@ module Tests = struct
and modes = field "modes" Executables.Link_mode.Set.t
~default:Executables.Link_mode.Set.default
and deps = field "deps" (Bindings.t Dep_conf.t) ~default:Bindings.empty
and enabled_if = field_o "enabled_if" Blang.t
in
{ exes =
{ Executables.
Expand All @@ -1555,6 +1599,7 @@ module Tests = struct
; locks
; package
; deps
; enabled_if
})

let multi = gen_parse (field "names" (list (located string)))
Expand Down
10 changes: 6 additions & 4 deletions src/jbuild.mli
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ module Alias_conf : sig
; action : (Loc.t * Action.Unexpanded.t) option
; locks : String_with_vars.t list
; package : Package.t option
; enabled_if : String_with_vars.t Blang.t option
}
end

Expand All @@ -355,10 +356,11 @@ end

module Tests : sig
type t =
{ exes : Executables.t
; locks : String_with_vars.t list
; package : Package.t option
; deps : Dep_conf.t Bindings.t
{ exes : Executables.t
; locks : String_with_vars.t list
; package : Package.t option
; deps : Dep_conf.t Bindings.t
; enabled_if : String_with_vars.t Blang.t option
}
end

Expand Down
69 changes: 41 additions & 28 deletions src/simple_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -70,32 +70,45 @@ let add_alias sctx ~dir ~name ~stamp ?(locks=[]) build =
SC.add_alias_action sctx alias ~locks ~stamp build

let alias sctx ~dir ~scope (alias_conf : Alias_conf.t) =
let stamp =
let module S = Sexp.To_sexp in
Sexp.List
[ Sexp.unsafe_atom_of_string "user-alias"
; Jbuild.Bindings.sexp_of_t Jbuild.Dep_conf.sexp_of_t alias_conf.deps
; S.option Action.Unexpanded.sexp_of_t
(Option.map alias_conf.action ~f:snd)
]
let enabled =
match alias_conf.enabled_if with
| None -> true
| Some blang ->
let f : String_with_vars.t Blang.expander =
{ f = fun ~mode sw ->
( String_with_vars.loc sw
, Super_context.expand_vars sctx ~scope ~mode ~dir sw
)
} in
Blang.eval_bool blang ~dir ~f
in
add_alias sctx
~dir
~name:alias_conf.name
~stamp
~locks:(interpret_locks sctx ~dir ~scope alias_conf.locks)
(SC.Deps.interpret_named sctx ~scope ~dir alias_conf.deps
>>>
match alias_conf.action with
| None -> Build.progn []
| Some (loc, action) ->
SC.Action.run
sctx
action
~loc
~dir
~dep_kind:Required
~bindings:(Pform.Map.of_bindings alias_conf.deps)
~targets:Alias
~targets_dir:dir
~scope)
if enabled then
let stamp =
let module S = Sexp.To_sexp in
Sexp.List
[ Sexp.unsafe_atom_of_string "user-alias"
; Jbuild.Bindings.sexp_of_t Jbuild.Dep_conf.sexp_of_t alias_conf.deps
; S.option Action.Unexpanded.sexp_of_t
(Option.map alias_conf.action ~f:snd)
]
in
add_alias sctx
~dir
~name:alias_conf.name
~stamp
~locks:(interpret_locks sctx ~dir ~scope alias_conf.locks)
(SC.Deps.interpret_named sctx ~scope ~dir alias_conf.deps
>>>
match alias_conf.action with
| None -> Build.progn []
| Some (loc, action) ->
SC.Action.run
sctx
action
~loc
~dir
~dep_kind:Required
~bindings:(Pform.Map.of_bindings alias_conf.deps)
~targets:Alias
~targets_dir:dir
~scope)
10 changes: 10 additions & 0 deletions src/stdune/bool.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type t = bool

let compare x y =
match x, y with
| true, true
| false, false -> Ordering.Eq
| true, false -> Gt
| false, true -> Lt

let to_string = string_of_bool
5 changes: 5 additions & 0 deletions src/stdune/bool.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type t = bool

val compare : t -> t -> Ordering.t

val to_string : t -> string
1 change: 0 additions & 1 deletion src/stdune/sexp.ml
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,6 @@ module Of_sexp = struct
String.Map.iteri map ~f:(Hashtbl.add tbl);
tbl


let find_cstr cstrs loc name ctx values =
match List.assoc cstrs name with
| Some t ->
Expand Down
Loading