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

Refactor library management #516

Merged
1 commit merged into from
Feb 20, 2018
Merged

Refactor library management #516

1 commit merged into from
Feb 20, 2018

Conversation

ghost
Copy link

@ghost ghost commented Feb 14, 2018

No description provided.

@ghost ghost mentioned this pull request Feb 15, 2018
11 tasks
@ghost
Copy link
Author

ghost commented Feb 17, 2018

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 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.

@ghost ghost requested a review from rgrinberg February 17, 2018 12:22
@ghost ghost changed the title [WIP] refactor library management Refactor library management Feb 19, 2018
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))
Copy link
Member

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).

Copy link
Author

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

Copy link
Author

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
Copy link
Member

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.

Copy link
Author

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,
Copy link
Member

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.

Copy link
Author

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

Copy link
Author

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 _ -> ()
Copy link
Member

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?

Copy link
Author

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
Copy link
Member

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.

Copy link
Author

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.
@ghost ghost force-pushed the refactor-lib-management branch from c0f1eec to 527bb4f Compare February 20, 2018 11:23
@ghost ghost merged commit 9e4cc84 into master Feb 20, 2018
@ghost ghost mentioned this pull request Feb 22, 2018
@emillon emillon deleted the refactor-lib-management branch December 13, 2018 16:04
This pull request was closed.
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