|
| 1 | +(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P. |
| 2 | + * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript |
| 3 | + * This program is free software: you can redistribute it and/or modify |
| 4 | + * it under the terms of the GNU Lesser General Public License as published by |
| 5 | + * the Free Software Foundation, either version 3 of the License, or |
| 6 | + * (at your option) any later version. |
| 7 | + * |
| 8 | + * In addition to the permissions granted to you by the LGPL, you may combine |
| 9 | + * or link a "work that uses the Library" with a publicly distributed version |
| 10 | + * of this file to produce a combined library or application, then distribute |
| 11 | + * that combined work under the terms of your choosing, with no requirement |
| 12 | + * to comply with the obligations normally placed on you by section 4 of the |
| 13 | + * LGPL version 3 (or the corresponding section of a later version of the LGPL |
| 14 | + * should you choose to use a later version). |
| 15 | + * |
| 16 | + * This program is distributed in the hope that it will be useful, |
| 17 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | + * GNU Lesser General Public License for more details. |
| 20 | + * |
| 21 | + * You should have received a copy of the GNU Lesser General Public License |
| 22 | + * along with this program; if not, write to the Free Software |
| 23 | + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) |
| 24 | + |
| 25 | +(* let get_list_string = Bsb_build_util.get_list_string *) |
| 26 | +let ( // ) = Ext_path.combine |
| 27 | + |
| 28 | +let resolve_package cwd package_name = |
| 29 | + let x = Bsb_pkg.resolve_bs_package ~cwd package_name in |
| 30 | + { |
| 31 | + Bsb_config_types.package_name; |
| 32 | + package_install_path = x // Bsb_config.lib_ocaml; |
| 33 | + } |
| 34 | + |
| 35 | +type json_map = Ext_json_types.t Map_string.t |
| 36 | + |
| 37 | +(* Key is the path *) |
| 38 | +let ( |? ) m (key, cb) = m |> Ext_json.test key cb |
| 39 | +let ( .?() ) = Map_string.find_opt |
| 40 | + |
| 41 | +(*TODO: it is a little mess that [cwd] and [project dir] are shared*) |
| 42 | + |
| 43 | +let extract_package_name_and_namespace (map : json_map) : string * string option |
| 44 | + = |
| 45 | + let package_name = |
| 46 | + match map.?(Bsb_build_schemas.name) with |
| 47 | + | Some (Str { str = "_" } as config) -> |
| 48 | + Bsb_exception.config_error config "_ is a reserved package name" |
| 49 | + | Some (Str { str = name }) -> name |
| 50 | + | Some config -> |
| 51 | + Bsb_exception.config_error config "name expect a string field" |
| 52 | + | None -> Bsb_exception.invalid_spec "field name is required" |
| 53 | + in |
| 54 | + let namespace = |
| 55 | + match map.?(Bsb_build_schemas.namespace) with |
| 56 | + | None | Some (False _) -> None |
| 57 | + | Some (True _) -> |
| 58 | + Some (Ext_namespace.namespace_of_package_name package_name) |
| 59 | + | Some (Str { str }) -> |
| 60 | + (*TODO : check the validity of namespace *) |
| 61 | + Some (Ext_namespace.namespace_of_package_name str) |
| 62 | + | Some x -> |
| 63 | + Bsb_exception.config_error x "namespace field expects string or boolean" |
| 64 | + in |
| 65 | + (package_name, namespace) |
| 66 | + |
| 67 | +(** |
| 68 | + There are two things to check: |
| 69 | + - the running bsb and vendoring bsb is the same |
| 70 | + - the running bsb need delete stale build artifacts |
| 71 | + (kinda check npm upgrade) |
| 72 | +
|
| 73 | + Note if the setup is correct: |
| 74 | + the running compiler and node_modules/rescript |
| 75 | + should be the same version, |
| 76 | + The exact check is that the running compiler should have a |
| 77 | + compatible runtime version installed, the location of the |
| 78 | + compiler is actually not relevant. |
| 79 | + We disable the check temporarily |
| 80 | + e.g, |
| 81 | + ``` |
| 82 | + bsc -runtime runtime_dir@version |
| 83 | + ``` |
| 84 | +*) |
| 85 | +let check_stdlib (map : json_map) : bool = |
| 86 | + (*built_in_package*) |
| 87 | + match map.?(Bsb_build_schemas.use_stdlib) with |
| 88 | + | Some (False _) -> false |
| 89 | + | None | Some _ -> true |
| 90 | + |
| 91 | +let extract_gentype_config (map : json_map) : Bsb_config_types.gentype_config = |
| 92 | + match map.?(Bsb_build_schemas.gentypeconfig) with |
| 93 | + | None -> false |
| 94 | + | Some (Obj _) -> true |
| 95 | + | Some config -> |
| 96 | + Bsb_exception.config_error config "gentypeconfig expect an object" |
| 97 | + |
| 98 | +let extract_uncurried (map : json_map) : bool = |
| 99 | + match map.?(Bsb_build_schemas.uncurried) with |
| 100 | + | None -> false |
| 101 | + | Some (True _) -> true |
| 102 | + | Some (False _) -> false |
| 103 | + | Some config -> |
| 104 | + Bsb_exception.config_error config "uncurried expects one of: true, false." |
| 105 | + |
| 106 | +let extract_string (map : json_map) (field : string) cb = |
| 107 | + match map.?(field) with |
| 108 | + | None -> None |
| 109 | + | Some (Str { str }) -> cb str |
| 110 | + | Some config -> Bsb_exception.config_error config (field ^ " expect a string") |
| 111 | + |
| 112 | +let extract_boolean (map : json_map) (field : string) (default : bool) : bool = |
| 113 | + match map.?(field) with |
| 114 | + | None -> default |
| 115 | + | Some (True _) -> true |
| 116 | + | Some (False _) -> false |
| 117 | + | Some config -> |
| 118 | + Bsb_exception.config_error config (field ^ " expect a boolean") |
| 119 | + |
| 120 | +let extract_reason_react_jsx (map : json_map) = |
| 121 | + let default : Bsb_config_types.reason_react_jsx option ref = ref None in |
| 122 | + map |
| 123 | + |? ( Bsb_build_schemas.reason, |
| 124 | + `Obj |
| 125 | + (fun m -> |
| 126 | + match m.?(Bsb_build_schemas.react_jsx) with |
| 127 | + | Some (Flo { loc; flo }) -> ( |
| 128 | + match flo with |
| 129 | + | "3" -> default := Some Jsx_v3 |
| 130 | + | _ -> Bsb_exception.errorf ~loc "Unsupported jsx version %s" flo |
| 131 | + ) |
| 132 | + | Some x -> |
| 133 | + Bsb_exception.config_error x |
| 134 | + "Unexpected input (expect a version number) for jsx, note \ |
| 135 | + boolean is no longer allowed" |
| 136 | + | None -> ()) ) |
| 137 | + |> ignore; |
| 138 | + !default |
| 139 | + |
| 140 | +let extract_warning (map : json_map) = |
| 141 | + match map.?(Bsb_build_schemas.warnings) with |
| 142 | + | None -> Bsb_warning.use_default |
| 143 | + | Some (Obj { map }) -> Bsb_warning.from_map map |
| 144 | + | Some config -> Bsb_exception.config_error config "expect an object" |
| 145 | + |
| 146 | +let extract_ignored_dirs (map : json_map) : Set_string.t = |
| 147 | + match map.?(Bsb_build_schemas.ignored_dirs) with |
| 148 | + | None -> Set_string.empty |
| 149 | + | Some (Arr { content }) -> |
| 150 | + Set_string.of_list (Bsb_build_util.get_list_string content) |
| 151 | + | Some config -> Bsb_exception.config_error config "expect an array of string" |
| 152 | + |
| 153 | +let extract_generators (map : json_map) = |
| 154 | + let generators = ref Map_string.empty in |
| 155 | + (match map.?(Bsb_build_schemas.generators) with |
| 156 | + | None -> () |
| 157 | + | Some (Arr { content = s }) -> |
| 158 | + generators := |
| 159 | + Ext_array.fold_left s Map_string.empty (fun acc json -> |
| 160 | + match json with |
| 161 | + | Obj { map = m; loc } -> ( |
| 162 | + match |
| 163 | + (m.?(Bsb_build_schemas.name), m.?(Bsb_build_schemas.command)) |
| 164 | + with |
| 165 | + | Some (Str { str = name }), Some (Str { str = command }) -> |
| 166 | + Map_string.add acc name command |
| 167 | + | _, _ -> |
| 168 | + Bsb_exception.errorf ~loc |
| 169 | + {| generators exepect format like { "name" : "cppo", "command" : "cppo $in -o $out"} |} |
| 170 | + ) |
| 171 | + | _ -> acc) |
| 172 | + | Some config -> |
| 173 | + Bsb_exception.config_error config |
| 174 | + (Bsb_build_schemas.generators ^ " expect an array field")); |
| 175 | + !generators |
| 176 | + |
| 177 | +let extract_dependencies (map : json_map) cwd (field : string) : |
| 178 | + Bsb_config_types.dependencies = |
| 179 | + match map.?(field) with |
| 180 | + | None -> [] |
| 181 | + | Some (Arr { content = s }) -> |
| 182 | + Ext_list.map (Bsb_build_util.get_list_string s) (fun s -> |
| 183 | + resolve_package cwd (Bsb_pkg_types.string_as_package s)) |
| 184 | + | Some config -> Bsb_exception.config_error config (field ^ " expect an array") |
| 185 | + |
| 186 | +(* return an empty array if not found *) |
| 187 | +let extract_string_list (map : json_map) (field : string) : string list = |
| 188 | + match map.?(field) with |
| 189 | + | None -> [] |
| 190 | + | Some (Arr { content = s }) -> Bsb_build_util.get_list_string s |
| 191 | + | Some config -> Bsb_exception.config_error config (field ^ " expect an array") |
| 192 | + |
| 193 | +let extract_ppx (map : json_map) (field : string) ~(cwd : string) : |
| 194 | + Bsb_config_types.ppx list = |
| 195 | + match map.?(field) with |
| 196 | + | None -> [] |
| 197 | + | Some (Arr { content }) -> |
| 198 | + let resolve s = |
| 199 | + if s = "" then |
| 200 | + Bsb_exception.invalid_spec "invalid ppx, empty string found" |
| 201 | + else |
| 202 | + (Bsb_build_util.resolve_bsb_magic_file ~cwd |
| 203 | + ~desc:Bsb_build_schemas.ppx_flags s) |
| 204 | + .path |
| 205 | + in |
| 206 | + Ext_array.to_list_f content (fun x -> |
| 207 | + match x with |
| 208 | + | Str x -> { Bsb_config_types.name = resolve x.str; args = [] } |
| 209 | + | Arr { content } -> ( |
| 210 | + let xs = Bsb_build_util.get_list_string content in |
| 211 | + match xs with |
| 212 | + | [] -> Bsb_exception.config_error x " empty array is not allowed" |
| 213 | + | name :: args -> { Bsb_config_types.name = resolve name; args }) |
| 214 | + | config -> |
| 215 | + Bsb_exception.config_error config |
| 216 | + (field ^ "expect each item to be either string or array")) |
| 217 | + | Some config -> Bsb_exception.config_error config (field ^ " expect an array") |
| 218 | + |
| 219 | +let extract_js_post_build (map : json_map) cwd : string option = |
| 220 | + let js_post_build_cmd = ref None in |
| 221 | + map |
| 222 | + |? ( Bsb_build_schemas.js_post_build, |
| 223 | + `Obj |
| 224 | + (fun m -> |
| 225 | + m |
| 226 | + |? ( Bsb_build_schemas.cmd, |
| 227 | + `Str |
| 228 | + (fun s -> |
| 229 | + js_post_build_cmd := |
| 230 | + Some |
| 231 | + (Bsb_build_util.resolve_bsb_magic_file ~cwd |
| 232 | + ~desc:Bsb_build_schemas.js_post_build s) |
| 233 | + .path) ) |
| 234 | + |> ignore) ) |
| 235 | + |> ignore; |
| 236 | + !js_post_build_cmd |
| 237 | + |
| 238 | +(** ATT: make sure such function is re-entrant. |
| 239 | + With a given [cwd] it works anywhere*) |
| 240 | +let interpret_json ~(package_kind : Bsb_package_kind.t) ~(per_proj_dir : string) ~(warn_legacy_config : bool) |
| 241 | + : Bsb_config_types.t = |
| 242 | + (* we should not resolve it too early, |
| 243 | + since it is external configuration, no {!Bsb_build_util.convert_and_resolve_path} |
| 244 | + *) |
| 245 | + |
| 246 | + (* When we plan to add more deps here, |
| 247 | + Make sure check it is consistent that for nested deps, we have a |
| 248 | + quck check by just re-parsing deps |
| 249 | + Make sure it works with [-make-world] [-clean-world] |
| 250 | + *) |
| 251 | + |
| 252 | + (* Setting ninja is a bit complex |
| 253 | + 1. if [build.ninja] does use [ninja] we need set a variable |
| 254 | + 2. we need store it so that we can call ninja correctly |
| 255 | + *) |
| 256 | + match |
| 257 | + Bsb_config_parse.parse_json ~per_proj_dir ~warn_legacy_config |
| 258 | + with |
| 259 | + | filename, _, Obj { map } -> ( |
| 260 | + let package_name, namespace = extract_package_name_and_namespace map in |
| 261 | + let gentype_config = extract_gentype_config map in |
| 262 | + |
| 263 | + (* This line has to be before any calls to Bsb_global_backend.backend, because it'll read the entries |
| 264 | + array from the bsconfig and set the backend_ref to the first entry, if any. *) |
| 265 | + |
| 266 | + (* The default situation is empty *) |
| 267 | + let built_in_package : bool = check_stdlib map in |
| 268 | + |
| 269 | + let pp_flags : string option = |
| 270 | + extract_string map Bsb_build_schemas.pp_flags (fun p -> |
| 271 | + if p = "" then |
| 272 | + Bsb_exception.invalid_spec "invalid pp, empty string found" |
| 273 | + else |
| 274 | + Some |
| 275 | + (Bsb_build_util.resolve_bsb_magic_file ~cwd:per_proj_dir |
| 276 | + ~desc:Bsb_build_schemas.pp_flags p) |
| 277 | + .path) |
| 278 | + in |
| 279 | + let reason_react_jsx = extract_reason_react_jsx map in |
| 280 | + let bs_dependencies = |
| 281 | + extract_dependencies map per_proj_dir Bsb_build_schemas.bs_dependencies |
| 282 | + in |
| 283 | + let bs_dev_dependencies = |
| 284 | + match package_kind with |
| 285 | + | Toplevel | Pinned_dependency _ -> |
| 286 | + extract_dependencies map per_proj_dir |
| 287 | + Bsb_build_schemas.bs_dev_dependencies |
| 288 | + | Dependency _ -> [] |
| 289 | + in |
| 290 | + let pinned_dependencies = Bsb_build_util.extract_pinned_dependencies map in |
| 291 | + match map.?(Bsb_build_schemas.sources) with |
| 292 | + | Some sources -> |
| 293 | + let cut_generators = |
| 294 | + extract_boolean map Bsb_build_schemas.cut_generators false |
| 295 | + in |
| 296 | + let groups = |
| 297 | + Bsb_parse_sources.scan ~ignored_dirs:(extract_ignored_dirs map) |
| 298 | + ~package_kind ~root:per_proj_dir ~cut_generators |
| 299 | + (* ~namespace *) |
| 300 | + sources |
| 301 | + in |
| 302 | + let bsc_flags = extract_string_list map Bsb_build_schemas.bsc_flags in |
| 303 | + let jsx = Bsb_jsx.from_map map in |
| 304 | + let jsx, bsc_flags = |
| 305 | + match package_kind with |
| 306 | + | Pinned_dependency x | Dependency x -> |
| 307 | + if List.mem package_name x.jsx.v3_dependencies then |
| 308 | + ( { jsx with version = Some Jsx_v3 }, |
| 309 | + "-open ReactV3" :: bsc_flags ) |
| 310 | + else (x.jsx, bsc_flags) |
| 311 | + | _ -> (jsx, bsc_flags) |
| 312 | + in |
| 313 | + { |
| 314 | + pinned_dependencies; |
| 315 | + gentype_config; |
| 316 | + package_name; |
| 317 | + namespace; |
| 318 | + warning = extract_warning map; |
| 319 | + external_includes = |
| 320 | + extract_string_list map Bsb_build_schemas.bs_external_includes; |
| 321 | + bsc_flags; |
| 322 | + ppx_files = |
| 323 | + extract_ppx map ~cwd:per_proj_dir Bsb_build_schemas.ppx_flags; |
| 324 | + pp_file = pp_flags; |
| 325 | + bs_dependencies; |
| 326 | + bs_dev_dependencies; |
| 327 | + (* |
| 328 | + reference for quoting |
| 329 | + {[ |
| 330 | + let tmpfile = Filename.temp_file "ocamlpp" "" in |
| 331 | + let comm = Printf.sprintf "%s %s > %s" |
| 332 | + pp (Filename.quote sourcefile) tmpfile |
| 333 | + in |
| 334 | + ]} |
| 335 | + *) |
| 336 | + js_post_build_cmd = extract_js_post_build map per_proj_dir; |
| 337 | + package_specs = |
| 338 | + (match package_kind with |
| 339 | + | Toplevel -> Bsb_package_specs.from_map ~cwd:per_proj_dir map |
| 340 | + | Pinned_dependency x | Dependency x -> x.package_specs); |
| 341 | + file_groups = groups; |
| 342 | + files_to_install = Queue.create (); |
| 343 | + built_in_dependency = built_in_package; |
| 344 | + reason_react_jsx; |
| 345 | + jsx; |
| 346 | + generators = extract_generators map; |
| 347 | + cut_generators; |
| 348 | + uncurried = |
| 349 | + (match package_kind with |
| 350 | + | Toplevel -> extract_uncurried map |
| 351 | + | Pinned_dependency x | Dependency x -> x.uncurried); |
| 352 | + filename; |
| 353 | + } |
| 354 | + | None -> |
| 355 | + Bsb_exception.invalid_spec "no sources specified in bsconfig.json") |
| 356 | + | _, _, _ -> Bsb_exception.invalid_spec "bsconfig.json expect a json object {}" |
| 357 | + |
| 358 | +let deps_from_bsconfig () = |
| 359 | + let cwd = Bsb_global_paths.cwd in |
| 360 | + let json = Bsb_config_parse.parse_json ~per_proj_dir:cwd ~warn_legacy_config:false in |
| 361 | + match json with |
| 362 | + | _, _, Obj { map } -> |
| 363 | + ( Bsb_package_specs.from_map ~cwd map, |
| 364 | + Bsb_jsx.from_map map, |
| 365 | + extract_uncurried map, |
| 366 | + Bsb_build_util.extract_pinned_dependencies map ) |
| 367 | + | _, _, _ -> assert false |
0 commit comments