-
Notifications
You must be signed in to change notification settings - Fork 409
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
Reason support #58
Reason support #58
Conversation
Add transparent support for reason sources. This requires adding a flag to Module.t to distinguish reason and and ocaml sources.
Module.t's ml_fname and mli_fname are no longer good names since Module.t can contain reason modules as well. impl_fname and intf_fname are better names
src/gen_rules.ml
Outdated
let ml = Module.ocaml_of_reason re in | ||
let refmt = | ||
match Context.which ctx "refmt" with | ||
| None -> die "refmt not found. Fix with $ opam install reason" |
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.
There is a function Utils.program_not_found
to raise a standard error, could you use it instead? Additionally, to match other messages, it should print:
Hint: opam install reason
you can add a ?hint
argument to Util.program_not_found
to do that.
Another issue is that the current code will fail when setting up the rules instead of when evaluating them, which for instance makes external-lib-deps
unusable. You can do this instead:
| None -> Build.Prog_spec.Dyn (fun _ -> <raise exception>)
| Some refmt -> Dep refmt
src/gen_rules.ml
Outdated
(* Generate rules for the reason modules in [modules] and return a list | ||
a new list of modules with only OCaml sources *) | ||
let reason_rules ~dir ~modules = | ||
let rule re = |
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.
This should be called rules
given that it returns several rules. Alternatively it could also be called setup_rules
and call add_rule
directly
src/gen_rules.ml
Outdated
; obj_name = lib.name ^ suf | ||
} | ||
Some (Module.create ~name:(main_module_name ^ suf) | ||
~impl_fname:(lib.name ^ suf ^ ".ml-gen") |
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.
Won't Module.create
choke on the .ml-gen
extension? I think it would be simpler to just add an is_reason:bool
argument to Module.create
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 think at that point create
becomes useless so I'll just remove it.
src/gen_rules.ml
Outdated
| ".re" -> (ml, mli, f :: re, rei) | ||
| ".rei" -> (ml, mli, rei, f :: rei) | ||
| "" -> (ml, mli, re, rei) | ||
| f -> invalid_arg ("Unexpected extension from: " ^ f)) |
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.
guess_modules
is called with all the files in the directory as argument, so it should simply ignore extensions it doesn't recognize.
BTW, the previous code used String.lsplit2 ~on:'.'
rather than Filename.extension
. This was intentional, it is to filter out filenames such as foo.cppo.ml
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'll leave a comment about this fact too
src/gen_rules.ml
Outdated
| Some _, Some _ -> assert false | ||
) | ||
else | ||
die "The following modules have both reason and ocaml sources:@.%a" |
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 don't think this is going to work really well. For instance, you might want to write a .rei file for a generated lexer. Then you have a module with a reason interface and a ml implementation.
I think it's fine to just keep 2 lists as before, but add a flag to each element telling whether it is reason or ocaml. The existing checks will catch cases when there are both a .ml and .re files
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.
For instance, you might want to write a .rei file for a generated lexer. Then you have a module with a reason interface and a ml implementation
So in this situation a per module reason
flag doesn't even make sense anymore. Since we'd allow to mix and match OCaml/Reason interfaces/implemenations? If so, where would this flag exist? Seems like the type of source could be discovered from the intf_fname
and impl_fname
Would let to get this and the comment below straight before I make the changes.
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.
Indeed, we could just look at the extension, but I think it would be cleaner to keep separated flags in [impl_fname] and [intf_fname]. We could change the types in module.mli as follow:
module Syntax : sig
type t = OCaml | Reason
end
module File : sig
type t =
{ name : string
; syntax : Syntax.t
}
end
type t =
{ name : string
; impl : File.t
; intf : File.t option
...
src/gen_rules.ml
Outdated
let preprocessor_deps = Dep_conf_interpret.dep_of_list ~dir preprocessor_deps in | ||
String_map.map modules ~f:(fun (m : Module.t) -> | ||
assert (not m.reason); |
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.
Instead of doing two successive String_map.map
, we could replace this line by:
let m = ocaml_of_reason m in
src/module.ml
Outdated
if t.reason then | ||
{ t with | ||
reason = false | ||
; impl_fname = Filename.chop_extension t.impl_fname ^ ".ml" |
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.
Actually we should just keep the two extensions, i.e. the filename would be: foo.re.ml
. The reason for this is that otherwise the generation of the rules is not a fix point: initially guess_modules
picks up only foo.re
, but if run again it would pickup foo.re
and foo.ml
. It doesn't matter right now, but it might for the jenga bridge. It might also matter for the implementation of #35.
src/module.ml
Outdated
~f:(fun mli -> Filename.chop_extension mli ^ ".mli") | ||
} | ||
else | ||
invalid_arg "ocaml_of_reason only expect reason sources" |
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 tend to use code_errorf
(defined in import.ml
). The Code_error
exception is caught in main.ml
and a better error message is printed. It should also include the function name
Thanks. Renaming looks good, let's keep it |
Note that this still lacks the ability to mix and match reason/ocaml sources
The tag denotes the syntax of the module
ok @diml I've tried to address all of your points. Every new commit tries to address one of your points separately except for the last few which tie-in everything together. I didn't quite get your suggestion as how to avoid the use of 2 successive |
The new rules now also allow to mix & match reason/ocaml interfaces/implementations
src/module.ml
Outdated
| Intf -> Option.map t.mli_fname ~f:(fun _ -> Path.relative dir (t.obj_name ^ ".cmti")) | ||
| Intf -> Option.map t.intf ~f:(fun _ -> Path.relative dir (t.obj_name ^ ".cmti")) | ||
|
||
let ocaml_of_reason _t = |
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.
This is no longer needed
Yep, that's what I meant. Code looks good, appart from the leftover function it's ready to be merged |
Should we let jordan and co. test it out first? |
src/module.mli
Outdated
@@ -16,3 +30,5 @@ val file : t -> dir:Path.t -> Ml_kind.t -> Path.t option | |||
val cm_source : t -> dir:Path.t -> Cm_kind.t -> Path.t option | |||
val cm_file : t -> dir:Path.t -> Cm_kind.t -> Path.t | |||
val cmt_file : t -> dir:Path.t -> Ml_kind.t -> Path.t option | |||
|
|||
val ocaml_of_reason : t -> t |
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.
And this one as well
Sure, our we can test it ourselves. BTW I just though of an improvement, instead of the |
Yup. I also tested this, but I don't really have any real reason projects to try it on. They can try this out at their own leisure I suppose. I'll make that change to use Artifacts. |
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.
Wow, thank you @rgrinberg.
a new module with only OCaml sources *) | ||
let setup_reason_rules ~dir (m : Module.t) = | ||
let refmt = | ||
match Artifacts.binary "refmt" with |
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.
Just to confirm, will this work if refmt
is a symlink? It very well might be. Nice touch with the hint to install via opam.
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.
Yes. Basically it is the same as writing ${bin:refmt}
in a user rule in a jbuild file:
- if
refmt
is a tool that is installed by the project being compiled, then jbuilder will use resolve${bin:refmt}
to the local binary - otherwise it is looked up in the PATH
Basically this changes nothing for users of reason. What it changes is that if you wanted to use jbuilder to build reason itself, then you could build the tests/examples directly without having to install reason first.
Ok, well the patch looks ready to me and has been tested so I'm merging now |
Thanks, everyone! Can't wait to try it out. |
Attempt at #53
@diml I've left the renaming commit separate at the end so you can judge if you want it or not.
@jordwalke @IwanKaramazow some testing would be appreciated.