Skip to content

Commit

Permalink
Merge pull request #5171 from cannorin/opam-tree
Browse files Browse the repository at this point in the history
Add `opam tree` subcommand
  • Loading branch information
kit-ty-kate authored Sep 12, 2022
2 parents 559143c + eece100 commit 6eeffc7
Show file tree
Hide file tree
Showing 20 changed files with 1,218 additions and 45 deletions.
20 changes: 20 additions & 0 deletions doc/man/opam-topics.inc
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,24 @@
(with-stdout-to opam-show.1 (run %{bin:opam} show --help=groff)))
(diff opam-show.err %{dep:opam-show.0}))))

(rule
(with-stdout-to opam-why.0 (echo "")))
(rule
(targets opam-why.1 opam-why.err)
(deps using-built-opam)
(action (progn (with-stderr-to opam-why.err
(with-stdout-to opam-why.1 (run %{bin:opam} why --help=groff)))
(diff opam-why.err %{dep:opam-why.0}))))

(rule
(with-stdout-to opam-tree.0 (echo "")))
(rule
(targets opam-tree.1 opam-tree.err)
(deps using-built-opam)
(action (progn (with-stderr-to opam-tree.err
(with-stdout-to opam-tree.1 (run %{bin:opam} tree --help=groff)))
(diff opam-tree.err %{dep:opam-tree.0}))))

