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

Private libraries attached to a package #3655

Merged
merged 5 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ Unreleased
- Fix cram tests inside vendored directories not being interpreted correctly.
(@rgrinberg, #3860)

- Add `package` field to private libraries. This allows such libraries to be
installed and to be usable by other public libraries in the same project
(#3655, fixes #1017, @rgrinberg)

2.7.1 (2/09/2020)
-----------------

Expand Down
6 changes: 6 additions & 0 deletions doc/dune-files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,12 @@ to use the :ref:`include_subdirs` stanza.
want. The package name must be one of the packages that dune knows about,
as determined by the :ref:`opam-files`

- ``(package <package>)`` Install private library under the specified package.
Such a library is now usable by public libraries defined in the same project.
The findlib name for this library will be ``<package>.__private__.<name>``,
however the library's interface will be hidden from consumers outside the
project.

- ``(synopsis <string>)`` should give a one-line description of the library.
This is used by tools that list installed libraries

Expand Down
26 changes: 26 additions & 0 deletions src/dune_engine/lib_name.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
open Stdune

let private_key = "__private__"

module Local = struct
type t = string

Expand Down Expand Up @@ -67,6 +69,9 @@ module Local = struct
loop false 0
end) :
Stringlike_intf.S with type t := t )

let mangled_path_under_package local_name =
[ private_key; to_string local_name ]
end

let split t =
Expand All @@ -76,6 +81,13 @@ let split t =

let to_local = Local.of_string_user_error

let to_local_exn t =
match Local.of_string_opt t with
| Some s -> s
| None ->
Code_error.raise "invalid Lib_name.t -> Lib_name.Local.t conversion"
[ ("t", String t) ]

