9046b27fe30f14e8d1ff35929aa431aad14a5b94
[alexxy/gromacs.git] / admin / containers / scripted_gmx_docker_builds.py
1 #!/usr/bin/env python
2 #
3 # This file is part of the GROMACS molecular simulation package.
4 #
5 # Copyright (c) 2020, by the GROMACS development team, led by
6 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 # and including many others, as listed in the AUTHORS file in the
8 # top-level source directory and at http://www.gromacs.org.
9 #
10 # GROMACS is free software; you can redistribute it and/or
11 # modify it under the terms of the GNU Lesser General Public License
12 # as published by the Free Software Foundation; either version 2.1
13 # of the License, or (at your option) any later version.
14 #
15 # GROMACS is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 # Lesser General Public License for more details.
19 #
20 # You should have received a copy of the GNU Lesser General Public
21 # License along with GROMACS; if not, see
22 # http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24 #
25 # If you want to redistribute modifications to GROMACS, please
26 # consider that scientific software is very special. Version
27 # control is crucial - bugs must be traceable. We will be happy to
28 # consider code for inclusion in the official distribution, but
29 # derived work must not be called official GROMACS. Details are found
30 # in the README & COPYING files - if they are missing, get the
31 # official version at http://www.gromacs.org.
32 #
33 # To help us fund GROMACS development, we humbly ask that you cite
34 # the research papers on the package. Check out http://www.gromacs.org.
35
36 """
37 Generates a set of docker images used for running GROMACS CI on Gitlab.
38 The images are prepared according to a selection of build configuration targets
39 that hope to cover a broad enough scope of different possible systems,
40 allowing us to check compiler types and versions, as well as libraries used
41 for accelerators and parallel communication systems. Each combinations is
42 described as an entry in the build_configs dictionary, with the script
43 analysing the logic and adding build stages as needed.
44
45 Based on the example script provided by the NVidia HPCCM repository.
46
47 Authors:
48     * Paul Bauer <paul.bauer.q@gmail.com>
49     * Eric Irrgang <ericirrgang@gmail.com>
50     * Joe Jordan <e.jjordan12@gmail.com>
51
52 Usage::
53
54     $ python3 scripted_gmx_docker_builds.py --help
55     $ python3 scripted_gmx_docker_builds.py --format docker > Dockerfile && docker build .
56     $ python3 scripted_gmx_docker_builds.py | docker build -
57
58 """
59
60 import argparse
61
62 import hpccm
63 import hpccm.config
64
65 try:
66     import utility
67 except ImportError:
68     raise RuntimeError(
69         'This module assumes availability of supporting modules in the same directory. Add the directory to '
70         'PYTHONPATH or invoke Python from within the module directory so module location can be resolved.')
71
72
73 # Parse command line arguments
74 parser = argparse.ArgumentParser(description='GROMACS CI image creation script', parents=[utility.parser])
75
76 parser.add_argument('--format', type=str, default='docker',
77                     choices=['docker', 'singularity'],
78                     help='Container specification format (default: docker)')
79
80 def main(args) -> hpccm.Stage:
81     # Create Stage
82     Stage0 = hpccm.Stage()
83
84     # Create string for base image tag
85     base_image_tag = str()
86
87     # Check if we use CUDA images or plain linux images
88     if (args.cuda is not None):
89         cuda_version_tag = 'nvidia/cuda:' + args.cuda + '-devel'
90         if (args.centos is not None):
91             cuda_version_tag += '-centos' + args.centos
92         elif (args.ubuntu is not None):
93             if ((args.cuda == '9.0') and (args.ubuntu == '18.04')):
94                 raise RuntimeError('Can not combine CUDA 9.0 and Ubuntu 18.04')
95             cuda_version_tag += '-ubuntu' + args.ubuntu
96         else:
97             raise RuntimeError('Logic error: no Linux distribution selected.')
98
99         base_image_tag = cuda_version_tag
100     else:
101         if (args.centos is not None):
102             base_image_tag = 'centos:centos' + args.centos
103         elif (args.ubuntu is not None):
104             base_image_tag = 'ubuntu:' + args.ubuntu
105         else:
106             raise RuntimeError('Logic error: no Linux distribution selected.')
107
108     Stage0 += hpccm.primitives.baseimage(image=base_image_tag)
109
110     # Install the GROMACS packages we always will need for our builds.
111     Stage0 += hpccm.building_blocks.packages(ospackages=['build-essential',
112                                                          'ccache',
113                                                          'git',
114                                                          'libfftw3-dev',
115                                                          'libhwloc-dev',
116                                                          'liblapack-dev',
117                                                          'libx11-dev',
118                                                          'moreutils',
119                                                          'ninja-build',
120                                                          'rsync',
121                                                          'valgrind',
122                                                          'wget',
123                                                          'xsltproc'])
124
125     # Add CMake to image
126     Stage0 += hpccm.building_blocks.cmake(eula=True, version=args.cmake)
127
128     # We always add Python3 and Pip
129     Stage0 += hpccm.building_blocks.python(python3=True, python2=False, devel=True)
130     Stage0 += hpccm.building_blocks.pip(upgrade=True, pip='pip3',
131                                         packages=['pytest', 'networkx', 'numpy'])
132
133     # Compiler
134     if (args.icc is not None):
135         raise RuntimeError('Intel compiler toolchain recipe not implemented yet')
136
137     if (args.llvm is not None):
138         # Build the default compiler if we don't need special support
139         if (args.tsan is None):
140             if (args.llvm == 3):
141                 if ((args.ubuntu is not None) and (args.ubuntu == '18.04')):
142                     raise RuntimeError('LLVM 3 and Ubuntu 18.04 can cause issues when used together')
143                 args.llvm = 3.6
144             compiler = hpccm.building_blocks.llvm(extra_repository=True, version=args.llvm)
145         # Build our own version instead to get TSAN + OMP
146         else:
147             compiler_branch = 'release_'+str(args.llvm)+'0'
148             compiler = hpccm.building_blocks.generic_cmake(repository='https://git.llvm.org/git/llvm.git',
149                     prefix='/usr/local', recursive=True, branch=compiler_branch,
150                     cmake_opts=['-D CMAKE_BUILD_TYPE=Release', '-D LLVM_ENABLE_PROJECTS="clang;openmp;clang-tools-extra"', '-D LIBOMP_TSAN_SUPPORT=on'],
151                     preconfigure=['export branch='+compiler_branch,
152                                   '(cd projects; git clone https://git.llvm.org/git/libcxx.git; cd libcxx; git checkout $branch)',
153                                   '(cd projects; git clone https://git.llvm.org/git/libcxxabi.git; cd libcxxabi; git checkout $branch)',
154                                   '(cd projects; git clone https://git.llvm.org/git/compiler-rt.git; cd compiler-rt; git checkout $branch)',
155                                   '(cd ..; git clone https://git.llvm.org/git/openmp.git; cd openmp; git checkout $branch)',
156                                   '(cd ..; git clone https://git.llvm.org/git/clang.git; cd clang; git checkout $branch)',
157                                   '(cd ..; git clone https://git.llvm.org/git/clang-tools-extra.git clang-tools-extra; cd clang-tools-extra; git checkout $branch)'],
158                     postinstall=['ln -s /usr/local/bin/clang++ /usr/local/bin/clang++-'+str(args.llvm),
159                                  'ln -s /usr/local/bin/clang-format /usr/local/bin/clang-format-'+str(args.llvm),
160                                  'ln -s /usr/local/bin/clang-tidy /usr/local/bin/clang-tidy-'+str(args.llvm),
161                                  'ln -s /usr/local/libexec/c++-analyzer /usr/local/bin/c++-analyzer-'+str(args.llvm)])
162
163
164     elif (args.gnu is not None):
165         compiler = hpccm.building_blocks.gnu(extra_repository=True,
166                                              version=args.gnu,
167                                              fortran=False)
168     else:
169         raise RuntimeError('Logic error: no compiler toolchain selected.')
170
171
172     Stage0 += compiler
173     # If we use the package version of LLVM, we need to install extra packages for it.
174     if (args.llvm is not None) and (args.tsan is None):
175         Stage0 += hpccm.building_blocks.packages(ospackages=['libomp-dev',
176                                                              'clang-format-'+str(args.llvm),
177                                                              'clang-tidy-'+str(args.llvm)])
178
179     # If needed, add MPI to the image
180     if (args.mpi is not None):
181         if args.mpi == 'openmpi':
182             use_cuda = False
183             if (args.cuda is not None):
184                 use_cuda = True
185
186             Stage0 += hpccm.building_blocks.openmpi(toolchain=compiler.toolchain, cuda=use_cuda, infiniband=False)
187         elif args.mpi == 'impi':
188             raise RuntimeError('Intel MPI recipe not implemented yet.')
189
190     # Add OpenCL environment if needed
191     if (args.opencl is not None):
192         if args.opencl == 'nvidia':
193             if (args.cuda is None):
194                 raise RuntimeError('Need Nvidia environment for Nvidia OpenCL image')
195
196             Stage0 += hpccm.building_blocks.packages(ospackages=['nvidia-opencl-dev'])
197
198         elif args.opencl == 'intel':
199             Stage0 += hpccm.building_blocks.packages(ospackages=['ocl-icd-opencl-dev', 'opencl-headers', 'beignet-opencl-icd'])
200         elif args.opencl == 'amd':
201             # Due to the wisdom of AMD, this needs to be done differently for the OS and version! Hurray!
202             # And they don't allow wget, so this branch is not taken for now! AMD, please allow me to use wget.
203             raise RuntimeError('AMD recipe can not be generated because they do not allow wget for getting the packages.')
204             # if args.ubuntu:
205             #     if args.ubuntu is not '16.04':
206             #         Stage0 += hpccm.building_blocks.generic_build(url='https://www2.ati.com/drivers/linux/ubuntu/'+args.ubuntu+'/amdgpu-pro-18.30-641594.tar.xz',
207             #                                                       install=['./amdgpu-install --opencl=legacy --headless -y'])
208             #     elif:
209             #         Stage0 += hpccm.building_blocks.generic_build(url='https://www2.ati.com/drivers/linux/ubuntu/amdgpu-pro-18.30-641594.tar.xz',
210             #                                                       install=['./amdgpu-install --opencl=legacy --headless -y'])
211             # elif args.centos:
212             #         Stage0 += hpccm.building_blocks.generic_build(url='https://www2.ati.com/drivers/linux/rhel'+args.centos'/amdgpu-pro-18.30-641594.tar.xz',
213             #                                                       install=['./amdgpu-install --opencl=legacy --headless -y'])
214
215         if (args.clfft is not None):
216             Stage0 += hpccm.building_blocks.generic_cmake(repository='https://github.com/clMathLibraries/clFFT.git',
217                                                           prefix='/usr/local', recursive=True, branch=args.clfft, directory='clFFT/src')
218
219     # Add documentation requirements (doxygen and sphinx + misc).
220     if (args.doxygen is not None):
221         if (args.doxygen == '1.8.5'):
222                 doxygen_commit = 'ed4ed873ab0e7f15116e2052119a6729d4589f7a'
223         elif (args.doxygen == '1.8.11'):
224                 doxygen_commit = 'a6d4f4df45febe588c38de37641513fd576b998f'
225         else:
226             raise RuntimeError('Need to provide either 1.8.5 or 1.8.11 as doxygen version.')
227         Stage0 += hpccm.building_blocks.packages(ospackages=['autoconf',
228                                                              'automake',
229                                                              'autopoint',
230                                                              'autotools-dev',
231                                                              'bison',
232                                                              'flex',
233                                                              'ghostscript',
234                                                              'graphviz',
235                                                              'help2man',
236                                                              'imagemagick',
237                                                              'libtool',
238                                                              'linkchecker',
239                                                              'mscgen',
240                                                              'm4',
241                                                              'texinfo',
242                                                              'texlive-latex-base',
243                                                              'texlive-latex-extra',
244                                                              'texlive-fonts-recommended',
245                                                              'texlive-fonts-extra'])
246         Stage0 += hpccm.building_blocks.generic_autotools(repository='https://github.com/westes/flex.git',
247                                                           commit='f7788a9a0ecccdc953ed12043ccb59ca25714018',
248                                                           prefix='/tmp/install-of-flex',
249                                                           configure_opts=['--disable-shared'],
250                                                           preconfigure=['./autogen.sh'])
251         Stage0 += hpccm.building_blocks.generic_autotools(repository='https://github.com/doxygen/doxygen.git',
252                                                           commit=doxygen_commit,
253                                                           prefix='',
254                                                           configure_opts=['--flex /tmp/install-of-flex/bin/flex', '--static'],
255                                                           postinstall=['sed -i \'/\"XPS\"/d;/\"PDF\"/d;/\"PS\"/d;/\"EPS\"/d;/disable ghostscript format types/d\' /etc/ImageMagick-6/policy.xml'])
256         Stage0 += hpccm.building_blocks.pip(pip='pip3', packages=['sphinx==1.6.1'])
257     
258     return Stage0
259
260
261 if __name__ == '__main__':
262     args = parser.parse_args()
263
264     container_recipe = main(args)
265
266     # Set container specification output format
267     hpccm.config.set_container_format(args.format)
268
269     # Output container specification
270     print(container_recipe)