1 # Hydra build file for coccinelle
3 { nixpkgs ? "/etc/nixos/nixpkgs"
4 , cocciSrc ? { outPath = ./.; revCount = 1234; gitTag = "abcdef"; }
5 , testsSrc ? { outPath = ../big-tests; rev = 1234; }
6 , officialRelease ? false
7 , performRegress ? true
14 version = builtins.readFile ./version;
15 versionSuffix = if officialRelease then "" else "pre${toString cocciSrc.revCount}-${cocciSrc.gitTag}";
19 # Source release (tarball)
22 # The source tarball taken from the repository.
23 # The tarball should actually be compilable using
24 # ./configure && make depend && make opt && make install
25 # on systems other than nix.
27 let pkgs = import nixpkgs { };
28 in with pkgs; with ocamlPackages; releaseTools.sourceTarball {
29 name = "coccinelle-tarball";
31 inherit officialRelease;
33 inherit versionSuffix;
36 ocaml findlib menhir python
37 texLiveFull # for building the documentation
38 pkgconfig # for the autoconf macros
43 export HOME=$TMPDIR # the latex installation needs to write to the $HOME directory, so rename it here
46 dontCopyDist = 1; # we'll copy the tarball to the tarballs folder ourselves (and rename it)
48 export HOME=$PREVHOME # restore the home directory
50 mkdir -p "$out/tarballs"
52 # rename the tarball to give it a version-specific name
53 cp coccinelle-*.tar.gz "$out/tarballs/coccinelle-${version}${versionSuffix}.tar.gz"
59 # Helper functions for building configurations
62 selOcamlDefault = orig: orig.ocamlPackages;
63 selOcaml400 = orig: orig.ocamlPackages_4_00_0;
64 selOcaml312 = orig: orig.ocamlPackages_3_12_1;
65 selOcaml311 = orig: orig.ocamlPackages_3_11_2;
66 selOcaml310 = orig: orig.ocamlPackages_3_10_0;
68 selCommonOcamlPkgs = ocamlPackages: with ocamlPackages; [
69 findlib menhir ocaml_sexplib
72 selMinimalOcamlPkgs = ocamlPackages: with ocamlPackages; [
76 selAllOcamlPkgs = ocamlPackages: with ocamlPackages; [
77 findlib menhir ocaml_sexplib ocaml_pcre pycaml
80 selCommonInputs = pkgs: [ pkgs.pkgconfig pkgs.pcre ];
82 selDefaultShell = pkgs: pkgs.stdenv.shell;
84 selPythonNone = pkgs: [];
85 selPythonDefault = pkgs: [ pkgs.python ];
86 selPython2 = pkgs: [ pkgs.python27 ];
87 selPython3 = pkgs: [ pkgs.python3 ];
89 # creates a configuration for a given python version
90 mkCfgPython = f: pkgs: with (f pkgs); {
91 inherit name pythons flags;
93 ocamls = selCommonOcamlPkgs pkgs.ocamlPackages ++ [ pkgs.ocamlPackages.pycaml ];
94 selOcaml = selOcamlDefault;
95 extras = selCommonInputs pkgs;
96 shell = selDefaultShell pkgs;
100 # creates a configuration for a given ocaml version
101 mkCfgOcaml = { name, selOcaml, flags }: pkgs: {
102 inherit flags selOcaml;
104 name = "ocaml-${name}";
105 pythons = selPythonDefault pkgs;
106 ocamls = selMinimalOcamlPkgs pkgs.ocamlPackages;
107 extras = selCommonInputs pkgs;
108 shell = selDefaultShell pkgs;
112 # creates a default configuration with additional flags
113 mkCfgDefault = { name, flags, extra ? {} }: pkgs: {
115 pythons = selPythonDefault pkgs;
116 ocamls = selAllOcamlPkgs pkgs.ocamlPackages;
117 selOcaml = selOcamlDefault;
118 extras = selCommonInputs pkgs;
119 shell = selDefaultShell pkgs;
123 # creates a minimal configuration with additional flags
124 mkCfgMinimal = { name, flags }: pkgs: {
128 selOcaml = selOcamlDefault;
130 shell = selDefaultShell pkgs;
134 # creates a configuration for the given ocaml packages
135 mkCfgPackage = { name, ocamls, flags }: pkgs: {
137 pythons = selPythonDefault pkgs;
138 ocamls = selMinimalOcamlPkgs pkgs.ocamlPackages ++ ocamls pkgs.ocamlPackages;
139 selOcaml = selOcamlDefault;
140 extras = selCommonInputs pkgs;
141 shell = selDefaultShell pkgs;
145 # build the project using the given shell
146 # it takes a minimal configuration, but then with all the
147 # libraries that trigger features of coccinelle to be enabled.
148 mkCfgShell = { name, selShell }: pkgs: {
150 pythons = selPythonDefault pkgs;
151 ocamls = selMinimalOcamlPkgs pkgs.ocamlPackages;
152 selOcaml = selOcamlDefault;
154 extras = [ pkgs.pcre ];
155 shell = selShell pkgs;
159 # creates a configuration with multiple ocaml versions: this gives
160 # conflicts. This is just a test to see whether our build system is
161 # not too much confused in this case. It seems at least that ocamlfind
162 # cannot be used in this setting.
165 selOcaml = pkgs: ocamlPkgSel: with (ocamlPkgSel pkgs); ocaml;
166 selPkgs = pkgs: ocamlPkgSel: with (ocamlPkgSel pkgs); [ menhir ];
170 ocamls = pkgs.lib.concatMap (selPkgs pkgs) sels;
171 selOcaml = selOcamlDefault;
173 extras = selCommonInputs pkgs ++ map (selOcaml pkgs) sels;
174 shell = selDefaultShell pkgs;
183 defaultCfg = mkCfgDefault { name = "default"; flags = []; };
184 debugCfg = mkCfgDefault { name = "debug"; flags = [ "--enable-release=no" ]; };
185 wrappersCfg = mkCfgDefault { name = "wrappers"; flags = [ "--enable-python" "--enable-ocaml" "--without-pkg-config" "--without-ocamlfind" ]; };
186 manyOcamlCfg = mkCfgManyOcaml [ selOcaml400 selOcaml312 selOcaml311 selOcaml310 ];
188 minimalCfgs = map mkCfgMinimal [
189 { name = "minimal"; flags = []; }
190 { name = "noocamlscripting"; flags = [ "--disable-ocaml" ]; }
193 # Several configurations testing different python versions.
194 # We exlicitly pass the "--enable-python" flag so that the
195 # build should fail if no suitable python can be detected.
198 ( _ : { name = "no-python"; pythons = []; flags = []; })
201 name = "python2-local";
202 pythons = selPython2 pkgs;
203 flags = [ "--enable-python" "--disable-pycaml" ];
207 name = "python3-local";
208 pythons = selPython3 pkgs;
209 flags = [ "--enable-python" "--disable-pycaml" ];
213 name = "python3-global";
214 pythons = selPython3 pkgs;
215 flags = [ "--enable-python" ];
219 name = "python-nopkgconfig";
220 pythons = selPython2 pkgs;
221 flags = [ "--enable-python" "--without-pkg-config" ];
224 # disabled because this combination does not work in NixOS
226 # name = "many-pythons";
227 # pythons = selPython3 pkgs ++ selPython2 pkgs;
228 # flags = [ "--with-python=python3" ];
232 # Several configurations testing different OCaml versions.
233 # These versions ship with minimal global packages in order
234 # to thest the bundled packages with these ocaml versions.
235 ocamlCfgs = map mkCfgOcaml [
236 { name = "400nat"; selOcaml = selOcaml400; flags = [ "--enable-release=yes" ]; }
237 { name = "400byt"; selOcaml = selOcaml400; flags = []; }
238 { name = "312"; selOcaml = selOcaml312; flags = []; }
239 { name = "311"; selOcaml = selOcaml311; flags = [ "--enable-release=yes" ]; }
240 { name = "310"; selOcaml = selOcaml310; flags = []; }
243 # Several configurations testing different available
245 pkgCfgs = map mkCfgPackage [
246 { name = "pcre"; ocamls = ps: [ ps.ocaml_pcre ]; flags = [ "--enable-pcre-syntax" ]; }
247 { name = "sexplib"; ocamls = ps: [ ps.ocaml_sexplib ]; flags = [ "--enable-sexplib" ]; }
248 { name = "pycaml"; ocamls = ps: [ ps.pycaml ]; flags = [ "--enable-pycaml" ]; }
251 # Tests using several different types of shells.
252 shellCfgs = map mkCfgShell [
253 { name = "bash"; selShell = pkgs: "${pkgs.bash}/bin/bash"; }
254 { name = "dash"; selShell = pkgs: "${pkgs.dash}/bin/dash"; }
255 { name = "zsh"; selShell = pkgs: "${pkgs.zsh}/bin/zsh"; }
257 # the configure script is not compatible with tcsh
258 # { name = "tcsh"; selShell = pkgs: "${pkgs.tcsh}/bin/tcsh"; }
262 # Configurations for the compilation of coccinelle using ocamlbuild.
265 ocamlbuildZeroCfg = mkCfgMinimal {
266 name = "ocamlbuild-zero";
267 flags = [ "--enable-ocamlbuild" "--enable-release" ];
270 ocamlbuildFullCfg = mkCfgDefault {
271 name = "ocamlbuild-full";
272 flags = [ "--enable-ocamlbuild" "--enable-release" ];
275 ocamlbuildCfgs = map mkCfgOcaml [
276 { name = "ocamlbuild-400nat"; selOcaml = selOcaml400;
277 flags = [ "--enable-ocamlbuild" "--enable-release=yes" ]; }
278 { name = "ocamlbuild-400byte"; selOcaml = selOcaml400;
279 flags = [ "--enable-ocamlbuild" ]; }
280 { name = "ocamlbuild-312"; selOcaml = selOcaml312;
281 flags = [ "--enable-ocamlbuild" "--enable-release" ]; }
282 { name = "ocamlbuild-311"; selOcaml = selOcaml311;
283 flags = [ "--enable-ocamlbuild" ]; }
284 { name = "ocamlbuild-310"; selOcaml = selOcaml310;
285 flags = [ "--enable-ocamlbuild" "--enable-release" ]; }
286 ] ++ [ ocamlbuildZeroCfg ocamlbuildFullCfg ];
289 [ debugCfg manyOcamlCfg ]
291 ++ ocamlCfgs ++ pythonCfgs
292 ++ pkgCfgs ++ shellCfgs
297 # Builds for specific configurations
300 # builds coccinelle, parameterized over the ocaml and python packages, and the configure flags.
301 # the result should be a usable nix-expression
303 # mkConfiguration is a function that takes the nix package collection of the build
304 # (called 'pkgs') and results in a record containing:
305 # name of the configuration, python packages, ocaml packages selection function
306 # (which takes the original 'pkgs' as parameter), and ocaml packages. The selection
307 # function is used by 'mkConfiguration' to determine the appropriate ocamlPackages
309 mkBuild = mkConfiguration: { system ? builtins.currentSystem }:
310 let pkgs = import nixpkgs {
312 config.packageOverrides = orig : {
313 ocamlPackages = cfg.selOcaml orig;
316 cfg = mkConfiguration pkgs;
317 flags = [ "--enable-release=world" ] ++ cfg.flags;
318 in with pkgs; releaseTools.nixBuild ({
320 name = "cocci-build-${cfg.name}";
322 enableParallelBuilding = true;
323 buildInputs = cfg.extras ++ [ ncurses ocamlPackages.ocaml ] ++ cfg.ocamls ++ cfg.pythons;
324 configureFlags = pkgs.lib.concatStringsSep " " flags; # hmm, flags are now not allowed to contain spaces
328 mkdir -p "$out/nix-support/"
329 touch "$out/nix-support/make.log"
330 echo "report log $out/nix-support/result.log" >> "$out/nix-support/hydra-build-products"
332 make all 2> >(tee -a "$out/nix-support/make.log" >&2)
335 # changes the shell in some of the scripts to the configured one
337 echo "patching the shell in scripts to: ${cfg.shell}"
338 for script in configure scripts/spatch.sh.in scripts/genversion.sh \
339 setup/fake-subst.sh setup/fake-menhir.sh setup/fake-pdflatex.sh; do
340 substituteInPlace $script --replace '#! /bin/sh' '#! ${cfg.shell}'
343 } // cfg.extraAttrs);
345 build = mkBuild defaultCfg;
346 altBuilds = map mkBuild altCfgs;
347 allBuilds = [ build ] ++ altBuilds;
349 # compile with ocaml profiling turned on and then running the
350 # test suite to collect results.
351 profileCfg = mkCfgDefault {
353 flags = [ "--enable-release=profile" ];
356 mkdir -p "$out/nix-support"
357 cp ocamlprof.dump "$out/ocamlprof.dump"
358 echo "file binary $out/ocamlprof.dump" >> "$out/nix-support/hydra-build-products"
362 profile = mkBuild profileCfg {};
369 # package builder for Debian-based OS'ses
371 system: diskImageFun:
373 with import nixpkgs { inherit system; };
374 releaseTools.debBuild {
375 name = "coccinelle-deb";
377 diskImage = diskImageFun vmTools.diskImageFuns {
378 extraPackages = [ "python" "python-support" "ocaml-nox" "ocaml-findlib" ];
380 debRequires = [ "python" "python-support" "ocaml-nox" "ocaml-findlib" ];
390 makeDeb_i686 = makeDeb "i686-linux";
391 makeDeb_x86_64 = makeDeb "x86_64-linux";
393 # different debian builds
394 # deb_ubuntu1010_i386 = makeDeb_i686 (disk: disk.ubuntu1010i386);
395 # deb_ubuntu1010_x86_64 = makeDeb_x86_64 (disk: disk.ubuntu1010x86_64);
403 argsfun: { system ? builtins.currentSystem }:
404 let pkgs = import nixpkgs { inherit system; };
405 args = argsfun pkgs system;
406 name = "${args.name}-${version}${versionSuffix}";
407 in pkgs.stdenv.mkDerivation ({
408 phases = [ "runPhase" ];
412 mkdir -p "$out/nix-support"
413 touch "$TMPDIR/result.log"
414 exec > >(tee -a "$TMPDIR/result.log") 2> >(tee -a "$TMPDIR/result.log" >&2)
416 cp "$TMPDIR/result.log" "$out/"
417 echo "report log $out/result.log" >> "$out/nix-support/hydra-build-products"
418 echo "$name" > "$out/nix-support/hydra-release-name"
422 description = "Coccinelle post-build task";
423 schedulingPriority = 8;
425 } // args // { inherit name; });
427 mkReport = inputs: mkTask (pkgs: _: with pkgs;
428 let builds = map (i: i { inherit (pkgs.stdenv) system; }) inputs; in {
432 echo "collecting logs"
433 for build in ${lib.concatStringsSep " " builds}; do
434 echo "log: $build/nix-support/make.log"
435 cat "$build/nix-support/make.log"
438 echo "grepping OCaml warnings"
439 if grep -2 "Warning " "$TMPDIR/result.log"
441 echo "found warnings!"
444 echo "there are apparently no significant warnings"
449 description = "Analysis of the coccinelle build reports";
450 schedulingPriority = 5;
454 report = mkReport allBuilds;
461 # Produces regression test results, which can be positive or
462 # negative. The build should succeed regardless of the outcome
463 # of individual tests unless coccinelle is horribly broken.
464 # The resulting files are stored in a tarball so that it allows
466 mkRegress = cocciSelect: mkTask (pkgs: system: with pkgs;
467 let coccinelle = cocciSelect { inherit system; };
469 name = "regression-${toString testsSrc.rev}";
470 buildInputs = [ coccinelle ];
473 # prepare a writeable tests directory
474 # as this directory contains large
475 # files, we'll create links to the
477 mkdir -p "$TMPDIR/tests"
478 cp -rs ${testsSrc}/* "$TMPDIR/tests/"
479 chmod -R u+w "$TMPDIR/tests/"
482 # initialize essential environment variables
484 export COCCINELLE_HOME=${coccinelle}/share/coccinelle
485 export COCCIDIR=$TMPDIR
486 export SPATCH=${coccinelle}/bin/spatch.opt
487 export ISO=${coccinelle}/share/coccinelle/standard.iso
488 export DEFS=${coccinelle}/share/coccinelle/standard.h
490 # generate the test outcomes using a parallel build
491 make -e all -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
493 # collect the results
494 # note: the tarball is likely to contain useless
495 # symbolic links to files in the nix store. We therefore
496 # delete these symlinks. As a result, you should be able
497 # to unpack the tarball in the tests directory.
498 find "$TMPDIR/tests" -depth -type l -delete
500 tar -czf "$out/results.tar.gz" ./tests
501 echo "file binary-dist $out/results.tar.gz" >> "$out/nix-support/hydra-build-products"
505 description = "Regression test of Coccinelle";
506 schedulingPriority = 8;
510 # Checks whether the regression tests meet our expectations.
511 # If the set of failed tests is different than specified in
512 # the tests repository, this check fails.
513 checkRegress = regressSelect: mkTask (pkgs: system: with pkgs;
514 let regress = regressSelect { inherit system; };
516 name = "test-${toString testsSrc.rev}";
519 # prepare a writeable tests directory
520 # as this directory contains large
521 # files, we'll create links to the
523 mkdir -p "$TMPDIR/tests"
524 cp -rs ${testsSrc}/* "$TMPDIR/tests/"
525 chmod -R u+w "$TMPDIR/tests/"
527 # extract the outcome of the regression test over it
528 echo "reconstructing regression directory"
530 tar xfz "${regress}/results.tar.gz"
533 echo "analyzing results"
536 echo "verifying the outcome"
541 description = "Regression test of Coccinelle";
542 schedulingPriority = 8;
546 regress = mkRegress build;
547 test = checkRegress regress;
551 # Performing release actions
555 let pkgs = import nixpkgs { };
556 name = "release-${version}${versionSuffix}";
557 in with pkgs; releaseTools.nixBuild {
560 buildInputs = with ocamlPackages; [
561 pkgconfig ncurses texLiveFull
565 configureFlags = "--enable-release";
568 export TARGETDIR="$TMPDIR/dists"
571 make prerelease GIT=echo TMP=$TARGETDIR
572 make release GIT=echo TMP=$TARGETDIR
573 make package TMP=$TARGETDIR
577 mkdir -p "$out/nix-support/"
578 echo "cocci-dist-${version}" > "$out/nix-support/hydra-release-name"
579 cp $TMPDIR/dists/*.tgz "$out/"
580 for file in $out/*.tgz; do
581 echo "file binary-dist $file" >> $out/nix-support/hydra-build-products
590 # collections of build tasks
601 # artificial dependency on report to ensure that we are not going through
602 # an expensive regression test when there is already something wrong with
604 reportFirst = x : if report == null then x else x;
605 testAttrs = reportFirst {
610 in basicAttrs // (if performRegress then testAttrs else {})