diff --git a/bin/pkg/pkg_common.ml b/bin/pkg/pkg_common.ml index 3ba8fef3496..62829688b7e 100644 --- a/bin/pkg/pkg_common.ml +++ b/bin/pkg/pkg_common.ml @@ -60,6 +60,32 @@ module Per_context = struct |> Dune_pkg.Pkg_workspace.Repository.Name.Map.of_list_exn ;; + let make_solver workspace context_common ~version_preference_arg ~lock = + let lock_dir_path = Option.value lock ~default:Dune_pkg.Lock_dir.default_path in + let lock_dir = Workspace.find_lock_dir workspace lock_dir_path in + let solver_sys_vars = + Option.bind lock_dir ~f:(fun lock_dir -> lock_dir.solver_sys_vars) + in + let version_preference_context = + Option.bind lock_dir ~f:(fun lock_dir -> lock_dir.version_preference) + in + let repositories = + Option.map lock_dir ~f:(fun lock_dir -> lock_dir.repositories) + |> Option.value + ~default:[ Dune_pkg.Pkg_workspace.Repository.Name.of_string "default" ] + in + { lock_dir_path + ; version_preference = + Version_preference.choose + ~from_arg:version_preference_arg + ~from_context:version_preference_context + ; context_common + ; solver_sys_vars + ; repositories + ; repos = repositories_of_workspace workspace + } + ;; + let choose ~context_name_arg ~all_contexts_arg ~version_preference_arg = let open Fiber.O in match context_name_arg, all_contexts_arg with @@ -82,26 +108,8 @@ module Per_context = struct "Unknown build context: %s" (Dune_engine.Context_name.to_string context_name |> String.maybe_quoted) ] - | Some - (Default - { lock - ; version_preference = version_preference_context - ; solver_sys_vars - ; repositories - ; base = context_common - ; _ - }) -> - [ { lock_dir_path = Option.value lock ~default:Lock_dir.default_path - ; version_preference = - Version_preference.choose - ~from_arg:version_preference_arg - ~from_context:version_preference_context - ; solver_sys_vars - ; repositories - ; context_common - ; repos = repositories_of_workspace workspace - } - ] + | Some (Default { lock; base = context_common; _ }) -> + [ make_solver workspace context_common ~version_preference_arg ~lock ] | Some (Opam _) -> User_error.raise [ Pp.textf @@ -111,25 +119,8 @@ module Per_context = struct | None, true -> let+ workspace = Memo.run (Workspace.workspace ()) in List.filter_map workspace.contexts ~f:(function - | Workspace.Context.Default - { lock - ; version_preference = version_preference_context - ; base = context_common - ; solver_sys_vars - ; repositories - } -> - let lock_dir_path = Option.value lock ~default:Dune_pkg.Lock_dir.default_path in - Some - { lock_dir_path - ; version_preference = - Version_preference.choose - ~from_arg:version_preference_arg - ~from_context:version_preference_context - ; context_common - ; solver_sys_vars - ; repositories - ; repos = repositories_of_workspace workspace - } + | Workspace.Context.Default { lock; base = context_common } -> + Some (make_solver workspace context_common ~version_preference_arg ~lock) | Opam _ -> None) ;; end diff --git a/src/dune_rules/context.ml b/src/dune_rules/context.ml index 8a6d9100456..d511501690e 100644 --- a/src/dune_rules/context.ml +++ b/src/dune_rules/context.ml @@ -599,8 +599,7 @@ module Group = struct match context with | Opam { base; switch } -> create_for_opam builder ~switch ~loc:base.loc ~targets:base.targets - | Default - { lock; version_preference = _; solver_sys_vars = _; repositories = _; base } -> + | Default { lock; base } -> let builder = match builder.findlib_toolchain with | Some _ -> builder diff --git a/src/dune_rules/workspace.ml b/src/dune_rules/workspace.ml index 3e4cbee4f67..57f6d309598 100644 --- a/src/dune_rules/workspace.ml +++ b/src/dune_rules/workspace.ml @@ -2,6 +2,73 @@ open Import open Dune_lang.Decoder module Repository = Dune_pkg.Pkg_workspace.Repository +module Lock_dir = struct + type t = + { path : Path.Source.t + ; version_preference : Dune_pkg.Version_preference.t option + ; solver_sys_vars : Dune_pkg.Solver_env.Variable.Sys.Bindings.t option + ; repositories : Dune_pkg.Pkg_workspace.Repository.Name.t list + } + + let to_dyn { path; version_preference; solver_sys_vars; repositories } = + Dyn.record + [ "path", Path.Source.to_dyn path + ; ( "version_preference" + , Dyn.option Dune_pkg.Version_preference.to_dyn version_preference ) + ; ( "solver_sys_vars" + , Dyn.option Dune_pkg.Solver_env.Variable.Sys.Bindings.to_dyn solver_sys_vars ) + ; ( "repositories" + , Dyn.list Dune_pkg.Pkg_workspace.Repository.Name.to_dyn repositories ) + ] + ;; + + let hash { path; version_preference; solver_sys_vars; repositories } = + Poly.hash (path, version_preference, solver_sys_vars, repositories) + ;; + + let equal { path; version_preference; solver_sys_vars; repositories } t = + Path.Source.equal path t.path + && Option.equal + Dune_pkg.Version_preference.equal + version_preference + t.version_preference + && Option.equal + Dune_pkg.Solver_env.Variable.Sys.Bindings.equal + solver_sys_vars + t.solver_sys_vars + && List.equal Dune_pkg.Pkg_workspace.Repository.Name.equal repositories t.repositories + ;; + + let decode = + let repositories_of_ordered_set ordered_set = + Dune_lang.Ordered_set_lang.eval + ordered_set + ~parse:(fun ~loc string -> + Dune_pkg.Pkg_workspace.Repository.Name.parse_string_exn (loc, string)) + ~eq:Dune_pkg.Pkg_workspace.Repository.Name.equal + ~standard:[ Dune_pkg.Pkg_workspace.Repository.Name.of_string "default" ] + in + let decode = + let+ path = + field_o "path" (Dune_lang.Path.Local.decode ~dir:Path.root) + >>| function + | None -> Path.Source.(relative root "dune.lock") + | Some p -> Path.as_in_source_tree_exn p + and+ solver_sys_vars = + field_o "solver_sys_vars" Dune_pkg.Solver_env.Variable.Sys.Bindings.decode + and+ version_preference = + field_o "version_preference" Dune_pkg.Version_preference.decode + and+ repositories = Dune_lang.Ordered_set_lang.field "repositories" in + { path + ; solver_sys_vars + ; version_preference + ; repositories = repositories_of_ordered_set repositories + } + in + fields decode + ;; +end + (* workspace files use the same version numbers as dune-project files for simplicity *) let syntax = Stanza.syntax @@ -280,33 +347,14 @@ module Context = struct type t = { base : Common.t ; lock : Path.Source.t option - ; version_preference : Dune_pkg.Version_preference.t option - ; solver_sys_vars : Dune_pkg.Solver_env.Variable.Sys.Bindings.t option - ; repositories : Dune_pkg.Pkg_workspace.Repository.Name.t list } - let to_dyn { base; lock; version_preference; solver_sys_vars; repositories } = + let to_dyn { base; lock } = Dyn.record - [ "base", Common.to_dyn base - ; "lock", Dyn.(option Path.Source.to_dyn) lock - ; ( "version_preference" - , Dyn.option Dune_pkg.Version_preference.to_dyn version_preference ) - ; ( "solver_sys_vars" - , Dyn.option Dune_pkg.Solver_env.Variable.Sys.Bindings.to_dyn solver_sys_vars ) - ; ( "repositories" - , Dyn.list Dune_pkg.Pkg_workspace.Repository.Name.to_dyn repositories ) - ] + [ "base", Common.to_dyn base; "lock", Dyn.(option Path.Source.to_dyn) lock ] ;; let decode = - let repositories_of_ordered_set ordered_set = - Dune_lang.Ordered_set_lang.eval - ordered_set - ~parse:(fun ~loc string -> - Dune_pkg.Pkg_workspace.Repository.Name.parse_string_exn (loc, string)) - ~eq:Dune_pkg.Pkg_workspace.Repository.Name.equal - ~standard:[ Dune_pkg.Pkg_workspace.Repository.Name.of_string "default" ] - in let+ common = Common.decode and+ name = field_o "name" (Dune_lang.Syntax.since syntax (1, 10) >>> Context_name.decode) @@ -316,12 +364,7 @@ module Context = struct 2. allow external paths *) field_o "lock" (Dune_lang.Path.Local.decode ~dir:(Path.source Path.Source.root)) - and+ version_preference = - field_o "version_preference" Dune_pkg.Version_preference.decode - and+ solver_sys_vars = - field_o "solver_sys_vars" Dune_pkg.Solver_env.Variable.Sys.Bindings.decode - and+ repositories_osl = Dune_lang.Ordered_set_lang.field "repositories" in - let repositories = repositories_of_ordered_set repositories_osl in + in let lock = Option.map lock ~f:Path.as_in_source_tree_exn in fun ~profile_default ~instrument_with_default ~x -> let common = common ~profile_default ~instrument_with_default in @@ -332,24 +375,11 @@ module Context = struct in let name = Option.value ~default name in let base = { common with targets = Target.add common.targets x; name } in - { base; lock; version_preference; solver_sys_vars; repositories } + { base; lock } ;; - let equal { base; lock; version_preference; solver_sys_vars; repositories } t = - Common.equal base t.base - && Option.equal Path.Source.equal lock t.lock - && Option.equal - Dune_pkg.Version_preference.equal - version_preference - t.version_preference - && Option.equal - Dune_pkg.Solver_env.Variable.Sys.Bindings.equal - solver_sys_vars - t.solver_sys_vars - && List.equal - Dune_pkg.Pkg_workspace.Repository.Name.equal - repositories - t.repositories + let equal { base; lock } t = + Common.equal base t.base && Option.equal Path.Source.equal lock t.lock ;; end @@ -413,9 +443,6 @@ module Context = struct let default ~x ~profile ~instrument_with = Default { lock = None - ; version_preference = None - ; solver_sys_vars = None - ; repositories = [ Dune_pkg.Pkg_workspace.Repository.Name.of_string "default" ] ; base = { loc = Loc.of_pos __POS__ ; targets = [ Option.value x ~default:Target.Native ] @@ -451,9 +478,10 @@ type t = ; env : Dune_env.Stanza.t option ; config : Dune_config.t ; repos : Dune_pkg.Pkg_workspace.Repository.t list + ; lock_dirs : Lock_dir.t list } -let to_dyn { merlin_context; contexts; env; config; repos } = +let to_dyn { merlin_context; contexts; env; config; repos; lock_dirs } = let open Dyn in record [ "merlin_context", option Context_name.to_dyn merlin_context @@ -461,24 +489,31 @@ let to_dyn { merlin_context; contexts; env; config; repos } = ; "env", option Dune_env.Stanza.to_dyn env ; "config", Dune_config.to_dyn config ; "repos", list Repository.to_dyn repos + ; "solver", (list Lock_dir.to_dyn) lock_dirs ] ;; -let equal { merlin_context; contexts; env; config; repos } w = +let equal { merlin_context; contexts; env; config; repos; lock_dirs } w = Option.equal Context_name.equal merlin_context w.merlin_context && List.equal Context.equal contexts w.contexts && Option.equal Dune_env.Stanza.equal env w.env && Dune_config.equal config w.config && List.equal Repository.equal repos w.repos + && List.equal Lock_dir.equal lock_dirs w.lock_dirs ;; -let hash { merlin_context; contexts; env; config; repos } = +let hash { merlin_context; contexts; env; config; repos; lock_dirs } = Poly.hash ( Option.hash Context_name.hash merlin_context , List.hash Context.hash contexts , Option.hash Dune_env.Stanza.hash env , Dune_config.hash config - , List.hash Repository.hash repos ) + , List.hash Repository.hash repos + , List.hash Lock_dir.hash lock_dirs ) +;; + +let find_lock_dir t path = + List.find t.lock_dirs ~f:(fun lock_dir -> Path.Source.equal lock_dir.path path) ;; include Dune_lang.Versioned_file.Make (struct @@ -628,7 +663,8 @@ let step1 clflags = "instrument_with" (lazy_ (Dune_lang.Syntax.since Stanza.syntax (2, 7) >>> repeat Lib_name.decode)) ~default:(lazy [])) - and+ config_from_workspace_file = Dune_config.decode_fields_of_workspace_file in + and+ config_from_workspace_file = Dune_config.decode_fields_of_workspace_file + and+ lock_dirs = multi_field "lock_dir" Lock_dir.decode in let+ contexts = multi_field "context" (lazy_ Context.decode) in let config = create_final_config @@ -694,7 +730,13 @@ let step1 clflags = then Some Context_name.default else None in - { merlin_context; contexts = top_sort (List.rev contexts); env; config; repos }) + { merlin_context + ; contexts = top_sort (List.rev contexts) + ; env + ; config + ; repos + ; lock_dirs + }) in { Step1.t; config } ;; @@ -724,6 +766,7 @@ let default clflags = ; env = None ; config ; repos = [ Repository.default ] + ; lock_dirs = [] } ;; diff --git a/src/dune_rules/workspace.mli b/src/dune_rules/workspace.mli index 209d0409b60..03fcc45e760 100644 --- a/src/dune_rules/workspace.mli +++ b/src/dune_rules/workspace.mli @@ -2,6 +2,18 @@ open Import +module Lock_dir : sig + type t = + { path : Path.Source.t + ; version_preference : Dune_pkg.Version_preference.t option + ; solver_sys_vars : Dune_pkg.Solver_env.Variable.Sys.Bindings.t option + ; repositories : Dune_pkg.Pkg_workspace.Repository.Name.t list + } + + val equal : t -> t -> bool + val to_dyn : t -> Dyn.t +end + module Context : sig module Target : sig type t = @@ -47,9 +59,6 @@ module Context : sig type t = { base : Common.t ; lock : Path.Source.t option - ; version_preference : Dune_pkg.Version_preference.t option - ; solver_sys_vars : Dune_pkg.Solver_env.Variable.Sys.Bindings.t option - ; repositories : Dune_pkg.Pkg_workspace.Repository.Name.t list } end @@ -81,11 +90,13 @@ type t = private ; env : Dune_env.Stanza.t option ; config : Dune_config.t ; repos : Dune_pkg.Pkg_workspace.Repository.t list + ; lock_dirs : Lock_dir.t list } val equal : t -> t -> bool val to_dyn : t -> Dyn.t val hash : t -> int +val find_lock_dir : t -> Path.Source.t -> Lock_dir.t option module Clflags : sig type t = diff --git a/test/blackbox-tests/test-cases/pkg/env-conditional-dependencies.t b/test/blackbox-tests/test-cases/pkg/env-conditional-dependencies.t index 397fd7d2058..e1a717d4268 100644 --- a/test/blackbox-tests/test-cases/pkg/env-conditional-dependencies.t +++ b/test/blackbox-tests/test-cases/pkg/env-conditional-dependencies.t @@ -29,18 +29,22 @@ dependencies are included. Create a workspace config that defines separate build contexts for macos and linux. $ cat >dune-workspace < (lang dune 3.8) + > (lock_dir + > (path dune.linux.lock) + > (solver_sys_vars + > (os linux))) + > (lock_dir + > (path dune.macos.lock) + > (solver_sys_vars + > (os macos))) > (context > (default > (name linux) - > (lock dune.linux.lock) - > (solver_sys_vars - > (os linux)))) + > (lock dune.linux.lock))) > (context > (default > (name macos) - > (lock dune.macos.lock) - > (solver_sys_vars - > (os macos)))) + > (lock dune.macos.lock))) > EOF Now the os-specific dependencies are included on their respective systems. diff --git a/test/blackbox-tests/test-cases/pkg/git-repo.t b/test/blackbox-tests/test-cases/pkg/git-repo.t index 0e3ca09449e..aeba91c289d 100644 --- a/test/blackbox-tests/test-cases/pkg/git-repo.t +++ b/test/blackbox-tests/test-cases/pkg/git-repo.t @@ -15,13 +15,14 @@ We'll set up a project that uses (only this) this repository, so doesn't use $ cat > dune-workspace < (lang dune 3.10) + > (lock_dir + > (repositories mock)) > (repository > (name mock) > (source "git+file://$PWD/mock-opam-repository")) > (context > (default - > (name default) - > (repositories mock))) + > (name default))) > EOF We depend on the foo package diff --git a/test/blackbox-tests/test-cases/pkg/just-print-solver-env.t b/test/blackbox-tests/test-cases/pkg/just-print-solver-env.t index 7f83722db2d..275748cd80f 100644 --- a/test/blackbox-tests/test-cases/pkg/just-print-solver-env.t +++ b/test/blackbox-tests/test-cases/pkg/just-print-solver-env.t @@ -13,22 +13,26 @@ Print the solver env when no dune-workspace is present Add some build contexts with different environments $ cat >dune-workspace < (lang dune 3.8) + > (lock_dir + > (path dune.linux.lock) + > (solver_sys_vars + > (os linux))) + > (lock_dir + > (path dune.linux.no-doc.lock) + > (solver_sys_vars + > (arch x86_64) + > (os linux) + > (os-family ubuntu) + > (os-distribution ubuntu) + > (os-version 22.04))) > (context > (default > (name linux) - > (lock dune.linux.lock) - > (solver_sys_vars - > (os linux)))) + > (lock dune.linux.lock))) > (context > (default > (name no-doc) - > (lock dune.linux.lock) - > (solver_sys_vars - > (arch x86_64) - > (os linux) - > (os-family ubuntu) - > (os-distribution ubuntu) - > (os-version 22.04)))) + > (lock dune.linux.no-doc.lock))) > EOF $ dune pkg print-solver-env --all-contexts --dont-poll-system-solver-variables diff --git a/test/blackbox-tests/test-cases/pkg/lock-per-context.t/run.t b/test/blackbox-tests/test-cases/pkg/lock-per-context.t/run.t index ea063e6a22e..217c4fdbfd5 100644 --- a/test/blackbox-tests/test-cases/pkg/lock-per-context.t/run.t +++ b/test/blackbox-tests/test-cases/pkg/lock-per-context.t/run.t @@ -1,18 +1,22 @@ Create a workspace with multiple contexts, each specifying a lockdir name. $ cat >dune-workspace < (lang dune 3.8) + > (lock_dir + > (path foo.lock) + > (version_preference newest)) ; this is the default + > (lock_dir + > (path prefers_oldest.lock) + > (version_preference oldest)) > (context > (default > (lock foo.lock))) > (context > (default > (name foo) - > (version_preference newest) ; this is the default > (lock bar.lock))) > (context > (default > (name prefers_oldest) - > (version_preference oldest) > (lock prefers_oldest.lock))) > (context > (opam diff --git a/test/blackbox-tests/test-cases/pkg/multiple-opam-repos.t b/test/blackbox-tests/test-cases/pkg/multiple-opam-repos.t index 624b6a2113c..970bd1ddf10 100644 --- a/test/blackbox-tests/test-cases/pkg/multiple-opam-repos.t +++ b/test/blackbox-tests/test-cases/pkg/multiple-opam-repos.t @@ -29,16 +29,14 @@ We have to define both repositories in the workspace, but will only use `new`. $ cat > dune-workspace < (lang dune 3.10) + > (lock_dir + > (repositories new)) > (repository > (name new) > (source "git+file://$(pwd)/mock-opam-repository")) > (repository > (name old) > (source "git+file://$(pwd)/old-mock-opam-repository")) - > (context - > (default - > (name default) - > (repositories new))) > EOF $ cat > dune-project < dune-workspace < (lang dune 3.10) + > (lock_dir + > (repositories old)) > (repository > (name new) > (source "git+file://$(pwd)/mock-opam-repository")) @@ -71,8 +71,7 @@ solution: > (source "git+file://$(pwd)/old-mock-opam-repository")) > (context > (default - > (name default) - > (repositories old))) + > (name default))) > EOF $ rm -r dune-workspace-cache && mkdir dune-workspace-cache @@ -85,6 +84,8 @@ package: $ cat > dune-workspace < (lang dune 3.10) + > (lock_dir + > (repositories old new)) > (repository > (name new) > (source "git+file://$(pwd)/mock-opam-repository")) @@ -93,8 +94,7 @@ package: > (source "git+file://$(pwd)/old-mock-opam-repository")) > (context > (default - > (name default) - > (repositories new old))) + > (name default))) > EOF $ rm -r dune-workspace-cache && mkdir dune-workspace-cache @@ -114,10 +114,11 @@ older version of foo: > (repository > (name old) > (source "git+file://$(pwd)/old-mock-opam-repository")) + > (lock_dir + > (repositories new old \ new)) > (context > (default - > (name default) - > (repositories new old \ new))) + > (name default))) > EOF $ rm -r dune-workspace-cache && mkdir dune-workspace-cache diff --git a/test/blackbox-tests/test-cases/pkg/opam-repository-download.t b/test/blackbox-tests/test-cases/pkg/opam-repository-download.t index df58f568968..14c6e99992f 100644 --- a/test/blackbox-tests/test-cases/pkg/opam-repository-download.t +++ b/test/blackbox-tests/test-cases/pkg/opam-repository-download.t @@ -65,10 +65,11 @@ The repository can also be injected via the dune-workspace file > (repository > (name foo) > (source "git+file://$(pwd)/mock-opam-repository")) + > (lock_dir + > (repositories foo)) > (context > (default - > (name default) - > (repositories foo))) + > (name default))) > EOF $ mkdir dune-workspace-cache $ XDG_CACHE_HOME=$(pwd)/dune-workspace-cache dune pkg lock diff --git a/test/blackbox-tests/test-cases/pkg/rev-store-lock-linux.t b/test/blackbox-tests/test-cases/pkg/rev-store-lock-linux.t index cc938a524de..bf01f6e35bf 100644 --- a/test/blackbox-tests/test-cases/pkg/rev-store-lock-linux.t +++ b/test/blackbox-tests/test-cases/pkg/rev-store-lock-linux.t @@ -17,10 +17,12 @@ Thus we first create a repo: > (repository > (name mock) > (source "git+file://$(pwd)/mock-opam-repository")) + > (lock_dir + > (repositories mock)) > (context > (default > (name default) - > (repositories mock))) + > (lock dune.lock))) > EOF We set the project up to depend on `foo` diff --git a/test/blackbox-tests/test-cases/pkg/rev-store-lock.t b/test/blackbox-tests/test-cases/pkg/rev-store-lock.t index 1f967ed7068..7beaca7b259 100644 --- a/test/blackbox-tests/test-cases/pkg/rev-store-lock.t +++ b/test/blackbox-tests/test-cases/pkg/rev-store-lock.t @@ -16,13 +16,15 @@ We set this repository as sole source for opam repositories. $ cat > dune-workspace < (lang dune 3.10) + > (lock_dir + > (repositories mock)) > (repository > (name mock) > (source "git+file://$(pwd)/mock-opam-repository")) > (context > (default > (name default) - > (repositories mock))) + > (lock dune.lock))) > EOF We set the project up to depend on `foo` diff --git a/test/blackbox-tests/test-cases/pkg/solver-vars-in-lockdir-metadata.t b/test/blackbox-tests/test-cases/pkg/solver-vars-in-lockdir-metadata.t index d1c83655924..dd5049dcb5b 100644 --- a/test/blackbox-tests/test-cases/pkg/solver-vars-in-lockdir-metadata.t +++ b/test/blackbox-tests/test-cases/pkg/solver-vars-in-lockdir-metadata.t @@ -88,12 +88,14 @@ Solve packages with no variables set. Make a workspace file which sets some of the variables. $ cat >dune-workspace < (lang dune 3.8) + > (lock_dir + > (path dune.lock) + > (solver_sys_vars + > (os linux) + > (arch arm))) > (context > (default - > (name default) - > (solver_sys_vars - > (os linux) - > (arch arm)))) + > (name default))) > EOF Solve the packages again, this time with the variables set. diff --git a/test/blackbox-tests/test-cases/pkg/unavailable-packages.t b/test/blackbox-tests/test-cases/pkg/unavailable-packages.t index fb86670c465..cfc993d13d9 100644 --- a/test/blackbox-tests/test-cases/pkg/unavailable-packages.t +++ b/test/blackbox-tests/test-cases/pkg/unavailable-packages.t @@ -4,15 +4,19 @@ Set up two build contexts: a default one for linux and another for macos. $ cat >dune-workspace < (lang dune 3.8) - > (context (default + > (lock_dir + > (path dune.lock) > (solver_sys_vars - > (os linux)))) + > (os linux))) + > (lock_dir + > (path dune.macos.lock) + > (solver_sys_vars + > (os macos))) + > (context (default)) > (context > (default > (name macos) - > (lock dune.macos.lock) - > (solver_sys_vars - > (os macos)))) + > (lock dune.macos.lock))) > EOF !! Do not delete this one for the one in helpers.sh as it passes --context !!