(rule
(with-stdout-to opam-search.0 (echo "")))
(rule
Expand Down Expand Up @@ -271,6 +289,8 @@
opam-install.1
opam-info.1
opam-show.1
opam-why.1
opam-tree.1
opam-search.1
opam-list.1
opam-init.1))
12 changes: 12 additions & 0 deletions master_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ users)
* [BUG] Fix spaces in root and switch dirs [#5203 @jonahbeckford]
* Use menu for init setup [#5057 @AltGr; #5217 @dra27]
* Do not show --yes and --no as special global options when using cmdliner >= 1.1 [#5269 @kit-ty-kate]
* ◈ Add `tree` subcommand to display a dependency tree of currently installed packages [#5171 @cannorin - fix #3775]
* ◈ Add `why` subcommand to examine how the versions of currently installed packages get constrained (alias to `tree --rev-deps`) [#5171 @cannorin - fix #3775]

## Plugins
*
Expand Down Expand Up @@ -333,6 +335,8 @@ users)
* Add some tests for --best-effort to avoid further regressions when trying to install specific versions of packages [@5261 @kit-ty-kate]
* Add unhelpful conflict error message test [#5270 @kit-ty-kate]
* Add rebuild test [#5258 @rjbou]
* Add test for opam tree command [#5171 @cannorin]

### Engine
* Add `opam-cat` to normalise opam file printing [#4763 @rjbou @dra27] [2.1.0~rc2 #4715]
* Fix meld reftest: open only with failing ones [#4913 @rjbou]
Expand Down Expand Up @@ -433,6 +437,9 @@ users)
* `OpamArgTools`: all flag definition takes now a section as a labelled argument [#5275 @rjbou]
* `OpamArg`: all flag definition takes now a section as an optional argument, default is set to `Manpage.s_options` [#5275 @rjbou]

* Add `OpamTreeCommand` [#5171 @cannorin]
* `OpamSolution`: add `dry_run` to simulate the new switch state after applying a solution [#5171 @cannorin]

## opam-repository
* `OpamRepositoryConfig`: add in config record `repo_tarring` field and as an argument to config functions, and a new constructor `REPOSITORYTARRING` in `E` environment module and its access function [#5015 @rjbou]
* New download functions for shared source, old ones kept [#4893 @rjbou]
Expand Down Expand Up @@ -489,6 +496,8 @@ users)
* `OpamFile.OPAM`: Add `locked`, file origin and extension, in the record with its modifiers/getter [#5080 @rjbou]
* `OpamFile.OPAM.effective_part`: empty extra-source url if checksum is specified and take first one (as for url) [#5258 @kit-ty-kate]
* `OpamFile.OPAM.effectively_equal`: return true if an extra-source url changes but not its checksum (as for url) [#5258 @kit-ty-kate]
* `OpamFormula`: add generic `formula_to_cnf` and `formula_to_dnf`, and use them in `to_cnf` and `to_dnf` [#5171 @cannorin]
* `OpamFilter`: add `?custom` argument in `to_string` to tweak the output [#5171 @cannorin]

## opam-core
* OpamSystem: avoid calling Unix.environment at top level [#4789 @hannesm]
Expand All @@ -512,3 +521,6 @@ users)
* `OpamStd.Sys`: add `all_shells` list of all supported shells [#5217 @dra27]
* `OpamUrl`: add `to_string_w_subpath` to display subpath inside urls (before hash) [#5219 @rjbou]
* `OpamFilename.SubPath`: remove `pretty_string` in favor to `OpamUrl.to_string_w_subpath` [#5219 @rjbou]
* `OpamConsole`: add a `Tree` submodule to draw a unicode/ascii-art tree [#5171 @cannorin]
* `OpamStd.List`: add `find_map_opt` (for ocaml < 4.10) and `fold_left_map` (for ocaml < 4.11) [#5171 @cannorin]
* `OpamCompat`: add `Int.equal` (for ocaml < 4.12)
114 changes: 114 additions & 0 deletions src/client/opamCommands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,118 @@ let list ?(force_search=false) cli =
$no_switch $depexts $vars $repos $owns_file $disjunction $search
$silent $no_depexts $package_listing cli $pattern_list)

(* TREE *)
let tree_doc = "Draw the dependency forest of installed packages."
let tree ?(why=false) cli =
let doc = tree_doc in
let build_docs = "TREE BUILDING OPTIONS" in
let filter_docs = "TREE FILTERING OPTIONS" in
let selection_docs = OpamArg.package_selection_section in
let display_docs = OpamArg.package_listing_section in
let man = [
`S Manpage.s_description;
`P "This command displays the forest of currently installed \
packages as a Unicode/ASCII-art.";
`P "Without argument, it draws the dependency forest of packages with no \
dependants. With packages arguments, draws the forest of the specified \
packages. When non-installed packages are specified in the arguments, \
it will try to simulate installing them before drawing the forest.";
`P "When the $(b,--rev-deps) option is used, it draws the \
reverse-dependency forest instead. Without argument, draws the forest \
of packages with no dependencies. With packages arguments, draws the \
forest of the specified packages. Note that non-installed packages are \
ignored when this option is used.";
`P ("When a package appears twice or more in the forest, the second or \
later occurrences of the said package will be marked as a duplicate, \
and be annotated with the $(i,"^OpamTreeCommand.duplicate_symbol^") \
symbol. Any sub-trees rooted from such duplicates will be truncated \
to avoid redundancy.");
`P ("See section $(b,"^filter_docs^") and $(b,"^selection_docs^") for all \
the ways to select the packages to be displayed, and section \
$(b,"^display_docs^") to customise the output format.");
`P "For a flat list of packages which may not be installed, \
see $(b,opam list).";
`S Manpage.s_arguments;
`S build_docs;
`S filter_docs;
`P "These options only take effect when $(i,PACKAGES) are present.";
`S selection_docs;
`S display_docs;
] in
let mode =
let default = OpamTreeCommand.(if why then ReverseDeps else Deps) in
mk_vflag default ~cli ~section:build_docs [
cli_from cli2_2, OpamTreeCommand.Deps, ["deps"],
"Draw a dependency forest, starting from the packages not required by \
any other packages (this is the default).";
cli_from cli2_2, OpamTreeCommand.ReverseDeps, ["rev-deps"],
"Draw a reverse-dependency forest, starting from the packages which \
have no dependencies.";
]
in
let filter =
let default = OpamTreeCommand.Roots_from in
mk_vflag default ~cli ~section:filter_docs [
cli_from cli2_2, OpamTreeCommand.Roots_from, ["roots-from"],
"Display only the trees which roots from one of the $(i,PACKAGES) \
(this is the default).";
cli_from cli2_2, OpamTreeCommand.Leads_to, ["leads-to"],
"Display only the branches which leads to one of the $(i,PACKAGES).";
]
in
let post =
mk_flag ~cli (cli_from cli2_2) ["post"] ~section:selection_docs
"Include dependencies tagged as $(i,post)."
in
let dev =
mk_flag ~cli (cli_from cli2_2) ["dev"] ~section:selection_docs
"Include development packages in dependencies."
in
let doc_flag =
mk_flag ~cli (cli_from cli2_2) ["doc";"with-doc"] ~section:selection_docs
"Include doc-only dependencies."
in
let test =
mk_flag ~cli (cli_from cli2_2) ["t";"test";"with-test"]
~section:selection_docs
"Include test-only dependencies."
in
let tools =
mk_flag ~cli (cli_from cli2_2) ["with-tools"] ~section:selection_docs
"Include development only dependencies."
in
let no_cstr =
mk_flag ~cli (cli_from cli2_2) ["no-constraint"] ~section:display_docs
"Do not display the version constraints e.g. $(i,(>= 1.0.0))."
in
let no_switch =
mk_flag ~cli (cli_from cli2_2) ["no-switch"] ~section:selection_docs
"Ignore active switch and simulate installing packages from an empty \
switch to draw the forest"
in
let tree global_options mode filter post dev doc test tools no_constraint
no_switch names () =
if names = [] && no_switch then
`Error
(true, "--no-switch can't be used without specifying a name")
else
(apply_global_options cli global_options;
OpamGlobalState.with_ `Lock_none @@ fun gt ->
OpamSwitchState.with_ `Lock_none gt @@ fun st ->
let tog = OpamListCommand.{
post; test; doc; dev; tools;
recursive = false;
depopts = false;
build = true;
} in
OpamTreeCommand.run st tog ~no_constraint ~no_switch mode filter names;
`Ok ())
in
mk_command_ret ~cli (cli_from cli2_2) "tree" ~doc ~man
Term.(const tree $global_options cli $mode $filter
$post $dev $doc_flag $test $tools
$no_cstr $no_switch
$name_list)

(* SHOW *)
let show_doc = "Display information about specific packages."
Expand Down Expand Up @@ -4192,6 +4304,8 @@ let commands cli =
init cli;
list cli;
make_command_alias ~cli (list ~force_search:true cli) ~options:" --search" "search";
tree cli;
make_command_alias ~cli (tree ~why:true cli) ~options:" --rev-deps" "why";
show; make_command_alias ~cli show "info";
install cli;
remove; make_command_alias ~cli remove "uninstall";
Expand Down
3 changes: 3 additions & 0 deletions src/client/opamSolution.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,9 @@ let simulate_new_state state t =
t state.installed in
{ state with installed }

let dry_run state solution =
simulate_new_state state (OpamSolver.get_atomic_action_graph solution)

(* Ask confirmation whenever the packages to modify are not exactly
the packages in the user request *)
let confirmation ?ask requested solution =
Expand Down
4 changes: 4 additions & 0 deletions src/client/opamSolution.mli
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ val check_solution:
(solution_result, 'conflict) result ->
unit

(** Simulate the new [switch_state] after applying the [solution]
without actually performing the action(s) on disk. *)
val dry_run: 'a switch_state -> OpamSolver.solution -> 'a switch_state

(* Install external dependencies of the given package set, according the depext
configuration. If [confirm] is false, install commands are directly
launched, without asking user (used by the `--depext-only` option). If
Expand Down
Loading

0 comments on commit 6eeffc7

Please sign in to comment.