C build tool of the 21st century
1open Types
2
3type t = {
4 env : Eio_posix.stdenv;
5 hidden : bool;
6 source : path;
7 build : path;
8 compiler_index : (string, Compiler.t) Hashtbl.t;
9 compilers : Compiler.Set.t;
10 linker : Linker.t option;
11 script : string option;
12 after : string option;
13 depends_on : string list;
14 name : string;
15 pkgconf : string list;
16 ignore : Re.t list;
17 output : path option;
18 disable_cache : bool;
19 mtime : float;
20 parallel : bool;
21 log_level : Util.log_level;
22 mutable files : Re.t list;
23 mutable headers : Re.t list;
24 mutable flags : Flags.t;
25 mutable compiler_flags : (string, Flags.t) Hashtbl.t;
26}
27
28module External = struct
29 type nonrec t = { path : string; target : string; build : t }
30end
31
32let add_compile_flags t = Flags.add_compile_flags t.flags
33let add_link_flags t = Flags.add_link_flags t.flags
34let obj_path t = Eio.Path.(t.build / "obj" / t.name)
35
36let v ?build ?(parallel = true) ?(hidden = false) ?mtime ?(pkgconf = []) ?script
37 ?after ?(depends_on = []) ?flags ?linker ?compilers ?(compiler_flags = [])
38 ?(files = []) ?(headers = []) ?(ignore = []) ?(disable_cache = false)
39 ?(log_level = `Quiet) ?output ~source ~name env =
40 let compilers =
41 match compilers with
42 | None -> Compiler.Set.default
43 | Some compilers ->
44 Compiler.Set.union Compiler.Set.default
45 @@ Compiler.Set.of_list compilers
46 in
47 let build =
48 match build with
49 | None -> Eio.Path.(env#cwd / "zenon-build")
50 | Some path -> path
51 in
52 Eio.Path.mkdirs ~exists_ok:true build ~perm:0o755;
53 let compiler_flags = Hashtbl.of_seq (List.to_seq compiler_flags) in
54 let compiler_index = Hashtbl.create 8 in
55 Compiler.Set.iter
56 (fun c ->
57 String_set.iter (fun ext -> Hashtbl.replace compiler_index ext c) c.ext)
58 compilers;
59 {
60 parallel;
61 pkgconf;
62 env;
63 source;
64 build;
65 compiler_index;
66 compilers;
67 linker;
68 files = List.map Util.glob files;
69 headers = List.map Util.glob headers;
70 script;
71 after;
72 depends_on;
73 flags = Option.value ~default:(Flags.v ()) flags;
74 output;
75 ignore;
76 name;
77 compiler_flags;
78 disable_cache;
79 mtime = Option.value ~default:(Unix.gettimeofday ()) mtime;
80 hidden;
81 log_level;
82 }
83
84let special_dirs = String_set.of_list [ "zenon-build"; ".git"; ".jj" ]
85let is_special_dir name = String_set.mem name special_dirs
86
87let rec collect_all count check_ignore root path =
88 let entries = Eio.Path.read_dir path |> List.to_seq in
89 Seq.concat_map
90 (fun name ->
91 incr count;
92 let full_path = Eio.Path.(path / name) in
93 if Eio.Path.is_directory full_path then
94 let rel_path = Util.relative_to root full_path in
95 if (not (is_special_dir name)) && check_ignore rel_path then
96 collect_all count check_ignore root full_path
97 else Seq.empty
98 else Seq.return full_path)
99 entries
100
101let locate_files t = function
102 | [] -> Seq.empty
103 | patterns ->
104 let ignore_re = Re.alt t.ignore |> Re.compile in
105 let check_ignore f =
106 match t.ignore with [] -> true | _ -> not (Re.execp ignore_re f)
107 in
108 let count = ref 0 in
109 let all_files = collect_all count check_ignore t.source t.source |> List.of_seq in
110 let seen = Hashtbl.create (List.length all_files) in
111 List.concat_map
112 (fun pattern ->
113 let re = Re.compile pattern in
114 List.filter_map
115 (fun path ->
116 let path_str = Eio.Path.native_exn path in
117 if Hashtbl.mem seen path_str then None
118 else
119 let rel_path = Util.relative_to t.source path in
120 if Re.execp re rel_path then (
121 Hashtbl.add seen path_str true;
122 Some path)
123 else None)
124 all_files)
125 patterns
126 |> List.to_seq
127
128let locate_source_files t : Source_file.t Seq.t =
129 locate_files t t.files
130 |> Seq.map (fun path -> Source_file.v ~root:t.source path)
131
132let locate_headers t : path Seq.t = locate_files t t.headers
133let add_source_file t path = t.files <- t.files @ [ Util.glob path ]
134
135let add_source_files t ?(reset = false) files =
136 if reset then t.files <- [];
137 t.files <- t.files @ List.map Util.glob files
138
139let clean t = Eio.Path.rmtree ~missing_ok:true t.build
140let clean_obj t = Eio.Path.rmtree ~missing_ok:true (obj_path t)