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