@@ -24,6 +24,14 @@ let readlink f =
24
24
try Sys. chdir cwd; f
25
25
with Sys_error _ -> Sys. chdir (Filename. get_temp_dir_name () ); f
26
26
27
+ let absolute_filename path =
28
+ (* Note: symlinks are not taken into account *)
29
+ if Filename. is_relative path
30
+ then Filename. concat (Sys. getcwd () ) path
31
+ else path
32
+
33
+ let dflt_build_dir = " _learn-ocaml-build"
34
+
27
35
module Args = struct
28
36
open Arg
29
37
@@ -44,6 +52,16 @@ module Args = struct
44
52
" The path to the repository containing the exercises, lessons and \
45
53
tutorials."
46
54
55
+ let build_dir =
56
+ value & opt dir (" ./" ^ dflt_build_dir) & info [" build-dir" ] ~docs ~docv: " DIR" ~doc:
57
+ (Printf. sprintf
58
+ " Directory where the repo exercises are copied and precompiled. \
59
+ When $(docv) takes its default value (e.g. when it is omitted in CLI), \
60
+ '$(b,learn-ocaml build)' first erases the '$(docv)/exercises' subfolder. \
61
+ Note that the default value for $(docv), './%s', is generally a sensible choice. \
62
+ But passing the same argument as the one for $(i,--repo) is also a valid value for $(docv)."
63
+ dflt_build_dir)
64
+
47
65
let app_dir =
48
66
value & opt string " ./www" & info [" app-dir" ; " o" ] ~docs ~docv: " DIR" ~doc:
49
67
" Directory where the app should be generated for the $(i,build) command, \
@@ -215,9 +233,10 @@ module Args = struct
215
233
Term. (const apply $ contents_dir $ try_ocaml $ lessons $ exercises $ playground $ toplevel $ base_url)
216
234
217
235
let repo_conf =
218
- let apply repo_dir exercises_filtered jobs =
236
+ let apply repo_dir build_dir exercises_filtered jobs =
219
237
Learnocaml_process_exercise_repository. exercises_dir :=
220
- repo_dir/ " exercises" ;
238
+ (* not repo_dir/"exercises" here - since we need write permissions *)
239
+ build_dir/ " exercises" ;
221
240
Learnocaml_process_exercise_repository. exercises_filtered :=
222
241
Learnocaml_data.SSet. of_list (List. flatten exercises_filtered);
223
242
Learnocaml_process_tutorial_repository. tutorials_dir :=
@@ -227,7 +246,7 @@ module Args = struct
227
246
Learnocaml_process_exercise_repository. n_processes := jobs;
228
247
()
229
248
in
230
- Term. (const apply $ repo_dir $ exercises_filtered $ jobs)
249
+ Term. (const apply $ repo_dir $ build_dir $ exercises_filtered $ jobs)
231
250
232
251
let term =
233
252
let apply conf () = conf in
@@ -243,16 +262,17 @@ module Args = struct
243
262
commands : command list ;
244
263
app_dir : string ;
245
264
repo_dir : string ;
265
+ build_dir : string ;
246
266
grader : Grader .t ;
247
267
builder : Builder .t ;
248
268
server : Server .t ;
249
269
}
250
270
251
271
let term =
252
- let apply commands app_dir repo_dir grader builder server =
253
- { commands; app_dir; repo_dir; grader; builder; server }
272
+ let apply commands app_dir repo_dir build_dir grader builder server =
273
+ { commands; app_dir; repo_dir; build_dir; grader; builder; server }
254
274
in
255
- Term. (const apply $ commands $ app_dir $ repo_dir
275
+ Term. (const apply $ commands $ app_dir $ repo_dir $ build_dir
256
276
$ Grader. term $ Builder. term $ Server. term app_dir base_url)
257
277
end
258
278
@@ -328,6 +348,49 @@ let main o =
328
348
> |= fun i -> Some i)
329
349
else Lwt. return_none
330
350
in
351
+ let copy_build_exercises o =
352
+ (* NOTE: if `--build` = `--repo`, then no copy is needed.
353
+ Before checking path equality, we need to get canonical paths *)
354
+ let repo_exos_dir = readlink o.repo_dir / " exercises" in
355
+ let build_exos_dir = readlink o.build_dir / " exercises" in
356
+ if repo_exos_dir <> build_exos_dir then begin
357
+ (* NOTE: if the CLI arg is "./_learn-ocaml-build" or "_learn-ocaml-build"
358
+ then the exercises subdirectory is erased beforehand *)
359
+ begin
360
+ if (o.build_dir = dflt_build_dir || o.build_dir = " ./" ^ dflt_build_dir)
361
+ && Sys. file_exists build_exos_dir then
362
+ Lwt. catch (fun () ->
363
+ Lwt_process. exec (" rm" ,[|" rm" ;" -rf" ; build_exos_dir|]) >> = fun r ->
364
+ if r <> Unix. WEXITED 0 then
365
+ Lwt. fail_with " Remove command failed"
366
+ else Lwt. return_unit)
367
+ (fun ex ->
368
+ Printf. eprintf
369
+ " Error: while removing previous build-dir \
370
+ %s:\n %s\n %!"
371
+ build_exos_dir (Printexc. to_string ex);
372
+ exit 1 )
373
+ else
374
+ Lwt. return_unit
375
+ end >> = fun () ->
376
+ Printf. printf " Building %s\n %!" (o.build_dir / " exercises" );
377
+ (* NOTE: we choose to reuse Lwt_utils.copy_tree,
378
+ even if we could use "rsync" (upside: "--delete-delay",
379
+ but downside: would require the availability of rsync). *)
380
+ Lwt. catch
381
+ (fun () -> Lwt_utils. copy_tree repo_exos_dir build_exos_dir)
382
+ (function
383
+ | Failure _ ->
384
+ Lwt. fail_with @@ Printf. sprintf
385
+ " Failed to copy repo exercises to %s"
386
+ (build_exos_dir)
387
+ | e -> Lwt. fail e)
388
+ (* NOTE: no chown is needed,
389
+ but we may want to run "chmod -R u+w exercises"
390
+ if the source repository has bad permissions... *)
391
+ end
392
+ else Lwt. return_unit
393
+ in
331
394
let generate o =
332
395
if List. mem Build o.commands then
333
396
(let get_app_dir o =
@@ -412,7 +475,9 @@ let main o =
412
475
(fun _ -> Learnocaml_process_playground_repository. main (o.app_dir))
413
476
>> = fun playground_ret ->
414
477
if_enabled o.builder.Builder. exercises (o.repo_dir/ " exercises" )
415
- (fun _ -> Learnocaml_process_exercise_repository. main (o.app_dir))
478
+ (fun _ ->
479
+ copy_build_exercises o >> = fun () ->
480
+ Learnocaml_process_exercise_repository. main (o.app_dir))
416
481
>> = fun exercises_ret ->
417
482
Lwt_io. with_file ~mode: Lwt_io. Output (o.app_dir/ " js" / " learnocaml-config.js" )
418
483
(fun oc ->
@@ -442,11 +507,7 @@ let main o =
442
507
let running = Learnocaml_server. check_running () in
443
508
Option. iter Learnocaml_server. kill_running running;
444
509
let temp = temp_app_dir o in
445
- let app_dir =
446
- if Filename. is_relative o.app_dir
447
- then Filename. concat (Sys. getcwd () ) o.app_dir
448
- else o.app_dir
449
- in
510
+ let app_dir = absolute_filename o.app_dir in
450
511
let bak =
451
512
let f =
452
513
Filename. temp_file
0 commit comments