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

enabled_if #819

merged 2 commits into from
Jul 31, 2018

Conversation

rgrinberg
Copy link
Member

@rgrinberg rgrinberg commented May 29, 2018

Problem

The problem this PR is trying to solve is a bit specific to dune, but it's likely that it will be encountered by other projects as well. Currently, dune has a complicated script for generating test definitions that make sure tests run only in environments which support them. For example, some tests may not run on win32. A mechanism to enable tests conditionally would fix these problems.

Proposal

Add an enabled_if field to aliases to toggle the execution of the alias. This field will be valued by a little EDSL for expressing boolean expressions. Here's an example of the kind of conditions we'd express with it:

(alias
 ((name runtest)
  (deps (foo.exe))
  (action (run ${<}))
  (enabled_if (and (<> ${os} win32) (>= ${ocaml_version} 4.0.5)))))

Progress

This stalled a bit since I'm not sure how to do handle type safety here. Ideally, we only allow numbers and versions to be compared with >=, <, etc. I guess we really need to annotate which variables are comparable and making sure that we don't compare numbers to strings or versions.

@rgrinberg rgrinberg requested a review from a user May 29, 2018 08:52
@ghost
Copy link

ghost commented May 30, 2018

this boolean language looks quite simple and during development all expressions should be evaluated on every run, so runtime checks will do.

Currently we already keep the type of variable expansion (string or path). We can add a version type

@rgrinberg
Copy link
Member Author

Okay, makes sense. I'm wondering how we should organize things however. Blang could certainly live in its own module, but Var_expansion.t isn't really specific to actions anymore.

@ghost
Copy link

ghost commented May 31, 2018

Maybe a new Value module? That's basically what it represents. BTW, let's do #701 (comment) first as it formalizes the interpretation of variables.

@rgrinberg
Copy link
Member Author

@diml now that we have a Value module, I'd like to move this along. One question is how to handle expressions like : (enabled_if %{ocaml_version}). In other words, what happens if we try to use a non boolean valued expression as the condition in enabled_if. Just a runtime error?

@ghost
Copy link

ghost commented Jul 6, 2018

Yes, a runtime error seems fine for now. These expressions are very simple and will be evaluated often during development, so there is no need for heavy machinery.

@rgrinberg rgrinberg added this to the 1.1.0 milestone Jul 11, 2018
@rgrinberg
Copy link
Member Author

Okay. One last point is that we might be closing the door on things like (enabled_if %{read:file-with-flag}) since there would be no way to read a file into a boolean like this. This is a bit awkward, but I can imagine wanting a toggle via env var for example. In which case, an awkward form would have to be invented: (enabled_if %{env-var-set-p:FOO})

@ghost
Copy link

ghost commented Jul 12, 2018

That seems fine, but if we add support for reading environment variables like this, then (setenv X foo (echo %{env:X}) must print "foo".

@rgrinberg rgrinberg changed the title enabled_if [WIP] enabled_if Jul 20, 2018
@rgrinberg
Copy link
Member Author

Okay, this PR has been revived and seems to be in a good enough form for a v1 of the feature. The only thing that remains is to make sure that the field is available only for 1.1 version of the language. The current version doesn't support advanced features like %{read:..} stanzas, but should be good enough for our use case in dune.

@rgrinberg
Copy link
Member Author

I added support for the (1, 1) version guard.

@rgrinberg
Copy link
Member Author

This PR is a pre-req as well for #924.

@ghost
Copy link

ghost commented Jul 30, 2018

Ok, could you write the documentation for this feature? I'd like to read the semantic of boolean expressions before reading the code that implements it.

@rgrinberg
Copy link
Member Author

Okay, will add that to the TODO list. As a tl;dr:

  • Value.String "false" is false, Value.String "true" is true. Everything else is an error when evaluated as a boolean. This is to match the way things are in $ ocamlc -config.

  • Comparison operators are done on Value.t list as string lists (with some optimization to avoid conversion when it isn't necessary).

I considered introducing Value.Boolean constructor, but if there's no way for the user to actually input this value as a literal, the use seems limited.

@ghost
Copy link

ghost commented Jul 30, 2018

Thanks, that seems reasonable

src/blang.ml Outdated
| Or of 'a t list
| Compare of Op.t * 'a * 'a

let compare_vals ~dir (x : Value.t) (y : Value.t) =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about putting this in Value.L?

~targets:Alias
~targets_dir:dir
~scope)
let add_alias () =
Copy link

@ghost ghost Jul 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code would be slightly clearer if written this way:

let enabled =
  match alias_conf.enabled_if with
  | None -> true
  | Some blang -> ...
in
if enabled then
  add_alias sctx ...

@@ -81,6 +81,11 @@ module Of_sexp = struct
Printf.ksprintf (fun msg ->
of_sexp_error loc ?hint ("No variables allowed " ^ msg)) fmt

let _fix f =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not modify the type of fix instead of adding this function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an artefact from the rebase. Removed.

src/jbuild.ml Outdated
Compare (op, x, y))))
in
let t =
fix begin fun (t : String_with_vars.t Blang.t Sexp.Of_sexp.t) ->
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation looks wrong

