(* Perform a complete Fedora OCaml rebuild, in build order. *) open Unix open Printf open Goaljobs open Config open Git open Fedora let branch = "master" let koji_target = "rawhide" (* The name of the rebuild, and also the magic substring that must * appear in the %changelog when the package has been rebuilt. *) let rebuild_name = "ocaml-4.02.1" (* Local repository that contains build dependencies. *) let yum_repo = "koji-rawhide" (* Packages that have problems. These block the packages and all * dependent packages. *) let blocked = [ "ocaml-pa-do"; (* orphaned *) "plplot"; (* FTBFS, unrelated to OCaml *) ] let blocked pkg = List.mem pkg blocked (* These packages are treated as if they have been rebuilt. *) let ignored = [ "ocaml-srpm-macros"; (* don't need to build this *) "ocaml"; (* rebuilt by hand *) "whenjobs"; (* obsolete *) ] let ignored pkg = List.mem pkg ignored (* List of OCaml-related source package names. *) let source_packages = let dirs = shlines "cd %s && ls -1d ocaml*" fedora_dir in dirs @ [ "alt-ergo"; "apron"; "brltty"; "coccinelle"; "coq"; "cduce"; "frama-c"; "gappalib-coq"; "graphviz"; "hevea"; "hivex"; "js-of-ocaml"; "plplot"; "virt-top"; "why3"; "flocq" (* no OCaml code, but needs to be rebuilt after Coq *); "guestfs-browser"; "libguestfs"; "virt-dmesg" ] (* Dependencies of each package. (pkg, [deps ...]) *) let pkg_deps = dependencies branch source_packages (* Remove blocked packages and packages which have a blocked package * as a dependency (recursively). *) let source_packages = let rec is_blocked pkg = if blocked pkg then true else ( let deps = List.assoc pkg pkg_deps in List.exists is_blocked deps ) in List.filter (fun pkg -> not (is_blocked pkg)) source_packages (* Short the dependencies lists so that the build order is stable * each time it runs. *) let pkg_deps = List.map (fun (pkg, deps) -> pkg, List.sort compare deps) pkg_deps (* Sort the source packages so that the packages with the largest * number of reverse dependencies [other packages that depend on it] * appear earlier in the list, on the basis that building these * packages first has the greatest advantage. *) let source_packages = let rdeps pkg = Utils.filter_map ( fun (rdep, deps) -> if List.mem pkg deps then Some rdep else None ) pkg_deps in let cmp p1 p2 = let r1 = rdeps p1 and r2 = rdeps p2 in let n1 = List.length r1 and n2 = List.length r2 in if n1 <> n2 then compare n2 n1 else compare p1 p2 in List.sort cmp source_packages let () = printf "final list of source packages = %s\n%!" (String.concat " " source_packages) (* We could make this a goal, but it's cheap enough to run it unconditionally. *) let install_build_dependencies pkg = sh "sudo yum clean all --disablerepo=\\* --enablerepo=%s" (quote yum_repo); sh "sudo yum-builddep -y --disablerepo=\\* --enablerepo=%s %s" (quote yum_repo) (fedora_specfile pkg branch) (* Unset MAKEFLAGS so it doesn't affect local builds. *) let () = Unix.putenv "MAKEFLAGS" "" (* Goal: rebuild all packages. *) let rec goal all () = let n = List.length source_packages in List.iteri ( fun i pkg -> require (rebuild_started pkg); printf "*** *** rebuilt %d/%d packages *** ***\n%!" (i+1) n ) source_packages (* Goal: That 'package' has been rebuilt and exists in Koji. *) and rebuilt pkg = let specfile = fedora_specfile pkg branch in (* Note: verrel may change as we go along, so don't assign it to * variable. *) (* Note the target must be both of these because the old verrel * could exist as a koji build without it having been part of the * rebuild. *) target (ignored pkg || (file_contains_string specfile rebuild_name && koji_build_state (fedora_verrel pkg branch) == `Complete)); (* Ignored packages are treated as if they have been rebuilt. *) if not (ignored pkg) then ( (* Start the rebuild. *) require (rebuild_started pkg); (* Wait for the build state to reach a conclusion. *) let rec loop () = match koji_build_state (fedora_verrel pkg branch) with | `No_such_build -> failwith (sprintf "rebuild of package %s: no build found" pkg) | `Building -> sleep 60; loop () | `Complete -> () | `Deleted -> failwith (sprintf "rebuild of package %s: deleted" pkg) | `Failed -> failwith (sprintf "rebuild of package %s: failed" pkg) | `Canceled -> failwith (sprintf "rebuild of package %s: canceled" pkg) in loop (); (* Wait for the build to appear in Koji repo. *) koji_wait_repo koji_target (fedora_verrel pkg branch) ) (* Goal: The rebuild of the package has started, but we haven't waited * for it to finish. *) and rebuild_started pkg = let deps = List.assoc pkg pkg_deps in let specfile = fedora_specfile pkg branch in (* Note the target must be both of these because the old verrel * could exist as a koji build without it having been part of the * rebuild. *) target (ignored pkg || (file_contains_string specfile rebuild_name && (match koji_build_state (fedora_verrel pkg branch) with | `Building | `Complete -> true | `Deleted | `Failed | `Canceled | `No_such_build -> false))); (* All dependent packages must have been fully rebuilt and in the * repo first. *) List.iter (fun dep -> require (rebuilt dep)) deps; (* Ignored packages are treated as if they have been rebuilt. *) if not (ignored pkg) then ( (* (* A local test build must succeed. *) require (local_build_succeeded pkg); *) (* local_build_succeeded normally does this ... *) require (specfile_updated pkg); (* Rebuild the package in Koji. Don't wait ... *) koji_build ~wait:false pkg branch; (* ... but the build doesn't appear in Koji (eg. in 'koji * buildinfo') until the SRPM has been built. This can take quite * some time. Loop here until the build appears. *) let rec loop () = match koji_build_state (fedora_verrel pkg branch) with | `No_such_build -> sleep 60; loop (); | `Building | `Complete -> () | `Deleted -> failwith (sprintf "rebuild of package %s: deleted" pkg) | `Failed -> failwith (sprintf "rebuild of package %s: failed" pkg) | `Canceled -> failwith (sprintf "rebuild of package %s: canceled" pkg) in loop () ) (* and local_build_succeeded pkg = (* The specfile must have been updated. *) require (specfile_updated pkg); let key = sprintf "fedora_ocaml_local_build_%s_%s" pkg (fedora_verrel pkg branch) in target (memory_exists key); install_build_dependencies pkg; (* Do a local test build to ensure the Koji build will work. *) sh " cd %s fedpkg local " (fedora_repo pkg branch); memory_set key "1" *) and specfile_updated pkg = let repodir = fedora_repo pkg branch in let specfile = fedora_specfile pkg branch in sh " cd %s rm -rf x86_64 noarch *.src.rpm .build* clog git fetch " repodir; if not (git_has_local_changes repodir) then sh " cd %s git pull --rebase " repodir; (* - XXX why did we do this here? install_build_dependencies pkg; *) (* For rationale behind always bumping the spec file, see comment * in 'fedora.ml'. *) let title = if not (file_contains_string specfile rebuild_name) then rebuild_name ^ " rebuild." else "Bump release and rebuild." in sh "rpmdev-bumpspec -c %s %s" (quote title) specfile; (* XXX Automate common specfile fixes. *) sh " cd %s fedpkg commit -c fedpkg push " repodir