Disable cross compilation for now
[ghc.git] / Jenkinsfile
1 #!groovy
2
3 /*
4   Jenkins dependencies:
5    * Pipeline Utility steps plugin
6
7   Linux (Debian) worker dependencies:
8    * xutils-dev curl automake autoconf libtool python3 python3-sphinx llvm-4.0
9
10    Requires approvals for:
11    * new net.sf.json.JSONObject
12
13 */
14
15 import net.sf.json.JSONObject
16
17 properties(
18   [
19     parameters(
20       [
21         booleanParam(name: 'build_docs', defaultValue: false, description: 'build and upload documentation'),
22         booleanParam(name: 'nightly', defaultValue: false, description: 'are we building a nightly?'),
23         booleanParam(name: 'runNofib', defaultValue: false, description: 'run nofib and archive results')
24       ])
25   ])
26
27
28 stage("Build source distribution") {
29   node(label: 'linux') {
30     stage("Checking out tree") {
31       checkout scm
32       sh """
33          git submodule update --init --recursive
34          mk/get-win32-tarballs.sh fetch all
35          """
36     }
37     stage("Configuring tree") {
38       sh """
39          ./boot
40          ./configure
41          """
42     }
43     stage("Build tarballs") {
44       def version = getMakeValue('make', 'ProjectVersion')
45       sh "make sdist"
46       sh "mv sdistprep/ghc-${version}-src.tar.xz ghc-src.tar.xz"
47       sh "mv sdistprep/ghc-${version}-testsuite.tar.xz ghc-testsuite.tar.xz"
48       sh "mv sdistprep/ghc-${version}-windows-extra-src.tar.xz ghc-win32-tarballs.tar.xz"
49
50       def json = new JSONObject()
51       json.put('dirName', "ghc-${version}" as String)
52       json.put('commit', resolveCommitSha('HEAD'))
53       writeJSON(file: 'src-dist.json', json: json)
54
55       def src_dist_files = 'ghc-src.tar.xz,ghc-win32-tarballs.tar.xz,src-dist.json'
56       stash(name: 'source-dist', includes: src_dist_files)
57       stash(name: 'testsuite-dist', includes: 'ghc-testsuite.tar.xz')
58       archiveArtifacts artifacts: src_dist_files
59       archiveArtifacts artifacts: 'ghc-testsuite.tar.xz'
60     }
61   }
62 }
63
64 parallel (
65   "linux x86-64"       : {
66     node(label: 'linux && amd64') {
67       buildAndTestGhc(targetTriple: 'x86_64-linux-gnu')
68       if (params.build_docs) {
69         updateReadTheDocs()
70         updateUsersGuide()
71       }
72     }
73   },
74   //"linux x86-64 -> aarch64 unreg" : {
75   //  node(label: 'linux && amd64') {buildGhc(crossCompiling: true, targetTriple: 'aarch64-linux-gnu', unreg: true)}
76   //},
77   //"linux x86-64 -> aarch64" : {
78   //  node(label: 'linux && amd64') {buildGhc(crossCompiling: true, targetTriple: 'aarch64-linux-gnu')}
79   //  node(label: 'linux && aarch64') {testGhc(targetTriple: 'aarch64-linux-gnu')}
80   //},
81   "aarch64"            : {
82     node(label: 'linux && aarch64') {buildGhc(targetTriple: 'aarch64-linux-gnu')}
83   },
84   "freebsd"            : {
85     node(label: 'freebsd && amd64') {
86       buildGhc(targetTriple: 'x86_64-portbld-freebsd11.0', makeCmd: 'gmake', disableLargeAddrSpace: true)
87     }
88   },
89   // Requires cygpath plugin?
90   "windows 64"         : {
91     node(label: 'windows && amd64') {
92       withMingw('MINGW64') { buildAndTestGhc(targetTriple: 'x86_64-w64-mingw32') }
93     }
94   },
95   "windows 32"         : {
96     node(label: 'windows && amd64') {
97       withMingw('MINGW32') { buildAndTestGhc(targetTriple: 'x86_64-pc-msys') }
98     }
99   },
100   /*
101   "osx"                : {
102     node(label: 'darwin') {buildGhc(targetTriple: 'x86_64-apple-darwin16.0.0')}
103   }
104   */
105 )
106
107 if (params.runNofib) {
108   node(label: 'linux && amd64 && perf') {
109     nofib(targetTriple: 'x86_64-linux-gnu')
110   }
111 }
112
113
114 def withMingw(String msystem, Closure f) {
115   // Derived from msys2's /etc/msystem
116   String msysRoot = 'C:\\msys64'
117   String carch, prefix, ghcPath
118   home = sh(script: 'echo -n $HOME', returnStdout: true)
119   if (msystem == 'MINGW32') {
120     prefix = "${msysRoot}\\mingw32"
121     carch = 'i686'
122     ghcPath = "${home}/ghc-8.2.1-i386/bin"
123   } else if (msystem == 'MINGW64') {
124     prefix = "${msysRoot}\\mingw64"
125     carch = 'x86_64'
126     ghcPath = "${home}/ghc-8.2.1-x86_64/bin"
127   } else {
128     fail
129   }
130   String chost = "${carch}-w64-mingw32"
131
132   withEnv(["MSYSTEM=${msystem}",
133            "PATH+mingw=${prefix}\\bin",
134            "PATH+ghc=${ghcPath}",
135            "MSYSTEM_PREFIX=${prefix}",
136            "MSYSTEM_CARCH=${carch}",
137            "MSYSTEM_CHOST=${chost}",
138            "MINGW_CHOST=${chost}",
139            "MINGW_PREFIX=${prefix}",
140            "MINGW_PACKAGE_PREFIX=mingw-w64-${carch}",
141            "CONFIG_SITE=${prefix}/etc/config.site"
142           ], f)
143 }
144
145 def installPackages(String[] pkgs) {
146   sh "cabal install -j${env.THREADS} --with-compiler=`pwd`/inplace/bin/ghc-stage2 --package-db=`pwd`/inplace/lib/package.conf.d ${pkgs.join(' ')}"
147 }
148
149 def buildAndTestGhc(params) {
150   buildGhc(params)
151   testGhc(params)
152 }
153
154 def buildGhc(params) {
155   String targetTriple = params?.targetTriple
156   boolean crossCompiling = params?.crossCompiling ?: false
157   boolean unreg = params?.unreg ?: false
158   boolean disableLargeAddrSpace = params?.disableLargeAddrSpace ?: false
159   String makeCmd = params?.makeCmd ?: 'make'
160
161   withGhcSrcDist() {
162     stage('Configure') {
163       sh 'echo $PATH'
164       sh "which ghc"
165
166       def speed = 'NORMAL'
167       if (params.nightly) {
168         speed = 'SLOW'
169       }
170       build_mk = """
171                 Validating=YES
172                 ValidateSpeed=${speed}
173                 ValidateHpc=NO
174                 BUILD_DPH=NO
175                 """
176       if (crossCompiling) {
177         build_mk += """
178                     # Cross compiling
179                     HADDOCK_DOCS=NO
180                     BUILD_SPHINX_HTML=NO
181                     BUILD_SPHINX_PDF=NO
182                     INTEGER_LIBRARY=integer-simple
183                     WITH_TERMINFO=NO
184                     """
185       }
186       writeFile(file: 'mk/build.mk', text: build_mk)
187
188       def configure_opts = []
189       if (crossCompiling) {
190         configure_opts += "--target=${targetTriple}"
191       }
192       if (disableLargeAddrSpace) {
193         configure_opts += '--disable-large-address-space'
194       }
195       if (unreg) {
196         configure_opts += '--enable-unregisterised'
197       }
198       sh "./configure ${configure_opts.join(' ')}"
199     }
200
201     stage('Build') {
202       sh "${makeCmd} -j${env.THREADS}"
203     }
204
205     stage('Prepare binary distribution') {
206       sh "${makeCmd} binary-dist"
207       def json = new JSONObject()
208       def tarPath = getMakeValue(makeCmd, 'BIN_DIST_PREP_TAR_COMP')
209       def tarName = sh(script: "basename ${tarPath}", returnStdout: true).trim()
210       json.put('tarName', tarName)
211       json.put('dirName', getMakeValue(makeCmd, 'BIN_DIST_NAME'))
212       json.put('ghcVersion', getMakeValue(makeCmd, 'ProjectVersion'))
213       json.put('targetPlatform', getMakeValue(makeCmd, 'TARGETPLATFORM'))
214       echo "${json}"
215       writeJSON(file: 'bindist.json', json: json)
216       // Write a file so we can easily file the tarball and bindist directory later
217       stash(name: "bindist-${targetTriple}", includes: "bindist.json,${tarName}")
218       archiveArtifacts artifacts: tarName
219     }
220   }
221 }
222
223 String getMakeValue(String makeCmd, String value) {
224   return sh(script: "${makeCmd} -s echo! VALUE=${value}", returnStdout: true)
225 }
226
227 def withTempDir(String name, Closure f) {
228   sh """
229      rm -Rf ${name} || true
230      mkdir ${name}
231      """
232   try {
233     dir(name) {
234       f()
235     }
236   } finally {
237     //sh "rm -Rf ${name}"
238   }
239 }
240
241 def withGhcSrcDist(Closure f) {
242   withTempDir('src-dist') {
243     stage('Unpack source distribution') {
244       unstash(name: "source-dist")
245       sh 'tar -xf ghc-src.tar.xz'
246       sh 'tar -xf ghc-win32-tarballs.tar.xz'
247     }
248
249     def metadata = readJSON file: 'src-dist.json'
250     dir(metadata.dirName) {
251       f()
252     }
253   }
254 }
255
256 def withGhcBinDist(String targetTriple, Closure f) {
257   withTempDir('bin-dist') {
258     unstash "bindist-${targetTriple}"
259     unstash "testsuite-dist"
260     def metadata = readJSON file: "bindist.json"
261     sh "tar -xf ${metadata.tarName}"
262     sh "tar -xf ghc-testsuite.tar.xz"
263     try {
264       dir(metadata.dirName) {
265         f()
266       }
267     } finally {
268       sh "rm -R ${metadata.dirName}"
269     }
270   }
271 }
272
273 def testGhc(params) {
274   String targetTriple = params?.targetTriple
275   // See Note [Spaces in TEST_HC]
276   String makeCmd = params?.makeCmd ?: 'make'
277   String instDir="${pwd()}/bindisttest/install   dir"
278   String testGhc="${instDir}/bin/ghc"
279
280   withGhcBinDist(targetTriple) {
281     stage('Configure') {
282       if (isUnix()) {
283           sh "./configure --prefix=\"${instDir}\""
284           sh "${makeCmd} install"
285       } else {
286           sh "mkdir -p \"${instDir}\""
287           sh "cp -a * \"${instDir}\""
288       }
289     }
290
291     stage('Install testsuite dependencies') {
292       if (params.nightly) {
293         def pkgs = ['mtl', 'parallel', 'parsec', 'primitive', 'QuickCheck',
294                     'random', 'regex-compat', 'syb', 'stm', 'utf8-string',
295                     'vector']
296         installPkgs pkgs
297       }
298     }
299
300     stage('Run testsuite') {
301       def target = 'test'
302       if (params.nightly) {
303         target = 'slowtest'
304       }
305       sh "${makeCmd} -Ctestsuite/tests LOCAL=0 BINDIST=YES THREADS=${env.THREADS} TEST_HC=\"${testGhc}\" JUNIT_FILE=../../testsuite.xml ${target}"
306       junit 'testsuite.xml'
307     }
308   }
309 }
310
311 def nofib(params) {
312   String targetTriple = params?.targetTriple
313   String makeCmd = params?.makeCmd ?: 'make'
314   withGhcBinDist(targetTriple) {
315     stage('Run nofib') {
316       installPkgs(['regex-compat'])
317       sh """
318          cd nofib
319          ${makeCmd} clean
320          ${makeCmd} boot
321          ${makeCmd} >../nofib.log 2>&1
322          """
323       archiveArtifacts artifacts: 'nofib.log'
324     }
325   }
326 }
327
328 def resolveCommitSha(String ref) {
329   return sh(script: "git rev-parse ${ref}", returnStdout: true).trim()
330 }
331
332 // Push update to ghc.readthedocs.org.
333 // Expects to be sitting in a build source tree.
334 def updateReadTheDocs() {
335   git clone 'git@github.com:bgamari/ghc-users-guide'
336   def commit = resolveCommitSha('HEAD')
337   sh """
338      export GHC_TREE=\$(pwd)
339      cd ghc-users-guide
340      ./export.sh
341      git commit -a -m \"Update to ghc commit ${commit}\" || true
342      git push
343      """
344 }
345
346 // Push update to downloads.haskell.org/~ghc/master/doc.
347 // Expects to be sitting in a configured source tree.
348 def updateUsersGuide() {
349   sh "${makeCmd} html haddock EXTRA_HADDOCK_OPTS=--hyperlinked-sources"
350   sh '''
351      out="$(mktemp -d)"
352      mkdir -p $out/libraries
353
354      cp -R docs/users_guide/build-html/users_guide $out/users-guide
355      for d in libraries/*; do
356          if [ ! -d $d/dist-install/doc ]; then continue; fi
357          mkdir -p $out/libraries/$(basename $d)
358          cp -R $d/dist-install/doc/*/* $out/libraries/\$(basename \$d)
359      done
360      cp -R libraries/*/dist-install/doc/* $out/libraries
361      chmod -R ugo+r $out
362
363      rsync -az $out/ downloads.haskell.org:public_html/master
364      rm -R $out
365      '''
366 }