Source file stog_main.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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
open Stog.Types;;
let output_dir = ref "stog-output";;
let site_url = ref None ;;
let tmpl_dirs = ref [] ;;
let mod_dirs = ref [] ;;
let use_cache = ref true;;
let depcut = ref false;;
let local = ref false;;
let stog_defs = ref [] ;;
let lang = ref None;;
let default_lang_to_set = ref None;;
let plugins = ref [];;
let packages = ref [];;
let only_doc = ref None;;
let publish_only = ref None ;;
type mode = Generate | Server
let mode = ref Generate
let add_stog_def s =
match Stog_base.Misc.split_string s [':'] with
[] -> ()
| [name] -> stog_defs := !stog_defs @ [(("", name), Xtmpl.Rewrite.atts_empty, [])]
| name :: q ->
let contents = Xtmpl.Rewrite.from_string (String.concat ":" q) in
stog_defs := !stog_defs @ [(("", name), Xtmpl.Rewrite.atts_empty, contents)]
let set_stog_options stog =
let stog = { stog with Stog.Types.stog_outdir = !output_dir } in
let stog =
match !site_url, !local with
None, false -> stog
| None, true ->
let d =
if Filename.is_relative stog.stog_outdir then
Filename.concat (Sys.getcwd()) stog.stog_outdir
else
stog.stog_outdir
in
let url = "file://" ^ d in
let url = Stog.Url.of_string url in
{ stog with Stog.Types.stog_base_url = url }
| Some s, false -> { stog with Stog.Types.stog_base_url = s }
| Some _, true ->
failwith "Please choose --local or --site-url but not both"
in
let stog = { stog with stog_tmpl_dirs = List.rev (stog.stog_tmpl_dirs @ !tmpl_dirs) } in
let stog = { stog with stog_mod_dirs = List.rev (stog.stog_mod_dirs @ !mod_dirs) } in
let stog =
match !lang with
None -> stog
| Some s -> { stog with Stog.Types.stog_lang = Some s }
in
let stog = { stog with Stog.Types.stog_depcut = !depcut } in
let stog = { stog with Stog.Types.stog_defs = stog.stog_defs @ !stog_defs } in
let stog =
match !publish_only with
None -> stog
| _ ->
{ stog with
stog_publish_only = !publish_only ;
}
in
let stog =
match stog.stog_tmpl_dirs with
[] -> stog
| dirs ->
{ stog with stog_tmpl_dirs = dirs @
[List.hd Stog.Install.Sites.templates] }
in
stog
;;
let run_from_dirs dirs =
try
let stog = Stog.Init.from_dirs ~set_fields: set_stog_options dirs in
let modules = Stog.Init.init_modules stog in
let only_docs =
match !only_doc with
None -> None
| Some s -> Some [s]
in
match !Stog.Server_mode.server_mode with
None -> Stog.Engine.generate ~use_cache: !use_cache ?only_docs stog modules
| Some (`Single f) ->
let read_stog () = Stog.Init.from_dirs ~set_fields: set_stog_options dirs in
f read_stog stog
| _ -> assert false
with Stog.Types.Path_trie.Already_present l ->
let msg = "Path already present: "^(String.concat "/" l) in
failwith msg
;;
let run_from_files files =
try
let stog = Stog.Init.from_files ~set_fields: set_stog_options files in
let modules = Stog.Init.init_modules stog in
match !Stog.Server_mode.server_mode with
None -> Stog.Engine.generate ~use_cache: false ~gen_cache: false stog modules
| Some (`Single f) ->
let read_stog () = Stog.Init.from_files ~set_fields: set_stog_options files in
f read_stog stog
| _ -> assert false
with Stog.Types.Path_trie.Already_present l ->
let msg = "Path already present: "^(String.concat "/" l) in
failwith msg
;;
let options = [
"-version",
Arg.Unit (fun () -> print_endline (Printf.sprintf "%s" (Stog.Version.number())); exit 0),
" print version and exit";
"-D", Arg.Set Stog.Config.debug, " debug mode" ;
"--verbose-level", Arg.String Stog.Log.set_level_of_string,
(Printf.sprintf "<%s> set verbose level" (String.concat "|"
(List.map Logs.level_to_string
Logs.([None;Some App;Some Error;Some Warning;Some Info;Some Debug])))) ;
"-d", Arg.Set_string output_dir,
"<dir> set output directory instead of "^ !output_dir ;
"--site-url", Arg.String (fun s -> site_url := Some (Stog.Url.of_string s)),
"<s> use <s> as site url instead of the one specified in the input stog" ;
"--local", Arg.Set local,
" set site-url as file://<destination directory>" ;
"--tmpl", Arg.String (fun s -> tmpl_dirs := s :: !tmpl_dirs ),
"<dir> add <dir> as template directory";
"--mods", Arg.String (fun s -> mod_dirs := s :: !mod_dirs ),
"<dir> add <dir> as module directory";
"--lang", Arg.String (fun s -> lang := Some s),
"<s> generate pages for language <s>" ;
"--default-lang", Arg.String (fun s -> default_lang_to_set := Some s),
"<lang> use <lang> as default language (dates, ...); default is \"en\"" ;
"--plugin", Arg.String (fun s -> plugins := !plugins @ [s]),
"<file> load plugin (ocaml object file)" ;
"--package", Arg.String (fun s -> packages := !packages @ [s]),
"<pkg[,pkg2[,...]]> load package (a plugin loaded with ocamlfind)";
"--only", Arg.String (fun s -> use_cache := false ; only_doc := Some s),
"<doc-id> generate only the page for the given document; imply --nocache" ;
"--nocache", Arg.Clear use_cache,
" do not use cache to prevent computing unmodified documents" ;
"--depcut", Arg.Set depcut,
" use only 1 level of dependency when getting cached documents";
"--stog-ocaml-session", Arg.Set_string Stog.Ocaml.stog_ocaml_session,
"<command> use <command> as stog-ocaml-session program";
"--def", Arg.String add_stog_def,
"name:contents add a global rule name with the given contents" ;
"--publish-only",
Arg.String (fun s -> publish_only := Some (Stog.Filter.filter_of_string s)),
"<filter> only keep documents verifying the given condition" ;
"--hackcmxs", Arg.Set Stog.Dyn.hack_cmxs,
" when a package to load depends on .cmxa or .cmx file, try to build .cmxs.\n\n *** Server options ***";
"--http", Arg.Set_string Stog.Server_mode.http_url,
"<url> set url of server, used to know port and host to listen on\n\t\t"^
"(default is "^(!Stog.Server_mode.http_url)^")" ;
"--ws", Arg.Set_string Stog.Server_mode.ws_url,
"<url> set websocket url of server, used to know port and host to listen on\n\t\t"^
"(default is "^(!Stog.Server_mode.ws_url)^")" ;
"--pub-http", Arg.String (fun s -> Stog.Server_mode.pub_http_url := Some s),
"<url> set public url of server (default is same as --http)" ;
"--pub-ws", Arg.String (fun s -> Stog.Server_mode.pub_ws_url := Some s),
"<url> set public url of websocket server (default is same as --ws)" ;
];;
let usage ?(with_options=true) ()=
Printf.sprintf
"Usage: %s [options] <directory>\n or %s [options] <files>%s"
Sys.argv.(0) Sys.argv.(0)
(if with_options then "\nwhere options are:" else "")
;;
let file_kind file =
try (Unix.stat file).Unix.st_kind
with Unix.Unix_error (e,s1,s2) ->
failwith (Printf.sprintf "%s: %s %s" (Unix.error_message e) s1 s2)
;;
let pp_header ~pp_h ppf (l, h) = match l with
| Logs.App ->
begin match h with
| None -> ()
| Some h -> Fmt.pf ppf "[%a] " Fmt.(styled Logs_fmt.app_style string) h
end
| Logs.Error ->
pp_h ppf Logs_fmt.err_style (match h with None -> "ERROR" | Some h -> h)
| Logs.Warning ->
pp_h ppf Logs_fmt.warn_style (match h with None -> "WARNING" | Some h -> h)
| Logs.Info ->
pp_h ppf Logs_fmt.info_style (match h with None -> "INFO" | Some h -> h)
| Logs.Debug ->
pp_h ppf Logs_fmt.debug_style (match h with None -> "DEBUG" | Some h -> h)
let =
let pp_h ppf style h = Fmt.pf ppf "[%a]" Fmt.(styled style string) h in
pp_header ~pp_h
let reporter ppf =
let report src level ~over k msgf =
let k _ = over (); k () in
let with_loc h tags k ppf fmt =
let loc = match tags with
| None -> None
| Some tags -> Logs.Tag.find Stog.Log.loc_tag tags
in
let loc = match loc with
| None -> ""
| Some l -> Printf.sprintf "%s:" (Xtmpl.Types.string_of_loc l)
in
if loc = "" then
Format.kfprintf k ppf ("%a@[" ^^ fmt ^^ "@]@.")
pp_header (level, h)
else
Format.kfprintf k ppf ("%a%s@.@[" ^^ fmt ^^ "@]@.")
pp_header (level, h) loc
in
msgf @@ fun ? ?tags fmt -> with_loc header tags k ppf fmt
in
{ Logs.report = report }
let main () =
Logs.set_level (Some Logs.Warning);
let remain = ref [] in
try
Arg.parse (Arg.align options) (fun s -> remain := s :: !remain) (usage()) ;
Fmt_tty.setup_std_outputs ();
Logs.set_reporter (reporter (Format.std_formatter));
Stog.Dyn.load_packages !packages;
Stog.Dyn.check_files_have_extension !plugins;
Stog.Dyn.load_files !plugins;
begin
match !default_lang_to_set with
None -> ()
| Some abbrev -> Stog.Intl.set_default_lang abbrev
end;
match !Stog.Server_mode.server_mode with
Some (`Multi f) -> f (List.rev !remain)
| _ ->
begin
match List.rev !remain with
[] -> failwith (usage ~with_options: false ())
| h :: q ->
let k = file_kind h in
List.iter
(fun f ->
if file_kind f <> k then
failwith (usage ~with_options: false ()))
q;
match k with
Unix.S_REG -> run_from_files (h::q)
| Unix.S_DIR -> run_from_dirs (h::q)
| _ -> failwith ("Invalid file type for "^h)
end;
let err = Logs.err_count () in
let warn = Logs.warn_count () in
begin
match err, warn with
0, 0 -> ()
| _, _ ->
let msg = Printf.sprintf "%d error%s, %d warning%s"
err (if err > 1 then "s" else "")
warn (if warn > 1 then "s" else "")
in
prerr_endline msg;
end;
exit err
with
e when !Stog.Config.debug -> raise e
| Stog.Engine.Cant_open_cache_file cache_file ->
Stog.Log.err ~fatal:1
(fun m-> m "Could open cache file %S@. You should run stog once with --nocache" cache_file)
| Xtmpl.Types.Error e ->
Stog.Log.err ~fatal:1 (fun m -> m "%s" (Xtmpl.Types.string_of_error e))
| Stog.Error.Error e ->
Stog.Log.err ~fatal:1
(fun m -> m "%s" (Stog.Error.string_of_error e))
| Failure s
| Sys_error s ->
Stog.Log.err ~fatal:1 (fun m -> m "%s" s)
| e ->
Stog.Log.err ~fatal:1 (fun m -> m "%s" (Printexc.to_string e))
;;
let () = main ()