Enable ocaml-zarith.
[goaljobs-goals.git] / fedora_ocaml_rebuild.ml
1 (* Perform a complete Fedora OCaml rebuild, in build order. *)
2
3 open Unix
4 open Printf
5
6 open Goaljobs
7 open Config
8 open Git
9 open Fedora
10
11 let branch = "master"
12 (*let side_tag = Some "f31-ocaml"*)
13 let side_tag = None
14
15 let koji_target =
16   match side_tag with
17   | Some t -> t
18   | None -> "f31-build"
19
20 (* The name of the rebuild, and also the magic substring that must
21  * appear in the %changelog when the package has been rebuilt.
22  *)
23 let rebuild_name = "OCaml 4.08.1 (rc2)"
24
25 (* Local repository that contains build dependencies. *)
26 let yum_repo = "koji-rawhide"
27
28 (* Packages that have problems.  These block the packages and all
29  * dependent packages.
30  *)
31 let blocked = [
32   "ocaml-camlp4";   (* deprecated + all dependent packages *)
33   "ocaml-yojson";   (* jjames is looking at this *)
34 ]
35 let blocked pkg = List.mem pkg blocked
36
37 (* These packages are treated as if they have been rebuilt. *)
38 let ignored = [
39   "ocaml-srpm-macros";             (* don't need to build this *)
40   "ocaml";                         (* rebuilt by hand *)
41 ]
42 let ignored pkg = List.mem pkg ignored
43
44 (* List of OCaml-related source package names. *)
45 let source_packages =
46   let dirs = shlines "
47        cd %s && \
48        for f in ocaml*; do
49          [ -f \"$f/master/$f.spec\" ] && echo \"$f\"
50        done
51   " fedora_dir in
52   dirs @ [ "alt-ergo"; "apron"; "brltty"; "coccinelle"; "coq";
53            "cduce"; "frama-c"; "gappalib-coq"; "graphviz"; "hevea"; "hivex";
54            "opam"; "plplot"; "virt-top"; "why3"; "z3";
55            "flocq" (* no OCaml code, but needs to be rebuilt after Coq *);
56            "guestfs-browser"; "libguestfs" ]
57
58 (* Dependencies of each package.  (pkg, [deps ...]) *)
59 let pkg_deps = dependencies branch source_packages
60
61 (* Remove blocked packages and packages which have a blocked package
62  * as a dependency (recursively).
63  *)
64 let source_packages =
65   let rec is_blocked pkg =
66     if blocked pkg then true
67     else (
68       let deps = List.assoc pkg pkg_deps in
69       List.exists is_blocked deps
70     )
71   in
72   List.filter (fun pkg -> not (is_blocked pkg)) source_packages
73
74 (* Short the dependencies lists so that the build order is stable
75  * each time it runs.
76  *)
77 let pkg_deps =
78   List.map (fun (pkg, deps) -> pkg, List.sort compare deps) pkg_deps
79
80 (* Sort the source packages so that the packages with the largest
81  * number of reverse dependencies [other packages that depend on it]
82  * appear earlier in the list, on the basis that building these
83  * packages first has the greatest advantage.
84  *)
85 let source_packages =
86   let rdeps pkg =
87     Utils.filter_map (
88       fun (rdep, deps) -> if List.mem pkg deps then Some rdep else None
89     ) pkg_deps
90   in
91   let cmp p1 p2 =
92     let r1 = rdeps p1 and r2 = rdeps p2 in
93     let n1 = List.length r1 and n2 = List.length r2 in
94     if n1 <> n2 then compare n2 n1 else compare p1 p2
95   in
96   List.sort cmp source_packages
97
98 let () =
99   printf "final list of source packages = %s\n%!"
100     (String.concat " " source_packages)
101
102 (*
103 (* We could make this a goal, but it's cheap enough to run it unconditionally. *)
104 let install_build_dependencies pkg =
105   sh "sudo yum clean all --disablerepo=\\* --enablerepo=%s"
106     (quote yum_repo);
107   sh "sudo yum-builddep -y --disablerepo=\\* --enablerepo=%s %s"
108     (quote yum_repo) (fedora_specfile pkg branch)
109  *)
110
111 (* Unset MAKEFLAGS so it doesn't affect local builds. *)
112 let () = Unix.putenv "MAKEFLAGS" ""
113
114 (* Goal: rebuild all packages. *)
115 let rec goal all () =
116   let n = List.length source_packages in
117   List.iteri (
118     fun i pkg ->
119       require (rebuild_started pkg);
120       printf "*** *** rebuilt %d/%d packages *** ***\n%!" (i+1) n
121   ) source_packages
122
123 (* Goal: That 'package' has been rebuilt and exists in Koji. *)
124 and rebuilt pkg =
125   let specfile = fedora_specfile pkg branch in
126
127   (* Note: verrel may change as we go along, so don't assign it to
128    * variable.
129    *)
130
131   (* Note the target must be both of these because the old verrel
132    * could exist as a koji build without it having been part of the
133    * rebuild.
134    *)
135   target (ignored pkg ||
136             (file_contains_string specfile rebuild_name &&
137                koji_build_state (fedora_verrel pkg branch) == `Complete));
138
139   (* Ignored packages are treated as if they have been rebuilt. *)
140   if not (ignored pkg) then (
141
142     (* Start the rebuild. *)
143     require (rebuild_started pkg);
144
145     (* Wait for the build state to reach a conclusion. *)
146     let rec loop () =
147       match koji_build_state (fedora_verrel pkg branch) with
148       | `No_such_build ->
149         failwith (sprintf "rebuild of package %s: no build found" pkg)
150       | `Building ->
151         sleep 60;
152         loop ()
153       | `Complete ->
154         ()
155       | `Deleted ->
156         failwith (sprintf "rebuild of package %s: deleted" pkg)
157       | `Failed ->
158         failwith (sprintf "rebuild of package %s: failed" pkg)
159       | `Canceled ->
160         failwith (sprintf "rebuild of package %s: canceled" pkg)
161     in
162     loop ();
163
164     (* Wait for the build to appear in Koji repo. *)
165     koji_wait_repo koji_target (fedora_verrel pkg branch)
166   )
167
168 (* Goal: The rebuild of the package has started, but we haven't waited
169  * for it to finish.
170  *)
171 and rebuild_started pkg =
172   let deps = List.assoc pkg pkg_deps in
173   let specfile = fedora_specfile pkg branch in
174
175   (* Note the target must be both of these because the old verrel
176    * could exist as a koji build without it having been part of the
177    * rebuild.
178    *)
179   target (ignored pkg ||
180             (file_contains_string specfile rebuild_name &&
181                (match koji_build_state (fedora_verrel pkg branch) with
182                | `Building | `Complete -> true
183                | `Deleted | `Failed | `Canceled | `No_such_build -> false)));
184
185   (* All dependent packages must have been fully rebuilt and in the
186    * repo first.
187    *)
188   List.iter (fun dep -> require (rebuilt dep)) deps;
189
190   (* Ignored packages are treated as if they have been rebuilt. *)
191   if not (ignored pkg) then (
192 (*
193     (* A local test build must succeed. *)
194     require (local_build_succeeded pkg);
195 *)
196     (* local_build_succeeded normally does this ... *)
197     require (specfile_updated pkg);
198
199     (* Rebuild the package in Koji.  Don't wait ... *)
200     koji_build ~wait:false ?side_tag pkg branch;
201
202     (* ... but the build doesn't appear in Koji (eg. in 'koji
203      * buildinfo') until the SRPM has been built.  This can take quite
204      * some time.  Loop here until the build appears.
205      *)
206     let rec loop () =
207       match koji_build_state (fedora_verrel pkg branch) with
208       | `No_such_build ->
209         sleep 60;
210         loop ();
211       | `Building | `Complete ->
212         ()
213       | `Deleted ->
214         failwith (sprintf "rebuild of package %s: deleted" pkg)
215       | `Failed ->
216         failwith (sprintf "rebuild of package %s: failed" pkg)
217       | `Canceled ->
218         failwith (sprintf "rebuild of package %s: canceled" pkg)
219     in
220     loop ()
221   )
222
223 (*
224 and local_build_succeeded pkg =
225   (* The specfile must have been updated. *)
226   require (specfile_updated pkg);
227
228   let key =
229     sprintf "fedora_ocaml_local_build_%s_%s" pkg (fedora_verrel pkg branch) in
230
231   target (memory_exists key);
232
233   install_build_dependencies pkg;
234
235  (* Do a local test build to ensure the Koji build will work. *)
236   sh "
237     cd %s
238      fedpkg local
239   " (fedora_repo pkg branch);
240
241   memory_set key "1"
242 *)
243
244 and specfile_updated pkg =
245   let repodir = fedora_repo pkg branch in
246   let specfile = fedora_specfile pkg branch in
247
248   sh "
249     cd %s
250     rm -rf x86_64 noarch *.src.rpm .build* clog
251     git fetch
252   " repodir;
253
254   if not (git_has_local_changes repodir) then
255     sh "
256       cd %s
257       git pull --rebase
258     " repodir;
259
260 (* - XXX why did we do this here?
261   install_build_dependencies pkg;
262 *)
263
264   (* For rationale behind always bumping the spec file, see comment
265    * in 'fedora.ml'.
266    *)
267   let title =
268     if not (file_contains_string specfile rebuild_name) then
269       rebuild_name ^ " rebuild."
270     else
271       "Bump release and rebuild." in
272   sh "rpmdev-bumpspec -c %s %s" (quote title) specfile;
273
274   (* XXX Automate common specfile fixes. *)
275
276   sh "
277     cd %s
278     fedpkg commit -c
279     fedpkg push
280   " repodir