-
Notifications
You must be signed in to change notification settings - Fork 413
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
Allow variables in pps flags #2076
Conversation
5e2ca41
to
d4472e9
Compare
Related to #57 |
We had a similar discussion in #2020. My idea was that when declaring the ppx rewriter, we would declare what informations it wants. i.e. instead of passing (library
(public_name landmarks.ppx)
(kind (ppx_rewriter (cookies (profile %{profile}))))) @Khady also mentioned that matching on the profile wasn't great. So I was thinking to add a new variable
The default would be However, the current PR still seems good to me. It's nice to be able to use variables in the arguiments of ppx rewriters. |
src/merlin.ml
Outdated
let compare_flags f1 f2 = | ||
String.compare | ||
(Expander.expand_str expander f1) | ||
(Expander.expand_str expander f2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we are going to do the expansion several times in this file. We should either do the expansion in make
, or simply comapre the string unexpanded here. The latter seems simpler and enough.
@diml Thanks for your input ! It seems indeed nicer to have it defined in the library stanzas. I try to implement your suggestions. It was a bit boring to write the comparison functions; should I have used String.compare composed with "String_with_vars.to_string" ? |
The way you did it is good. Unfortunately, we do have to write these boring functions at the moment. However, we are thinking of setting up something based on Could you add a changelog entry and a test for this feature? You can add some variables in the BTW, I just noticed that variables are only supported after the |
Ok, I'll try to do that on the beginning of next week. |
df4ea51
to
776a73f
Compare
(preprocess (staged_pps -arg1 driver_print_tool -arg2 -- -foo bar))) | ||
(preprocess | ||
(staged_pps -arg1 driver_print_tool -arg2 -arg3=%{env:SOME_UNDEFINED_VARIABLE=undefined} | ||
-- -foo bar %{env:SOME_UNDEFINED_VARIABLE=undefined}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to the reviewer: I struggled to find a variable that is actually ... a constant in (almost) all context; that's why I used this "trick" ; the test will fail on machine that define a variable called "SOME_UNDEFINED_VARIABLE".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack. I wondered about this actually. To avoid the last pitfall, you can add the following stanza in this dune
file:
(env (_ (env-vars (X foo))))
src/dune_file.ml
Outdated
in | ||
let flags = Option.value flags ~default:[] in | ||
if syntax_version < (1, 10) then | ||
List.iter ~f:check_allowed_variable flags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small nitpick: I would do the check right at the end, on the final more_flags @ flags
list. That would make the code slightly shorter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok !
src/dune_lang/template.mli
Outdated
val string_of_var : var -> string | ||
|
||
val pp : File_syntax.t -> Format.formatter -> t -> unit | ||
|
||
val pp_split_strings : Format.formatter -> t -> unit | ||
|
||
val remove_locs : t -> t | ||
val is_prefix : t -> prefix:string -> bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The false
result is slightly ambiguous here, as we don't know whether prefix
is really not a prefix or wether we can't answer accurately because the string is not evaluated enough. I suggest to return something else that bool
that can express this third state. The case where we don't know should be effectively handled the same way as false
, but at least it's explicit when reading the code at the call site. For instance:
type is_prefix_result = Yes | No | Unknown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or I could rename the function into "is_prefix_before_expansion" ?
Note that the implementation of is_prefix is not trivial and could be replaced by the simpler
let is_prefix {parts; _} ~prefix = function Text s :: _ -> String.is_prefix s ~prefix | [] | Var _ :: _ -> String.length prefix = 0
But it assumes that text parts are always interleaved with variables (as does the already existing "text_only" function). I actually think that this simpler implementation is better, what do you think ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_prefix_before_expansion
makes me think of testing is_prefix
on the original string, i.e. is_prefix_before_expansion t ~prefix = String.is_prefix (to_string t) ~prefix
.
But it assumes that text parts are always interleaved with variables (as does the already existing "text_only" function). I actually think that this simpler implementation is better, what do you think ?
Agreed. That seems better to me as well. The various functions are also careful about ensuring that we never produce Text x :: Text y
or Text ""
.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the 3-way is_prefix_result
better than trying to express this in the name.
Regarding canonical representation, I think it's better to not rely on canonical representation if we can help it.
Maybe a nice middle ground could be a function like this:
let get_known_prefix =
let rec go t acc = match t with
| Text s :: rest -> go (s :: acc) rest
| _ -> String.concat ~sep:"" (List.rev acc)
in
fun t -> go t []
(or perhaps a variation that distinguishes fully known string from partially unknown)
In fact I need something like this (for the suffix) in #2073.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, the is_prefix
that assume the canonical representation would be:
type is_prefix_result = Yes | No | Unknown
let is_prefix t ~prefix =
match t.template_.parts with
| [Text s] -> if String.is_prefix s ~prefix then Yes else No
| Text s :: _ ->
if String.is_prefix s ~prefix then
Yes
else
if String.is_prefix prefix ~prefix:s then
Unknown
else No
| [] -> if String.length prefix = 0 then Yes else No
| Var _ :: _ -> if String.length prefix = 0 then Yes else Unknown
I think I like the "get_known_prefix" better.
in | ||
(pps, more_flags @ Option.value flags ~default:[]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
( Btw, the final flags are "swapped": when we write "-arg1 -arg2 -- arg3 arg4" in the dune file the preprocessor will read "arg3 arg4 -arg1 -arg2" ; was it on purpose ? )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, no that does look like a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I actually misread the order is right.
5663510
to
b09ee23
Compare
This commit adds support for variables in the flags of the "pps" (and "pps_staged") kind of preprocessing. This allows, for instance, to pass %{profile}, %{context_name}, or environment variables as preprocessor argument. Signed-off-by: Marc Lasson <marc.lasson@lexifi.com>
b09ee23
to
c3e0c58
Compare
It should be good now. |
Looks good, thanks for this PR! |
BTW, would you be happy to help with #2020? :) I started the work in the branch |
I'll see what I can do ! (but no promise; as I have my own todo list to go through :)). |
Sounds good :) |
CHANGES: - Restricted the set of variables available for expansion in the destination filename of `install` stanza to simplify implementation and avoid dependency cycles. (ocaml/dune#2073, @aalekseyev, @diml) - [menhir] call menhir from context root build_dir (ocaml/dune#2067, @ejgallego, review by @diml, @rgrinberg) - [coq] Add `coq.pp` stanza to help with pre-processing of grammar files (ocaml/dune#2054, @ejgallego, review by @rgrinberg) - Add a new more generic form for the *promote* mode: `(promote (until-clean) (into <dir>))` (ocaml/dune#2068, @diml) - Allow to promote only a subset of the targets via `(promote (only <pred>))`. For instance: `(promote (only *.mli))` (ocaml/dune#2068, @diml) - Improve the behavior when a strict subset of the targets of a rule is already in the source tree for projects using the dune language < 1.10 (ocaml/dune#2068, fixes ocaml/dune#2061, @diml) - With lang dune >= 1.10, rules in standard mode are no longer allowed to produce targets that are present in the source tree. This has been a warning for long enough (ocaml/dune#2068, @diml) - Allow %{...} variables in pps flags (ocaml/dune#2076, @mlasson review by @diml and @aalekseyev). - Add a 'cookies' option to ppx_rewriter/deriver flags in library stanzas. This allow to specify cookie requests from variables expanded at each invocation of the preprocessor. (ocaml/dune#2106, @mlasson @diml) - Add more opam metadata and use it to generate `.opam` files. In particular, a `package` field has been added to specify package specific information. (ocaml/dune#2017, ocaml/dune#2091, @avsm, @jonludlam, @rgrinberg) - Clean up the special support for `findlib.dynload`. Before, Dune would simply match on the library name. Now, we only match on the findlib package name when the library doesn't come from Dune. Someone writing a library called `findlib.dynload` with Dune would have to add `(special_builtin_support findlib_dynload)` to trigger the special behavior. (ocaml/dune#2115, @diml) - Install the `future_syntax` preprocessor as `ocaml-syntax-shims.exe` (ocaml/dune#2125, @rgrinberg) - Hide full command on errors and warnings in development and show them in CI. (detected using the `CI` environment variable). Commands for which the invocation might be omitted must output an error prefixed with `File `. Add an `--always-show-command-line` option to disable this behavior and always show the full command. (ocaml/dune#2120, fixes ocaml/dune#1733, @rgrinberg) - In `dune-workspace` files, add the ability to choose the host context and to create duplicates of the default context with different settings. (ocaml/dune#2098, @TheLortex, review by @diml, @rgrinberg and @aalekseyev) - Add support for hg in `dune subst` (ocaml/dune#2135, @diml) - Don't build documentation for implementations of virtual libraries (ocaml/dune#2141, fixes ocaml/dune#2138, @jonludlam) - Fix generation of the `-pp` flag in .merlin (ocaml/dune#2142, @rgrinberg) - Make `dune subst` add a `(version ...)` field to the `dune-project` file (ocaml/dune#2148, @diml) - Add the `%{os_type}` variable, which is a short-hand for `%{ocaml-config:os_type}` (ocaml/dune#1764, @diml) - Allow `enabled_if` fields in `library` stanzas, restricted to the `%{os_type}`, `%{model}`, `%{architecture}`, `%{system}` variables (ocaml/dune#1764, ocaml/dune#2164 @diml, @rgrinberg) - Fix `chdir` on external and source paths. Dune will also fail gracefully if the external or source path does not exist (ocaml/dune#2165, fixes ocaml/dune#2158, @rgrinberg) - Support the `.cc` extension fro C++ sources (ocaml/dune#2195, fixes ocaml/dune#83, @rgrinberg) - Run `ocamlformat` relative to the context root. This improves the locations of errors. (ocaml/dune#2196, fixes ocaml/dune#1370, @rgrinberg) - Fix detection of `README`, `LICENSE`, `CHANGE`, and `HISTORY` files. These would be undetected whenever the project was nested in another workspace. (ocaml/dune#2194, @rgrinberg) - Fix generation of `.merlin` whenever there's more than one stanza with the same ppx preprocessing specification (ocaml/dune#2209 ,fixes ocaml/dune#2206, @rgrinberg) - Fix generation of `.meriln` in the presence of the `copy_files` stanza and preprocessing specifications of other stanazs. (ocaml/dune#2211, fixes ocaml/dune#2206, @rgrinberg)
CHANGES: - Restricted the set of variables available for expansion in the destination filename of `install` stanza to simplify implementation and avoid dependency cycles. (ocaml/dune#2073, @aalekseyev, @diml) - [menhir] call menhir from context root build_dir (ocaml/dune#2067, @ejgallego, review by @diml, @rgrinberg) - [coq] Add `coq.pp` stanza to help with pre-processing of grammar files (ocaml/dune#2054, @ejgallego, review by @rgrinberg) - Add a new more generic form for the *promote* mode: `(promote (until-clean) (into <dir>))` (ocaml/dune#2068, @diml) - Allow to promote only a subset of the targets via `(promote (only <pred>))`. For instance: `(promote (only *.mli))` (ocaml/dune#2068, @diml) - Improve the behavior when a strict subset of the targets of a rule is already in the source tree for projects using the dune language < 1.10 (ocaml/dune#2068, fixes ocaml/dune#2061, @diml) - With lang dune >= 1.10, rules in standard mode are no longer allowed to produce targets that are present in the source tree. This has been a warning for long enough (ocaml/dune#2068, @diml) - Allow %{...} variables in pps flags (ocaml/dune#2076, @mlasson review by @diml and @aalekseyev). - Add a 'cookies' option to ppx_rewriter/deriver flags in library stanzas. This allow to specify cookie requests from variables expanded at each invocation of the preprocessor. (ocaml/dune#2106, @mlasson @diml) - Add more opam metadata and use it to generate `.opam` files. In particular, a `package` field has been added to specify package specific information. (ocaml/dune#2017, ocaml/dune#2091, @avsm, @jonludlam, @rgrinberg) - Clean up the special support for `findlib.dynload`. Before, Dune would simply match on the library name. Now, we only match on the findlib package name when the library doesn't come from Dune. Someone writing a library called `findlib.dynload` with Dune would have to add `(special_builtin_support findlib_dynload)` to trigger the special behavior. (ocaml/dune#2115, @diml) - Install the `future_syntax` preprocessor as `ocaml-syntax-shims.exe` (ocaml/dune#2125, @rgrinberg) - Hide full command on errors and warnings in development and show them in CI. (detected using the `CI` environment variable). Commands for which the invocation might be omitted must output an error prefixed with `File `. Add an `--always-show-command-line` option to disable this behavior and always show the full command. (ocaml/dune#2120, fixes ocaml/dune#1733, @rgrinberg) - In `dune-workspace` files, add the ability to choose the host context and to create duplicates of the default context with different settings. (ocaml/dune#2098, @TheLortex, review by @diml, @rgrinberg and @aalekseyev) - Add support for hg in `dune subst` (ocaml/dune#2135, @diml) - Don't build documentation for implementations of virtual libraries (ocaml/dune#2141, fixes ocaml/dune#2138, @jonludlam) - Fix generation of the `-pp` flag in .merlin (ocaml/dune#2142, @rgrinberg) - Make `dune subst` add a `(version ...)` field to the `dune-project` file (ocaml/dune#2148, @diml) - Add the `%{os_type}` variable, which is a short-hand for `%{ocaml-config:os_type}` (ocaml/dune#1764, @diml) - Allow `enabled_if` fields in `library` stanzas, restricted to the `%{os_type}`, `%{model}`, `%{architecture}`, `%{system}` variables (ocaml/dune#1764, ocaml/dune#2164 @diml, @rgrinberg) - Fix `chdir` on external and source paths. Dune will also fail gracefully if the external or source path does not exist (ocaml/dune#2165, fixes ocaml/dune#2158, @rgrinberg) - Support the `.cc` extension fro C++ sources (ocaml/dune#2195, fixes ocaml/dune#83, @rgrinberg) - Run `ocamlformat` relative to the context root. This improves the locations of errors. (ocaml/dune#2196, fixes ocaml/dune#1370, @rgrinberg) - Fix detection of `README`, `LICENSE`, `CHANGE`, and `HISTORY` files. These would be undetected whenever the project was nested in another workspace. (ocaml/dune#2194, @rgrinberg) - Fix generation of `.merlin` whenever there's more than one stanza with the same ppx preprocessing specification (ocaml/dune#2209 ,fixes ocaml/dune#2206, @rgrinberg) - Fix generation of `.merlin` in the presence of the `copy_files` stanza and preprocessing specifications of other stanazs. (ocaml/dune#2211, fixes ocaml/dune#2206, @rgrinberg) - Run `refmt` from the context's root directory. This improves error messages in case of syntax errors. (ocaml/dune#2223, @rgrinberg) - In .merlin files, don't pass `-dump-ast` to the `future_syntax` preprocessor. Merlin doesn't seem to like it when binary AST is generated by a `-pp` preprocessor. (ocaml/dune#2236, @aalekseyev) - `dune install` will verify that all files mentioned in all .install files exist before trying to install anything. This prevents partial installation of packages (ocaml/dune#2230, @rgrinberg)
This PR propose to add support for variables in the flags of the "pps" (and "pps_staged") kind of preprocessing.
The use-case I have in mind would be to pass "%{profile}" to the preprocessor which would allow it to generate a different code depending on the profile. This would be useful for preprocessors that implement code instrumentation (eg. bisect, or landmarks): they could implement the "identity preprocessor" when profile is "release" (also they could use the profile name as a scoping mechanism).
Here is an example:
I made this PR to experiment with a profile-based workflow for landmarks and maybe to see if others think it sounds like a good idea. So, do not hesitate to comment or to close the PR if it happens to be a bad idea.