|
1 | 1 | open Import
|
2 | 2 |
|
| 3 | +module Dune_version = struct |
| 4 | + type t = |
| 5 | + | Release of int * int * int |
| 6 | + | Preview of int * int * int |
| 7 | + |
| 8 | + let parse_release_version version_str = |
| 9 | + let extract_int s = |
| 10 | + let digits = String.filter s ~f:Char.is_digit in |
| 11 | + if String.is_empty digits then None else Some (Int.of_string digits) |
| 12 | + in |
| 13 | + match String.split_on_chars ~on:[ '.' ] version_str with |
| 14 | + | [ major; minor; patch ] -> |
| 15 | + (match extract_int major, extract_int minor, extract_int patch with |
| 16 | + | Some major, Some minor, Some patch_num -> |
| 17 | + Some (Release (major, minor, patch_num)) |
| 18 | + | _ -> None) |
| 19 | + | _ -> None |
| 20 | + ;; |
| 21 | + |
| 22 | + let parse_preview_version rest = |
| 23 | + match String.split_on_chars ~on:[ ',' ] rest with |
| 24 | + | timestamp_str :: _ -> |
| 25 | + (try |
| 26 | + Stdlib.Scanf.sscanf |
| 27 | + (String.strip timestamp_str) |
| 28 | + "%d-%d-%dT%d:%d:%dZ" |
| 29 | + (fun y m d _h _min _s -> Some (Preview (y, m, d))) |
| 30 | + with |
| 31 | + | Stdlib.Scanf.Scan_failure _ | End_of_file -> None) |
| 32 | + | _ -> None |
| 33 | + ;; |
| 34 | + |
| 35 | + let from_string str = |
| 36 | + let prefix = "\"Dune Developer Preview: build " in |
| 37 | + if String.is_prefix str ~prefix |
| 38 | + then ( |
| 39 | + let rest = String.drop_prefix str (String.length prefix) in |
| 40 | + match parse_preview_version rest with |
| 41 | + | Some version -> Some version |
| 42 | + | None -> parse_release_version str) |
| 43 | + else parse_release_version str |
| 44 | + ;; |
| 45 | + |
| 46 | + (* Released versions >= 3.20.0 and preview versions with a timestamp on or |
| 47 | + after 2025-07-30 support everything needed for VSCode to support DPM, |
| 48 | + e.g. `dune lock`, `dune tools exec`, support of various Dune dev tools etc. |
| 49 | + The last feature needed was implemented on 2025-07-29 and ensures that |
| 50 | + other Dune dev tools (in particular ocamlformat) are included in the PATH |
| 51 | + of the `ocamllsp` process. *) |
| 52 | + let is_valid version = |
| 53 | + match version with |
| 54 | + | Release (major, minor, patch) -> |
| 55 | + Stdlib.compare (major, minor, patch) (3, 20, 0) >= 0 |
| 56 | + | Preview (y, m, d) -> Stdlib.compare (y, m, d) (2025, 7, 30) >= 0 |
| 57 | + ;; |
| 58 | +end |
| 59 | + |
3 | 60 | type t =
|
4 | 61 | { root : Path.t
|
5 | 62 | ; bin : Cmd.spawn
|
6 | 63 | }
|
7 | 64 |
|
8 | 65 | let binary = Path.of_string "dune"
|
9 | 66 |
|
10 |
| -let make root () = |
11 |
| - let open Promise.Syntax in |
12 |
| - (* Should we do something specific for Windows ? *) |
13 |
| - match root with |
14 |
| - | Some root -> |
15 |
| - let spawn = { Cmd.bin = binary; args = [] } in |
16 |
| - let+ spawn = Cmd.check_spawn spawn in |
17 |
| - (match spawn with |
18 |
| - | Ok bin -> Some { bin; root } |
19 |
| - | Error _ -> None) |
20 |
| - | None -> Promise.return None |
21 |
| -;; |
22 |
| - |
23 | 67 | let is_project_locked t =
|
24 | 68 | (* Path to the dune.lock dir *)
|
25 | 69 | let dune_lock_path = Path.join t.root (Path.of_string "dune.lock") in
|
@@ -50,3 +94,56 @@ let exec_tool ~tool ?(args = []) t =
|
50 | 94 |
|
51 | 95 | let equal d1 d2 = Path.equal d1.root d2.root
|
52 | 96 | let root t = t.root
|
| 97 | + |
| 98 | +let make root () = |
| 99 | + let open Promise.Syntax in |
| 100 | + let dune_version_output bin root = |
| 101 | + command { root; bin } ~args:[ "--version" ] |> Cmd.output ~cwd:root |
| 102 | + in |
| 103 | + (* Should we do something specific for Windows ? *) |
| 104 | + match root with |
| 105 | + | Some root -> |
| 106 | + let spawn = { Cmd.bin = binary; args = [] } in |
| 107 | + let* spawn = Cmd.check_spawn spawn in |
| 108 | + (match spawn with |
| 109 | + | Ok bin -> |
| 110 | + let+ dune_version_output = dune_version_output bin root in |
| 111 | + (match dune_version_output with |
| 112 | + | Ok v -> |
| 113 | + (match Dune_version.from_string v with |
| 114 | + | Some version when Dune_version.is_valid version -> |
| 115 | + show_message |
| 116 | + `Info |
| 117 | + "Dune Package Management selected with dune from %s, version %s." |
| 118 | + (Path.to_string binary) |
| 119 | + v; |
| 120 | + Some { bin; root } |
| 121 | + | _ -> None) |
| 122 | + | Error _err -> None) |
| 123 | + | Error _err -> |
| 124 | + let* check_if_dune_is_installed_with_opam = |
| 125 | + command |
| 126 | + { root; bin = { Cmd.bin = Path.of_string "opam"; args = [] } } |
| 127 | + ~args:[ "exec"; "--"; "which"; "dune" ] |
| 128 | + |> Cmd.output ~cwd:root |
| 129 | + in |
| 130 | + (match check_if_dune_is_installed_with_opam with |
| 131 | + | Error _err -> Promise.return None |
| 132 | + | Ok path when String.is_empty path -> Promise.return None |
| 133 | + | Ok path -> |
| 134 | + let bin = { Cmd.bin = Path.of_string path; args = [] } in |
| 135 | + let* dune_version_output = dune_version_output bin root in |
| 136 | + (match dune_version_output with |
| 137 | + | Ok v -> |
| 138 | + (match Dune_version.from_string v with |
| 139 | + | Some version when Dune_version.is_valid version -> |
| 140 | + show_message |
| 141 | + `Info |
| 142 | + "Dune Package Management selected with dune from %s, version %s." |
| 143 | + path |
| 144 | + v; |
| 145 | + Promise.return (Some { bin; root }) |
| 146 | + | _ -> Promise.return None) |
| 147 | + | Error _err -> Promise.return None))) |
| 148 | + | None -> Promise.return None |
| 149 | +;; |
0 commit comments