-
Notifications
You must be signed in to change notification settings - Fork 412
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
Compute transitive modules depencies #666
Conversation
Indeed, that's the idea. OverviewThe So the goal of this PR is to make
Implementation details
Build.contents "file.ml.d"
>>>
Build.arr parse where TestingWe have a blackbox tests framework which allows to run the final jbuilder executables on real examples and capture the output. Usually what we do is start by adding a test that demonstrates the failure so that we can see when it is fixed. To add a blackbox test, you can create a directory in
Then edit BTW, I was initially thinking that we would just remove a few |
f9ad029
to
f37e2dd
Compare
Thanks for the detailed instructions. I have some questions already: The build arrowI'm having trouble understanding the relation between rules and the build arrow. If I understand it correctly, a rule is added for each module, and there's no "pattern rule": Lines 129 to 133 in 126c91f
Is there an implicit dependency through the file system, or do I have to handle that by hand? In other words, if my TestsAbout tests, other blackbox tests use a plain
No problem, I'll just take baby steps because that's indeed quite involved already! |
@emillon Jeremie is MIA this week, so I'll try and answer your questions:
Yes. Dependency analysis is done on every module separately to produce a corresponding
That's correct. You must set up rules explicitly by looping.
The build arrow will handle dependencies for you as long as you use the dependency aware primitives. What this means is that you should certainly avoid the
There are many examples of this, so I can't list them all, but certainly ask if you're unclear about how to model your dependencies.
|
Thanks, that's helpful. I think that I'm getting somewhere:
Anyway, the problem I'm hitting is how to handle missing files. It seems that I'm not handling the case where the Extra question: what is Thanks a lot! |
That seems like it could be a useful change, but it's actually not necessary for this work.
Why do you need
There's no need to add a target here because it will be inferred from your action. You can see this from how Anyway, what I think will be a lot more convenient for you is to use the functions in the
How should this case be handled? Sorry, I haven't taken the time to understand the issue and solution yet. At first glance, the way you check for .mli absence is correct.
As I said, the way you're handling missing interfaces seem correct. A couple of points that might clarify things for you:
|
08f57cd
to
e249f90
Compare
This is starting to work! The problem is now fixed in the case where there are explicit interface files (I added a separate test case for that). I'd like to use If the There is a similar problem with what the action will read, so I added the dependencies with There are still some problems:
|
OK, I re-read the issue and you're doing things the right way.
What should happen when there is no interface file? Can we add a test case for this situation perhaps? If you'd like to your test cases run in isolation, take a look at a test like
The way you're doing it is fine. I'd get rid of all the 1 off functions as they don't really have good names. Something like this: diff --git a/src/ocamldep.ml b/src/ocamldep.ml
index fdf3ea61..4172e4d1 100644
--- a/src/ocamldep.ml
+++ b/src/ocamldep.ml
@@ -120,12 +120,6 @@ let parse_deps ~dir ~file ~(unit : Module.t)
in
List.concat_map lines ~f:parse_line
-let will_read_this =
- Build.dyn_paths (Build.arr (fun x -> x))
-
-let will_write_to target =
- Build.action_dyn ~targets:[target] ()
-
let rules ~(ml_kind:Ml_kind.t) ~dir ~modules
?(already_used=Module.Name.Set.empty)
~alias_module ~lib_interface_module sctx =
@@ -164,15 +158,13 @@ let rules ~(ml_kind:Ml_kind.t) ~dir ~modules
in
paths
in
- let write paths =
- Action.with_stdout_to all_deps_file @@ Action.cat paths
- in
SC.add_rule sctx
( Build.lines_of ocamldep_output
>>^ build_paths
- >>> will_read_this
- >>^ write
- >>> will_write_to all_deps_file
+ >>> Build.dyn_paths (Build.arr (fun x -> x))
+ >>^ (fun paths ->
+ Action.with_stdout_to all_deps_file (Action.cat paths))
+ >>> Build.action_dyn ~targets:[all_deps_file] ()
)
end;
Build.memoize (Path.to_string all_deps_file) |
e249f90
to
c3d7dfb
Compare
@emillon btw, a signed CLA will be necessary before we can accept your code. If you don't mind, the details of the procedure are available here: https://janestreet.github.io/contributing.html Sorry for the hassle, but if we get this done in advance, then your valueable contribution will not be delayed. |
c3d7dfb
to
b5b1393
Compare
Just re-wrote the tests with subdirectories as you suggested. I don't know yet how to handle this subcase, unfortunately.
Done.
I am very surprised by this. I assumed that since this project moved under |
Changing the org in Github doesn't really change the legal status. That legal status, though, doesn't I hope cut against this being a community-oriented project. Note that contributing to OCaml itself also requires a CLA (though to a different organization), and until fairly recently, it was considered part of best practices for responsibly managing the licensing of an open-source project. And to be clear, the source code itself is licensed liberally, and there are contributors for a number of organizations. FWIW, we do hope to move to a new more lenient license and to a DCO, not just for Dune, but also for all of Jane Street's open source code. But until then, the CLA is still required. |
b5b1393
to
83ce859
Compare
When there is no mli, we should read the Adding a custom action seems fine to me, we already have specific actions that are not exposed to the user such as
The values computed by arrows are not memoized by default, so if a |
8c1f10f
to
63e7a83
Compare
I signed and sent the CLA.
Great, I did this and this made the second test pass! I had to ignore the case where the dependency is an alias. I'm not sure I understand why this is necessary, but removing this case makes bootstrap fail.
I implemented a first version of that. Next I'll try to see if I can move that into a custom action, which might remove the need for extending |
63e7a83
to
88cb057
Compare
I replaced the extended Let me know what you think. |
88cb057
to
51a98c3
Compare
src/ocamldep.ml
Outdated
in | ||
let paths = | ||
[ocamldep_output] | ||
@ List.filter_map dependencies ~f:mli_d_path |
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.
We must read the .mli.all-deps
files here
src/ocamldep.ml
Outdated
| None when Option.is_some alias_module -> None | ||
| None -> Module.file ~dir m Ml_kind.Impl | ||
in | ||
Option.map path ~f:ocamldep_output_path |
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 read the .all-deps
files, otherwise we are not computing the transitive dependencies. Doing this change should simplify the code a bit, as the .all-deps
no longer need to use the ocamldep syntax and can use whatever syntax we want
Thanks, I think if the complicated code is in |
51a98c3
to
7b43460
Compare
I changed so that it reads the corresponding |
What about changing |
7b43460
to
2aac534
Compare
Sure, I just changed it so that it's the initial value for the fold. It'd be great to be able to pass |
I guess it would be good to have a way to register custom actions, so that we could define this action in ocamldep.ml directly, but that's too big of a change for this PR I think. Could you update the code in ocamldep.ml to use this new argument rather than go through a temporary file? |
src/ocamldep.ml
Outdated
else | ||
Module.Name.Map.find modules m) | ||
in | ||
if check then |
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 rest of this function (the check and addition the alias_module) should go in parse_deps
. We only need to do these operations once when reading the output of ocamldep.
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.
Sure! I wasn't sure if the alias_module
part was relevant.
src/ocamldep.ml
Outdated
let path = | ||
match Module.file ~dir m Ml_kind.Intf with | ||
| Some x -> Some x | ||
| None when Option.is_some alias_module -> None |
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 had a look and I understand why we need this: alias_modules
is in modules
so we setup the rule to call ocamldep on it and given it's generated contents, it always reports dependencies on every other modules. Additionally, we manually add a dependency from every other module to alias_module
, so we end up with a circular dependency.
We didn't notice the issue before this PR as we manually override the binding for alias_module
just after (line 193). What we should do instead is add a test at the beginning of this function (line 140) to make sure we don't generate rules for alias_module
and just return Build.return []
instead. dependency_file_path
should return None
when m
is alias_module
.
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 moved that part to the main Module.Name.Map.map modules
call as you suggested. There's some duplication between the two Module.file
calls since they're both coupled to a match on alias_module
, but that might be early to extract an helper function.
2aac534
to
db61586
Compare
I believe that I took care of your remarks. Good call about the intermediate file, it's indeed not necessary if this data goes through |
db61586
to
9191606
Compare
Ok, I think we should indeed add an helper function |
This uses two different extensions: - `.d` corresponds to the raw `ocamldep` output. - `.all-deps` corresponds to this output, merged with the dependencies of all the interfaces mentioned in the earlier. This also means that `.all-deps` files will contain output from multiple files.
9191606
to
d282564
Compare
Done. Thanks for your comments! |
No problem, thanks for your contribution! |
This work in progress PR is an attempt to fix #660.
@diml let me know how we should do that. From what I understood, the plan is to compute transitive dependencies of modules, so in the linked bug, add a dependency from
Main
toLib_sub
?