Skip to content

Commit fb11417

Browse files
authored
Merge pull request #2937 from easybuilders/4.7.x
release EasyBuild v4.7.2
2 parents 893400f + 6e2cf1e commit fb11417

File tree

15 files changed

+442
-26
lines changed

15 files changed

+442
-26
lines changed

RELEASE_NOTES

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,25 @@ For more detailed information, please see the git log.
33

44
These release notes can also be consulted at http://easybuild.readthedocs.org/en/latest/Release_notes.html.
55

6-
The latest version of easybuild-easyblocks provides 248 software-specific easyblocks and 39 generic easyblocks.
6+
The latest version of easybuild-easyblocks provides 248 software-specific easyblocks and 41 generic easyblocks.
7+
8+
9+
v4.7.2 (27 May 2023)
10+
--------------------
11+
12+
- new generic easyblock for installing Rust crates with cargo: Cargo and CargoPythonPackage (#2902, #2934)
13+
- minor enhancements and updates, including:
14+
- let MATLAB easyblock raise an error if the MATLAB installation key is not provided (#2905)
15+
- print message to inform that GPU package (instead of Kokkos) is used for LAMMPS (#2906)
16+
- enhance PyTorch easyblock to use FlexiBLAS for PyTorch >= 1.11.0 (#2915)
17+
- various bug fixes, including:
18+
- use custom RPATH sanity check for Go packages that doesn't actually check for an RPATH section in the binary (#2913)
19+
- use string '0' to avoid problems when openssl version is not determined (#2914)
20+
- update GCC easyblock to ensure that --sysroot is passed to linker (but only when it needs to be) (#2921)
21+
- add output log to MATLAB installs, actually parse for common errors (#2924)
22+
- enhance Gurobi easyblock to allow using $EB_GUROBI_LICENSE_FILE environment variable (#2926)
23+
- force building torchvision with CUDA support if CUDA is included as dependency by setting `$FORCE_CUDA` (#2931)
24+
- fix exec permission on files in arch bindir for COMSOL (#2932)
725

826
v4.7.1 (March 20th 2023)
927
------------------------

easybuild/easyblocks/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
# recent setuptools versions will *TRANSFORM* something like 'X.Y.Zdev' into 'X.Y.Z.dev0', with a warning like
4444
# UserWarning: Normalizing '2.4.0dev' to '2.4.0.dev0'
4545
# This causes problems further up the dependency chain...
46-
VERSION = LooseVersion('4.7.1')
46+
VERSION = LooseVersion('4.7.2')
4747
UNKNOWN = 'UNKNOWN'
4848

4949

easybuild/easyblocks/c/comsol.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ def install_step(self):
114114
# make sure setup script is executable
115115
adjust_permissions(setup_script, stat.S_IXUSR)
116116

117+
# make sure binaries in arch bindir is executable
118+
archpath = os.path.join(self.start_dir, 'bin', 'glnxa64')
119+
adjust_permissions(os.path.join(archpath, 'inflate'), stat.S_IXUSR)
120+
adjust_permissions(os.path.join(archpath, 'setuplauncher'), stat.S_IXUSR)
121+
117122
# make sure $DISPLAY is not defined, which may lead to (hard to trace) problems
118123
# this is a workaround for not being able to specify --nodisplay to the install scripts
119124
env.unset_env_vars(['DISPLAY'])

easybuild/easyblocks/g/gcc.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from easybuild.tools.build_log import EasyBuildError
4949
from easybuild.tools.config import build_option
5050
from easybuild.tools.filetools import apply_regex_substitutions, change_dir, copy_file, move_file, symlink
51-
from easybuild.tools.filetools import which, write_file
51+
from easybuild.tools.filetools import which, read_file, write_file
5252
from easybuild.tools.modules import get_software_root
5353
from easybuild.tools.run import run_cmd
5454
from easybuild.tools.systemtools import RISCV, check_os_dependency, get_cpu_architecture, get_cpu_family
@@ -357,8 +357,19 @@ def configure_step(self):
357357
# (see https://gcc.gnu.org/install/configure.html)
358358
self.cfg.update('configopts', '--with-sysroot=%s' % sysroot)
359359

360-
# avoid that --sysroot is passed to linker by patching value for SYSROOT_SPEC in gcc/gcc.c
361-
apply_regex_substitutions(os.path.join('gcc', 'gcc.c'), [('--sysroot=%R', '')])
360+
libc_so_candidates = [os.path.join(sysroot, x, 'libc.so') for x in
361+
['lib', 'lib64', os.path.join('usr', 'lib'), os.path.join('usr', 'lib64')]]
362+
for libc_so in libc_so_candidates:
363+
if os.path.exists(libc_so):
364+
# only patch gcc.c or gcc.cc if entries in libc.so are prefixed with sysroot
365+
if '\nGROUP ( ' + sysroot in read_file(libc_so):
366+
gccfile = os.path.join('gcc', 'gcc.c')
367+
# renamed to gcc.cc in GCC 12
368+
if not os.path.exists(gccfile):
369+
gccfile = os.path.join('gcc', 'gcc.cc')
370+
# avoid that --sysroot is passed to linker by patching value for SYSROOT_SPEC in gcc/gcc.c*
371+
apply_regex_substitutions(gccfile, [('--sysroot=%R', '')])
372+
break
362373

363374
# prefix dynamic linkers with sysroot
364375
# this patches lines like:

easybuild/easyblocks/g/gurobi.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,25 @@ def __init__(self, *args, **kwargs):
5555
"""Easyblock constructor, define custom class variables specific to Gurobi."""
5656
super(EB_Gurobi, self).__init__(*args, **kwargs)
5757

58-
self.license_file = self.cfg['license_file']
58+
# make sure license file is available
59+
self.orig_license_file = self.cfg['license_file']
60+
if self.orig_license_file is None:
61+
self.orig_license_file = os.getenv('EB_GUROBI_LICENSE_FILE', None)
5962

6063
if self.cfg['copy_license_file']:
6164
self.license_file = os.path.join(self.installdir, 'gurobi.lic')
65+
else:
66+
self.license_file = self.orig_license_file
6267

6368
def install_step(self):
6469
"""Install Gurobi and license file."""
65-
66-
# make sure license file is available
67-
if self.cfg['license_file'] is None or not os.path.exists(self.cfg['license_file']):
68-
raise EasyBuildError("No existing license file specified: %s", self.cfg['license_file'])
69-
7070
super(EB_Gurobi, self).install_step()
7171

7272
if self.cfg['copy_license_file']:
73-
copy_file(self.cfg['license_file'], self.license_file)
73+
if self.orig_license_file is None or not os.path.exists(self.orig_license_file):
74+
raise EasyBuildError("No existing license file specified: %s", self.orig_license_file)
75+
76+
copy_file(self.orig_license_file, self.license_file)
7477

7578
if get_software_root('Python'):
7679
run_cmd("python setup.py install --prefix=%s" % self.installdir)
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
##
2+
# Copyright 2009-2023 Ghent University
3+
#
4+
# This file is part of EasyBuild,
5+
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
6+
# with support of Ghent University (http://ugent.be/hpc),
7+
# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be),
8+
# Flemish Research Foundation (FWO) (http://www.fwo.be/en)
9+
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
10+
#
11+
# https://github.com/easybuilders/easybuild
12+
#
13+
# EasyBuild is free software: you can redistribute it and/or modify
14+
# it under the terms of the GNU General Public License as published by
15+
# the Free Software Foundation v2.
16+
#
17+
# EasyBuild is distributed in the hope that it will be useful,
18+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
# GNU General Public License for more details.
21+
#
22+
# You should have received a copy of the GNU General Public License
23+
# along with EasyBuild. If not, see <http://www.gnu.org/licenses/>.
24+
##
25+
"""
26+
EasyBuild support for installing Cargo packages (Rust lang package system)
27+
28+
@author: Mikael Oehman (Chalmers University of Technology)
29+
"""
30+
31+
import os
32+
33+
import easybuild.tools.environment as env
34+
from easybuild.tools.build_log import EasyBuildError
35+
from easybuild.framework.easyconfig import CUSTOM
36+
from easybuild.framework.extensioneasyblock import ExtensionEasyBlock
37+
from easybuild.tools.filetools import extract_file, change_dir
38+
from easybuild.tools.run import run_cmd
39+
from easybuild.tools.config import build_option
40+
from easybuild.tools.filetools import write_file, compute_checksum
41+
42+
CRATESIO_SOURCE = "https://crates.io/api/v1/crates"
43+
44+
45+
class Cargo(ExtensionEasyBlock):
46+
"""Support for installing Cargo packages (Rust)"""
47+
48+
@staticmethod
49+
def extra_options(extra_vars=None):
50+
"""Define extra easyconfig parameters specific to Cargo"""
51+
extra_vars = ExtensionEasyBlock.extra_options(extra_vars)
52+
extra_vars.update({
53+
'enable_tests': [True, "Enable building of tests", CUSTOM],
54+
'offline': [True, "Build offline", CUSTOM],
55+
'lto': [None, "Override default LTO flag ('fat', 'thin', 'off')", CUSTOM],
56+
'crates': [[], "List of (crate, version, [repo, rev]) tuples to use", CUSTOM],
57+
})
58+
59+
return extra_vars
60+
61+
def __init__(self, *args, **kwargs):
62+
"""Constructor for Cargo easyblock."""
63+
super(Cargo, self).__init__(*args, **kwargs)
64+
self.cargo_home = os.path.join(self.builddir, '.cargo')
65+
env.setvar('CARGO_HOME', self.cargo_home)
66+
env.setvar('RUSTC', 'rustc')
67+
env.setvar('RUSTDOC', 'rustdoc')
68+
env.setvar('RUSTFMT', 'rustfmt')
69+
optarch = build_option('optarch')
70+
if not optarch:
71+
optarch = 'native'
72+
env.setvar('RUSTFLAGS', '-C target-cpu=%s' % optarch)
73+
env.setvar('RUST_LOG', 'DEBUG')
74+
env.setvar('RUST_BACKTRACE', '1')
75+
76+
# Populate sources from "crates" list of tuples (only once)
77+
if self.cfg['crates']:
78+
# copy list of crates, so we can wipe 'crates' easyconfig parameter,
79+
# to avoid that creates are processed into 'sources' easyconfig parameter again
80+
# when easyblock is initialized again using same parsed easyconfig
81+
# (for example when check_sha256_checksums function is called, like in easyconfigs test suite)
82+
self.crates = self.cfg['crates'][:]
83+
sources = []
84+
for crate_info in self.cfg['crates']:
85+
if len(crate_info) == 2:
86+
crate, version = crate_info
87+
sources.append({
88+
'download_filename': crate + '/' + version + '/download',
89+
'filename': crate + '-' + version + '.tar.gz',
90+
'source_urls': [CRATESIO_SOURCE],
91+
'alt_location': 'crates.io',
92+
})
93+
else:
94+
crate, version, repo, rev = crate_info
95+
url, repo_name_git = repo.rsplit('/', maxsplit=1)
96+
sources.append({
97+
'git_config': {'url': url, 'repo_name': repo_name_git[:-4], 'commit': rev},
98+
'filename': crate + '-' + version + '.tar.gz',
99+
'source_urls': [CRATESIO_SOURCE],
100+
})
101+
102+
self.cfg.update('sources', sources)
103+
104+
# set 'crates' easyconfig parameter to empty list to prevent re-processing into sources
105+
self.cfg['crates'] = []
106+
107+
def extract_step(self):
108+
"""
109+
Unpack the source files and populate them with required .cargo-checksum.json if offline
110+
"""
111+
if self.cfg['offline']:
112+
self.log.info("Setting vendored crates dir")
113+
# Replace crates-io with vendored sources using build dir wide toml file in CARGO_HOME
114+
# because the rust source subdirectories might differ with python packages
115+
config_toml = os.path.join(self.cargo_home, 'config.toml')
116+
write_file(config_toml, '[source.vendored-sources]\ndirectory = "%s"\n\n' % self.builddir, append=True)
117+
write_file(config_toml, '[source.crates-io]\nreplace-with = "vendored-sources"\n\n', append=True)
118+
119+
# also vendor sources from other git sources (could be many crates for one git source)
120+
git_sources = set()
121+
for crate_info in self.crates:
122+
if len(crate_info) == 4:
123+
_, _, repo, rev = crate_info
124+
git_sources.add((repo, rev))
125+
for repo, rev in git_sources:
126+
write_file(config_toml, '[source."%s"]\ngit = "%s"\nrev = "%s"\n'
127+
'replace-with = "vendored-sources"\n\n' % (repo, repo, rev), append=True)
128+
129+
# Use environment variable since it would also be passed along to builds triggered via python packages
130+
env.setvar('CARGO_NET_OFFLINE', 'true')
131+
132+
# More work is needed here for git sources to work, especially those repos with multiple packages.
133+
for src in self.src:
134+
existing_dirs = set(os.listdir(self.builddir))
135+
self.log.info("Unpacking source %s" % src['name'])
136+
srcdir = extract_file(src['path'], self.builddir, cmd=src['cmd'],
137+
extra_options=self.cfg['unpack_options'], change_into_dir=False)
138+
change_dir(srcdir)
139+
if srcdir:
140+
self.src[self.src.index(src)]['finalpath'] = srcdir
141+
else:
142+
raise EasyBuildError("Unpacking source %s failed", src['name'])
143+
144+
# Create checksum file for all sources required by vendored crates.io sources
145+
new_dirs = set(os.listdir(self.builddir)) - existing_dirs
146+
if self.cfg['offline'] and len(new_dirs) == 1:
147+
cratedir = new_dirs.pop()
148+
self.log.info('creating .cargo-checksums.json file for : %s', cratedir)
149+
chksum = compute_checksum(src['path'], checksum_type='sha256')
150+
chkfile = os.path.join(self.builddir, cratedir, '.cargo-checksum.json')
151+
write_file(chkfile, '{"files":{},"package":"%s"}' % chksum)
152+
153+
def configure_step(self):
154+
"""Empty configuration step."""
155+
pass
156+
157+
@property
158+
def profile(self):
159+
return 'debug' if self.toolchain.options.get('debug', None) else 'release'
160+
161+
def build_step(self):
162+
"""Build with cargo"""
163+
parallel = ''
164+
if self.cfg['parallel']:
165+
parallel = "-j %s" % self.cfg['parallel']
166+
167+
tests = ''
168+
if self.cfg['enable_tests']:
169+
tests = "--tests"
170+
171+
lto = ''
172+
if self.cfg['lto'] is not None:
173+
lto = '--config profile.%s.lto=\\"%s\\"' % (self.profile, self.cfg['lto'])
174+
175+
run_cmd('rustc --print cfg', log_all=True, simple=True) # for tracking in log file
176+
cmd = ' '.join([
177+
self.cfg['prebuildopts'],
178+
'cargo build',
179+
'--profile=' + self.profile,
180+
lto,
181+
tests,
182+
parallel,
183+
self.cfg['buildopts'],
184+
])
185+
run_cmd(cmd, log_all=True, simple=True)
186+
187+
def test_step(self):
188+
"""Test with cargo"""
189+
if self.cfg['enable_tests']:
190+
cmd = ' '.join([
191+
self.cfg['pretestopts'],
192+
'cargo test',
193+
'--profile=' + self.profile,
194+
self.cfg['testopts'],
195+
])
196+
run_cmd(cmd, log_all=True, simple=True)
197+
198+
def install_step(self):
199+
"""Install with cargo"""
200+
cmd = ' '.join([
201+
self.cfg['preinstallopts'],
202+
'cargo install',
203+
'--profile=' + self.profile,
204+
'--root=' + self.installdir,
205+
'--path=.',
206+
self.cfg['installopts'],
207+
])
208+
run_cmd(cmd, log_all=True, simple=True)
209+
210+
211+
def generate_crate_list(sourcedir):
212+
"""Helper for generating crate list"""
213+
import toml
214+
215+
cargo_toml = toml.load(os.path.join(sourcedir, 'Cargo.toml'))
216+
cargo_lock = toml.load(os.path.join(sourcedir, 'Cargo.lock'))
217+
218+
app_name = cargo_toml['package']['name']
219+
deps = cargo_lock['package']
220+
221+
app_in_cratesio = False
222+
crates = []
223+
other_crates = []
224+
for dep in deps:
225+
name = dep['name']
226+
version = dep['version']
227+
if 'source' in dep:
228+
if name == app_name:
229+
app_in_cratesio = True # exclude app itself, needs to be first in crates list or taken from pypi
230+
else:
231+
if dep['source'] == 'registry+https://github.com/rust-lang/crates.io-index':
232+
crates.append((name, version))
233+
else:
234+
# Lock file has revision#revision in the url for some reason.
235+
crates.append((name, version, dep['source'].rsplit('#', maxsplit=1)[0]))
236+
else:
237+
other_crates.append((name, version))
238+
return app_in_cratesio, crates, other_crates
239+
240+
241+
if __name__ == '__main__':
242+
import sys
243+
app_in_cratesio, crates, other = generate_crate_list(sys.argv[1])
244+
print(other)
245+
if app_in_cratesio or crates:
246+
print('crates = [')
247+
if app_in_cratesio:
248+
print(' (name, version),')
249+
for crate_info in crates:
250+
print(" ('" + "', '".join(crate_info) + "'),")
251+
print(']')

0 commit comments

Comments
 (0)