include Stringlike.Make (struct
type nonrec t = string

Expand All @@ -95,6 +107,20 @@ include Stringlike.Make (struct
| s -> Option.some_if (s.[0] <> '.') s
end)

type analyze =
| Public of Package.Name.t * string list
| Private of Package.Name.t * Local.t

let analyze t =
let pkg, rest = split t in
match rest with
| [ pkey; name ] when pkey = private_key -> Private (pkg, Local.of_string name)
| _ -> Public (pkg, rest)

let mangled pkg local_name =
let under_pkg = Local.mangled_path_under_package local_name in
Package.Name.to_string pkg :: under_pkg |> String.concat ~sep:"." |> of_string

let of_local (_loc, t) = t

let of_package_name p = Package.Name.to_string p
Expand Down
12 changes: 12 additions & 0 deletions src/dune_engine/lib_name.mli
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Local : sig

(** Description of valid library names *)
val valid_format_doc : User_message.Style.t Pp.t

val mangled_path_under_package : t -> string list
end

val compare : t -> t -> Ordering.t
Expand All @@ -21,12 +23,22 @@ val of_local : Loc.t * Local.t -> t

val to_local : Loc.t * t -> (Local.t, User_message.t) result

val to_local_exn : t -> Local.t

val split : t -> Package.Name.t * string list

val package_name : t -> Package.Name.t

val of_package_name : Package.Name.t -> t

type analyze =
| Public of Package.Name.t * string list
| Private of Package.Name.t * Local.t

val analyze : t -> analyze

val mangled : Package.Name.t -> Local.t -> t

module Map : Map.S with type key = t

module Set : sig
Expand Down
9 changes: 5 additions & 4 deletions src/dune_rules/compilation_context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ module SC = Super_context
module Includes = struct
type t = Command.Args.dynamic Command.Args.t Cm_kind.Dict.t

let make ~opaque ~requires : _ Cm_kind.Dict.t =
let make ~project ~opaque ~requires : _ Cm_kind.Dict.t =
match requires with
| Error exn ->
Cm_kind.Dict.make_all (Command.Args.Fail { fail = (fun () -> raise exn) })
| Ok libs ->
let iflags = Lib.L.include_flags libs in
let iflags = Lib.L.include_flags ~project libs in
let cmi_includes =
Command.Args.memo
(Command.Args.S
Expand Down Expand Up @@ -118,8 +118,9 @@ let create ~super_context ~scope ~expander ~obj_dir ~modules ~flags
~requires_compile ~requires_link ?(preprocessing = Preprocessing.dummy)
~opaque ?stdlib ~js_of_ocaml ~dynlink ~package ?vimpl ?modes
?(bin_annot = true) () =
let project = Scope.project scope in
let requires_compile =
if Dune_project.implicit_transitive_deps (Scope.project scope) then
if Dune_project.implicit_transitive_deps project then
Lazy.force requires_link
else
requires_compile
Expand All @@ -146,7 +147,7 @@ let create ~super_context ~scope ~expander ~obj_dir ~modules ~flags
; flags
; requires_compile
; requires_link
; includes = Includes.make ~opaque ~requires:requires_compile
; includes = Includes.make ~project ~opaque ~requires:requires_compile
; preprocessing
; opaque
; stdlib
Expand Down
70 changes: 51 additions & 19 deletions src/dune_rules/dune_file.ml
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ module Library = struct

type visibility =
| Public of Public_lib.t
| Private
| Private of Package.t option

type t =
{ name : Loc.t * Lib_name.Local.t
Expand Down Expand Up @@ -609,6 +609,10 @@ module Library = struct
field_o "instrumentation.backend"
( Dune_lang.Syntax.since Stanza.syntax (2, 7)
>>> fields (field "ppx" (located Lib_name.decode)) )
and+ package =
field_o "package"
( Dune_lang.Syntax.since Stanza.syntax (2, 8)
>>> located Stanza_common.Pkg.decode )
in
let wrapped =
Wrapped.make ~wrapped ~implements ~special_builtin_support
Expand Down Expand Up @@ -646,9 +650,17 @@ module Library = struct
]
in
let visibility =
match public with
| None -> Private
| Some public -> Public public
match (public, package) with
| None, None -> Private None
| Some public, None -> Public public
| None, Some (_loc, package) -> Private (Some package)
| Some public, Some (loc, _) ->
User_error.raise ~loc
[ Pp.textf
"This library has a pullic_name, it already belongs to the \
package %s"
(Package.Name.to_string public.package.name)
]
in
Option.both virtual_modules implements
|> Option.iter ~f:(fun (virtual_modules, (_, impl)) ->
Expand Down Expand Up @@ -694,12 +706,15 @@ module Library = struct
let package t =
match t.visibility with
| Public p -> Some p.package
| Private -> None
| Private p -> p

let sub_dir t =
match t.visibility with
| Public p -> p.sub_dir
| Private -> None
| Private None -> None
| Private (Some _) ->
Lib_name.Local.mangled_path_under_package (snd t.name)
|> String.concat ~sep:"/" |> Option.some

let has_foreign t = Buildable.has_foreign t.buildable

Expand All @@ -723,17 +738,24 @@ module Library = struct

let best_name t =
match t.visibility with
| Private -> Lib_name.of_local t.name
| Private _ -> Lib_name.of_local t.name
| Public p -> snd p.name

let is_virtual t = Option.is_some t.virtual_modules

let is_impl t = Option.is_some t.implements

let obj_dir ~dir t =
let private_lib =
match t.visibility with
| Private (Some _) -> true
| Private None
| Public _ ->
false
in
Obj_dir.make_lib ~dir
~has_private_modules:(t.private_modules <> None)
(snd t.name)
~private_lib (snd t.name)

let main_module_name t : Lib_info.Main_module_name.t =
match (t.implements, t.wrapped) with
Expand Down Expand Up @@ -772,7 +794,7 @@ module Library = struct
in
let status =
match conf.visibility with
| Private -> Lib_info.Status.Private conf.project
| Private pkg -> Lib_info.Status.Private (conf.project, pkg)
| Public p -> Public (conf.project, p.package)
in
let virtual_library = is_virtual conf in
Expand Down Expand Up @@ -835,6 +857,7 @@ module Library = struct
let version =
match status with
| Public (_, pkg) -> pkg.version
| Installed_private
| Installed
| Private _ ->
None
Expand Down Expand Up @@ -1895,22 +1918,31 @@ module Library_redirect = struct
module Local = struct
type nonrec t = (Loc.t * Lib_name.Local.t) t

let for_lib (lib : Library.t) ~new_public_name ~loc : t =
{ loc; new_public_name; old_name = lib.name; project = lib.project }

let of_private_lib (lib : Library.t) : t option =
match lib.visibility with
| Public _
| Private None ->
None
| Private (Some package) ->
let loc, name = lib.name in
let new_public_name = (loc, Lib_name.mangled package.name name) in
Some (for_lib lib ~loc ~new_public_name)

let of_lib (lib : Library.t) : t option =
let open Option.O in
let* public =
let* public_name =
match lib.visibility with
| Public p -> Some p
| Private -> None
| Public plib -> Some plib.name
| Private _ -> None
in
if Lib_name.equal (Lib_name.of_local lib.name) (snd public.name) then
if Lib_name.equal (Lib_name.of_local lib.name) (snd public_name) then
None
else
Some
{ loc = Loc.none
; project = lib.project
; old_name = lib.name
; new_public_name = public.name
}
let loc = fst public_name in
Some (for_lib lib ~loc ~new_public_name:public_name)
end
end

Expand Down
4 changes: 3 additions & 1 deletion src/dune_rules/dune_file.mli
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ end
module Library : sig
type visibility =
| Public of Public_lib.t
| Private
| Private of Package.t option

type t =
{ name : Loc.t * Lib_name.Local.t
Expand Down Expand Up @@ -365,6 +365,8 @@ module Library_redirect : sig

module Local : sig
type nonrec t = (Loc.t * Lib_name.Local.t) t

val of_private_lib : Library.t -> t option
end
end

Expand Down
6 changes: 5 additions & 1 deletion src/dune_rules/dune_package.ml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ module Lib = struct
let info : Path.t Lib_info.t =
let src_dir = Obj_dir.dir obj_dir in
let enabled = Lib_info.Enabled_status.Normal in
let status = Lib_info.Status.Installed in
let status =
match Lib_name.analyze name with
| Private (_, _) -> Lib_info.Status.Installed_private
| Public (_, _) -> Lib_info.Status.Installed
in
let version = None in
let main_module_name = Lib_info.Inherited.This main_module_name in
let foreign_objects = Lib_info.Source.External foreign_objects in
Expand Down
6 changes: 5 additions & 1 deletion src/dune_rules/findlib/findlib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,11 @@ end = struct
let kind = kind t in
let sub_systems = Sub_system_name.Map.empty in
let synopsis = description t in
let status = Lib_info.Status.Installed in
let status =
match Lib_name.analyze t.name with
| Private (_, _) -> Lib_info.Status.Installed_private
| Public (_, _) -> Lib_info.Status.Installed
in
let src_dir = Obj_dir.dir obj_dir in
let version = version t in
let dune_version = None in
Expand Down
37 changes: 30 additions & 7 deletions src/dune_rules/gen_meta.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ module Pub_name = struct
| Dot of t * string
| Id of string

let parse s =
let s = Lib_name.to_string s in
match String.split s ~on:'.' with
let of_list = function
| [] -> assert false
| x :: l ->
let rec loop acc l =
Expand All @@ -20,6 +18,10 @@ module Pub_name = struct
in
loop (Id x) l

let of_lib_name s =
let pkg, xs = Lib_name.split s in
of_list (Package.Name.to_string pkg :: xs)

let rec root = function
| Dot (t, _) -> root t
| Id n -> n
Expand Down Expand Up @@ -157,18 +159,39 @@ let gen ~(package : Package.t) ~add_directory_entry entries =
List.map entries ~f:(fun (e : Super_context.Lib_entry.t) ->
match e with
| Library lib -> (
let name = Lib.Local.info lib |> Lib_info.name in
let pub_name = Pub_name.parse name in
let info = Lib.Local.info lib in
let pub_name =
let name = Lib_info.name info in
Pub_name.of_lib_name name
in
match Pub_name.to_list pub_name with
| [] -> assert false
| _package :: path ->
| package :: path ->
let pub_name, path =
match Lib_info.status info with
| Private (_, None) ->
(* Not possible b/c we wouldn't be generating a META file for a
private library without a package. *)
assert false
| Private (_, Some pkg) ->
assert (path = []);
let path =
Lib_name.Local.mangled_path_under_package
(Lib_name.Local.of_string package)
in
let pub_name =
Pub_name.of_list (Package.Name.to_string pkg.name :: path)
in
(pub_name, path)
| _ -> (pub_name, path)
in
(pub_name, gen_lib pub_name ~path (Lib.Local.to_lib lib) ~version) )
| Deprecated_library_name
{ old_name = old_public_name, _
; new_public_name = _, new_public_name
; _
} ->
( Pub_name.parse (Dune_file.Public_lib.name old_public_name)
( Pub_name.of_lib_name (Dune_file.Public_lib.name old_public_name)
, version @ [ requires (Lib_name.Set.singleton new_public_name) ] ))
in
let pkgs =
Expand Down
Loading