src/jbuild.ml Outdated
[ "or", repeat t >>| (fun x -> Or x)
; "and", repeat t >>| (fun x -> And x)
] @ ops
|> sum ~rest:(String_with_vars.t >>| fun v -> Expr v)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm dubious about this rest argument. I feel like it's going to make error messages worse. Could you use if_list instead?

@rgrinberg
Copy link
Member Author

@diml I've addressed all your comments. Actually, ~rest was only there because it's older than if_list. I've simply didn't reconsider the implementation using the new API after rebasing.


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be: (<op> <template> <template>)

src/value.ml Outdated
let compare x y =
match x, y with
| String x, String y -> String.compare x y
| Path x, Path y -> Path.compare x y
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Dir x, Dir y case is missing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we don't need this function anyway. I got rid of it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@rgrinberg rgrinberg force-pushed the enabled-if branch 2 times, most recently from f7a9dc8 to bf89dee Compare July 31, 2018 08:09
@ghost
Copy link

ghost commented Jul 31, 2018

Looks good, but it seems that the doc comment I added on Value.L.compare_vals disappeared

@rgrinberg
Copy link
Member Author

@diml oops, the subsequent commit removed it during the rebase. The doc is now back.

This field controls whether the alias/test will be run or not. Boolean
expressions are defined using the Blang.t type. This type represents simple
boolean expressions that become useful when we allow to interpolate variables
into them.

Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
@rgrinberg
Copy link
Member Author

Squashed & Rebased. Also added a change log entry.

@rgrinberg rgrinberg merged commit de0ccfa into ocaml:master Jul 31, 2018
rgrinberg added a commit to rgrinberg/opam-repository that referenced this pull request Aug 6, 2018
CHANGES:

- Fix lookup of command line specified files when `--root` is given. Previously,
  passing in `--root` in conjunction with `--workspace` or `--config` would not
  work correctly (ocaml/dune#997, @rgrinberg)

- Add support for customizing env nodes in workspace files. The `env` stanza is
  now allowed in toplevel position in the workspace file, or for individual
  contexts. This feature requires `(dune lang 1.1)` (ocaml/dune#1038, @rgrinberg)

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

- Make `name`, `names` fields optional when a `public_name`, `public_names`
  field is provided. (ocaml/dune#1041, fix ocaml/dune#1000, @rgrinberg)

- Interpret `X` in `--libdir X` as relative to `PREFIX` when `X` is relative
  (ocaml/dune#1072, fix ocaml/dune#1070, @diml)

- Add support for multi directory libraries by writing
  `(include_subdirs unqualified)` (ocaml/dune#1034, @diml)

- Add `(staged_pps ...)` to support staged ppx rewriters such as ones
  using the OCaml typer like `ppx_import` (ocaml/dune#1080, fix ocaml/dune#193, @diml)

- Use `-opaque` in the `dev` profile. This option trades off binary quality for
  compilation speed when compiling .cmx files. (ocaml/dune#1079, fix ocaml/dune#1058, @rgrinberg)

- Fix placeholders in `dune subst` documentation (ocaml/dune#1090, @emillon, thanks
  @trefis for the bug report)

- Add locations to errors when a missing binary in PATH comes from a dune file
  (ocaml/dune#1096, fixes ocaml/dune#1095, @rgrinberg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant