diff --git a/README.org b/README.org index f4948db6..f3406c0b 100644 --- a/README.org +++ b/README.org @@ -1,5 +1,4 @@ * JBUILDER - A composable build system for OCaml and Reason -%%VERSION%% Jbuilder is a build system designed for OCaml/Reason projects only. It focuses on providing the user with a consistent experience and takes diff --git a/bin/main.ml b/bin/main.ml index aa37b470..afbab5e9 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -647,6 +647,18 @@ let subst = ; `P {|If you use topkg to handle the releases of your project, then you should add this line to the $(b,build:) instructions in your opam file:|} ; `Pre {| ["jbuilder" "subst" name] {pinned}|} + ; `S "HOOKS" + ; `I ("pre_subst", + {|If a a file $(b,pkg/pre_subst_hook.ml) exists, it is evaluated before + performing the expansion.|}) + ; `I ("post_subst", + {|If a a file $(b,pkg/post_subst_hook.ml) exists, it is evaluated after + performing the expansion.|}) + ; `P ({|A typical example of $(b,pre_subst) hook is a script that adds %%|} ^ + {|VERSION%% + to the README, so that the version is present in releases and you + don't get the variable unexpanded on github. + Jbuilder itself does this.|}) ; `Blocks help_secs ] in diff --git a/pkg/pre_subst_hook.ml b/pkg/pre_subst_hook.ml new file mode 100644 index 00000000..7f11d264 --- /dev/null +++ b/pkg/pre_subst_hook.ml @@ -0,0 +1,23 @@ +let split_at_first_empty_line s = + let len = String.length s in + let rec loop i = + if i + 1 >= len then + (s, "") + else if s.[i] = '\n' && s.[i + 1] = '\n' then + let i = i + 1 in + (String.sub s 0 i, + String.sub s i (len - i)) + else + loop (i + 1) + in + loop 0 + +let () = + let ic = open_in_bin "README.org" in + let len = in_channel_length ic in + let s = really_input_string ic len in + close_in ic; + let oc = open_out_bin "README.org" in + let before, after = split_at_first_empty_line s in + Printf.fprintf oc "%s%%%%%,VERSION%%%%\n%s" before after; + close_out oc diff --git a/src/watermarks.ml b/src/watermarks.ml index e614a8f9..7c152d3d 100644 --- a/src/watermarks.ml +++ b/src/watermarks.ml @@ -204,6 +204,19 @@ let get_name ~files ?name () = die "@{Error@}: cannot determine name automatically.\n\ You must pass a [--name] command line argument." +let ocaml = lazy ( + match Bin.which "ocaml" with + | None -> Utils.program_not_found "ocaml" + | Some p -> Path.to_string p +) + +let run_hook ~files hook = + let fn = sprintf "pkg/%s_hook.ml" hook in + if List.mem fn ~set:files then + Future.run Strict (Lazy.force ocaml) [fn] + else + Future.return () + let subst_git ?name () = let rev = "HEAD" in let git = @@ -217,6 +230,8 @@ let subst_git ?name () = (Future.run_capture Strict git ["rev-parse"; rev])) (Future.run_capture_lines Strict git ["ls-tree"; "-r"; "--name-only"; rev]) >>= fun ((version, commit), files) -> + run_hook "pre_subst" ~files + >>= fun () -> let version = String.trim version in let commit = String.trim commit in let name = get_name ~files ?name () in @@ -224,7 +239,7 @@ let subst_git ?name () = List.iter files ~f:(fun fn -> if is_a_source_file fn then subst_file fn ~map:watermarks); - Future.return () + run_hook "post_subst" ~files let subst ?name () = if Sys.file_exists ".git" then