-
Notifications
You must be signed in to change notification settings - Fork 410
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
Refactor library management #516
Conversation
This took much longer than I expected but it's now working. I think things are a bit cleaner now, in particular all the library resolution and management is done at a single place. Here is a summary of the changes: Lib moduleWe have a new module We also have library databases represented as A library database can have a parent database that is used to lookup names that are not found in the current database. In practice we have the following hierarchy:
The dependencies of a library are always resolved inside the DB it is part of. When we compute a transitive closure, we check that we don't have two libraries from two different DB with the same name. So for instance linting Base should now supported. Jbuild.Scope_info
Scope moduleThis replaces We no longer have an external scope or special anonymous scope. Instead one should use |
bin/main.ml
Outdated
@@ -758,7 +756,7 @@ let external_lib_deps = | |||
in | |||
let missing = | |||
String_map.filter externals ~f:(fun name _ -> | |||
not (Findlib.available context.findlib name)) | |||
not (Findlib.mem context.findlib name)) |
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 old name here a bit more. Since findlib does have a distinction between a package existing vs. being available (e.g. the exists_if stuff).
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, makes sense. This is the same for Lib.DB.mem actually
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.
done
x | ||
| exception exn -> | ||
m.state <- Unevaluated; | ||
reraise exn |
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 this some bug that's being fixed? It doesn't seem related to the lib refactoring.
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.
Yh, it just that it popped up in a test with this PR. Basically if evaluating a memoized Build.t failed, it was staying forever in Evaluating
state, causing a cycle error
src/odoc.ml
Outdated
match Lib.DB.find (Scope.libs scope) lib.name with | ||
| Error Not_found -> assert false | ||
| Error (Hidden _) -> | ||
(Utils.library_object_directory ~dir lib.name, |
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.
Would you mind explaining this case a bit? I'm not sure when an internal lib would be hidden. Also, it's still unsatisfying to go outside of the lib interface to get the object dir in that case.
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's for libraries that are optional with unavailable dependencies.
Also, it's still unsatisfying to go outside of the lib interface to get the object dir in that case.
Indeed. I suppose we could keep the Info.t
in this case
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.
done
Option.iter (Lib_db.Scope.find scope lib >>= Lib.src_dir) | ||
~f:(fun dir -> SC.load_dir sctx ~dir) | ||
match Lib.DB.find lib_db lib with | ||
| Error _ -> () |
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.
A bit off topic since that's the old behavior anyway, but why are we ignoring such errors? This cames seems impossible, so shouldn't it be assert false or a Sexp.code_error?
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 it's not impossible. We can reach this case with:
$ jbuilder build _build/default/_doc/blah/...
Currently, this will cause a no rule found for ...
error. Maybe we could return something more precise
val map : ('a, 'error) t -> f:('a -> 'b) -> ('b, 'error) t | ||
val bind : ('a, 'error) t -> f:('a -> ('b, 'error) t) -> ('b, 'error) t | ||
|
||
val map_error : ('a, 'error1) t -> f:('error1 -> 'error2) -> ('a, 'error2) 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.
Rather than extending this module, we should consider just vendoring rresult.ml I 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.
I think we should limit the vendoring to things to things that provide complex features or that are dependencies of things we vendor. Result
is a very simple module. Moreover the API conventions of rresult are quite different from the rest of the Dune codebase
Lib module ---------- We have a new module Lib that replaces Lib, parts of Lib_db and parts of Findlib. It is used to manage all libraries (internal and extrernal). Lib.t represent a completely resolved library, i.e. where all the dependencies have been resolved. Lib.Compile is used to provide what is necessary to build the library itself. Lib.Meta provides what is necessary to generate the META file for the library. We also have library databases represented as Lib.DB.t. A library database is simply a mapping from names to Lib.t values and and created from a resolve function that looks up a name and return a Lib.Info.t. A Lib.Info.t is the same as a Lib.t except that dependencies are not resolved. A library database can have a parent database that is used to lookup names that are not found in the current database. In practice we have the following hierarchy: 1. For every scope, we have a library database that holds all the libraries of this scope. In this DB, a library can be referred by either it's name or public name 2. the parent of each of these databases is a database that holds all the public libraries of the workspace. In this DB libraries must be referred by their public name 3. the parent of this DB is for installed libraries (1) databases are accessible via Scope.libs (Super_context.find_scope_by_{name,dir} sctx xxx) (2) is accessible via Super_context.public_libs sctx (3) is accessible via Super_context.installed_libs sctx The dependencies of a library are always resolved inside the DB it is part of. When we compute a transitive closure, we check that we don't have two libraries from two different DB with the same name. So for instance linting Base should now supported. Jbuild.Scope_info ----------------- Jbuild.Scope was renamed Jbuild.Scope_info Scope module ------------ This replaces Lib_db. A Scope.t is now just a pair of a Jbuild.Scope_info.t and a Lib.DB.t. Scope.DB.t is an object used to lookup scopes by either name or directory. We no longer have an external scope or special anonymous scope. Instead one should use Super_context.installed_libs or Super_context.public_libs depending on the context.
c0f1eec
to
527bb4f
Compare
No description provided.