package bistro
A library to build and run distributed scientific workflows
Install
Dune Dependency
Authors
Maintainers
Sources
bistro-0.6.0.tbz
sha256=146177faaaa9117a8e2bf0fd60cb658662c0aa992f35beb246e6fd0766050e66
sha512=553fe0c20f236316449b077a47e6e12626d193ba1916e9da233e5526dd39090e8677277e1c79baace3bdc940cb009f25431730a8efc00ae4ed9cc42a0add9609
doc/src/bistro.engine/shell_command.ml.html
Source file shell_command.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
open Core open Bistro_internals type file_dump = File_dump of { text : string ; path : string ; } type symbolic_file_dump = Symbolic_file_dump of { contents : Execution_env.insert Template.t ; } type t = Command of { text : Execution_env.insert Command.t ; env : Execution_env.t ; container : [ `Docker_container of Workflow.Docker_image.t | `Singularity_container of Workflow.container_image ] option ; } let container_env (Command { container ; env ; text = _ }) = match container with | None -> env | Some (`Docker_container _) -> Execution_env.dockerize env | Some (`Singularity_container _) -> Execution_env.singularize env let rec file_dumps_of_tokens toks = List.map toks ~f:file_dumps_of_token |> List.concat |> List.dedup_and_sort ~compare:Caml.compare and file_dumps_of_token = let open Template in function | NP | DEST | TMP | S _ | D _ | MEM -> [] | F contents -> Symbolic_file_dump { contents = contents ; } :: file_dumps_of_tokens contents |> List.dedup_and_sort ~compare:Caml.compare let rec file_dumps_of_command = let open Command in function | Simple_command toks -> file_dumps_of_tokens toks | And_list xs | Or_list xs | Pipe_list xs -> List.map xs ~f:file_dumps_of_command |> List.concat |> List.dedup_and_sort ~compare:Caml.compare let string_of_token (env : Execution_env.t) = let open Template in function | S s -> s | D (Execution_env.Path p) -> env.dep p | D (Execution_env.Path_list { elts ; quote ; sep }) -> let quote = Option.value_map quote ~default:Fn.id ~f:(fun c p -> sprintf "%c%s%c" c p c) in List.map elts ~f:env.dep |> List.map ~f:quote |> String.concat ~sep | D (String s) -> s | F toks -> env.file_dump toks | DEST -> env.dest | TMP -> env.tmp | NP -> string_of_int env.np | MEM -> string_of_int env.mem let string_of_tokens env xs = List.map ~f:(string_of_token env) xs |> String.concat let compile_file_dump (env : Execution_env.t) (container_env : Execution_env.t) (Symbolic_file_dump { contents }) = let path = env.file_dump contents in let text = string_of_tokens container_env contents in File_dump { path ; text } let file_dumps (Command cmd as c) = file_dumps_of_command cmd.text |> List.map ~f:(compile_file_dump cmd.env (container_env c)) let par x = "( " ^ x ^ " )" (* spaces after '(' and before ')' are essential here, to prevent '(( ))' which has a specific meaning for bash *) let command_path_deps cmd = Command.deps cmd ~compare:Execution_env.compare_insert |> List.filter_map ~f:(function | Execution_env.Path p -> Some [ p ] | Path_list l -> Some l.elts | String _ -> None ) |> List.concat module Mounts = struct type t = { host_paths : string list ; container_paths : string list ; } let of_pair (host_paths, container_paths) = { host_paths ; container_paths ; } let deps ~env (Command cmd) = let open Execution_env in command_path_deps cmd.text |> List.map ~f:(Execution_env.container_mount env.db) |> List.map ~f:Execution_env.(fun m -> m.mount_host_location, m.mount_container_location) |> List.dedup_and_sort ~compare:Caml.compare |> List.unzip |> of_pair let file_dumps env container_env (Command cmd) = let open Execution_env in let file_dumps = file_dumps_of_command cmd.text in let f env (Symbolic_file_dump { contents = fd ; _ }) = env.file_dump fd in { host_paths = List.map file_dumps ~f:(f env) ; container_paths = List.map file_dumps ~f:(f container_env) ; } let tmp env container_env = let open Execution_env in { host_paths = [env.tmp] ; container_paths = [container_env.tmp] ; } let script script = { host_paths = [script] ; container_paths = [script] ; } let dest env container_env = let open Execution_env in { host_paths = Filename.[ dirname env.dest ] ; container_paths = Filename.[ dirname container_env.dest ] ; } let docker_opt { host_paths ; container_paths } = Docker.mount_options ~host_paths ~container_paths end let singularity_mounts (env : Execution_env.t) (container_env : Execution_env.t) cmd script_fn = let binding (m : Mounts.t) = List.map2_exn m.host_paths m.container_paths ~f:(fun hp cp -> sprintf "%s:%s" hp cp ) in Mounts.[ deps ~env cmd ; dest env container_env ; script script_fn ; file_dumps env container_env cmd ; tmp env container_env ; ] |> List.concat_map ~f:binding |> String.concat ~sep:"," let rec string_of_command env = let open Command in function | Simple_command tokens -> string_of_tokens env tokens | And_list xs -> par (string_of_command_aux env " && " xs) | Or_list xs -> par (string_of_command_aux env " || " xs) | Pipe_list xs -> par (string_of_command_aux env " | " xs) and string_of_command_aux env sep xs = List.map xs ~f:(string_of_command env) |> String.concat ~sep let text (Command cmd as c) = string_of_command (container_env c) cmd.text let make env img cmd = let container = Execution_env.choose_container env.Execution_env.allowed_containers img in Command { text = cmd ; env ; container ; } let write_file_dumps xs = let f (File_dump { text ; path }) = Lwt_io.(with_file ~mode:output path (fun oc -> write oc text)) in Lwt_list.iter_p f xs let uses_docker (Command cmd) = match cmd.container with | None | Some (`Singularity_container _) -> false | Some (`Docker_container _) -> true let invocation (Command cmd as c) script = match cmd.container with | None -> sprintf "/bin/bash %s" script | Some (`Docker_container image) -> let container_env = container_env c in sprintf "docker run --log-driver=none --rm %s %s %s %s %s -i %s /bin/bash %s" (Mounts.deps ~env:cmd.env c |> Mounts.docker_opt) (Mounts.file_dumps cmd.env container_env c |> Mounts.docker_opt) (Mounts.tmp cmd.env container_env |> Mounts.docker_opt) (Mounts.dest cmd.env container_env |> Mounts.docker_opt) (Mounts.script script |> Mounts.docker_opt) (Docker.image_url image) script | Some (`Singularity_container image) -> let container_env = container_env c in sprintf "singularity exec --no-home -B %s %s /bin/bash '%s'" (singularity_mounts cmd.env container_env c script) (Db.singularity_image cmd.env.Execution_env.db image) script let run (Command cmd as c) = let open Lwt in let script_file = Filename.concat cmd.env.tmp_dir "script.sh" in let invocation = invocation c script_file in Misc.remove_if_exists cmd.env.tmp_dir >>= fun () -> Unix.mkdir_p cmd.env.tmp ; Out_channel.write_all (Filename.concat cmd.env.tmp_dir "run.sh") ~data:invocation ; write_file_dumps (file_dumps c) >>= fun () -> Lwt_io.(with_file ~mode:output script_file (fun oc -> write oc (text c))) >>= fun () -> Misc.redirection cmd.env.stdout >>= fun stdout -> Misc.redirection cmd.env.stderr >>= fun stderr -> Lwt_process.exec ~stdout ~stderr (Lwt_process.shell invocation) >>= fun status -> (* Lwt_unix.unlink script_file >>= fun () -> *) let exit_code = Caml.Unix.( match status with | WEXITED code | WSIGNALED code | WSTOPPED code -> code ) in let dest_exists = match Sys.file_exists cmd.env.dest with `Yes -> true | `Unknown | `No -> false in ( if Execution_env.allows_docker cmd.env && uses_docker c then ( Misc.docker_chown ~path:cmd.env.tmp_dir ~uid:cmd.env.uid >>= fun () -> if dest_exists then Misc.docker_chown ~path:cmd.env.dest ~uid:cmd.env.uid else Lwt.return () ) else Lwt.return () ) >>= fun () -> Lwt.return (exit_code, dest_exists) let container (Command cmd) = cmd.container
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>