Skip to content

Commit

Permalink
Merge pull request #2937 from easybuilders/4.7.x
Browse files Browse the repository at this point in the history
release EasyBuild v4.7.2
  • Loading branch information
boegel authored May 27, 2023
2 parents 893400f + 6e2cf1e commit fb11417
Show file tree
Hide file tree
Showing 15 changed files with 442 additions and 26 deletions.
20 changes: 19 additions & 1 deletion RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,25 @@ For more detailed information, please see the git log.

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

The latest version of easybuild-easyblocks provides 248 software-specific easyblocks and 39 generic easyblocks.
The latest version of easybuild-easyblocks provides 248 software-specific easyblocks and 41 generic easyblocks.


v4.7.2 (27 May 2023)
--------------------

- new generic easyblock for installing Rust crates with cargo: Cargo and CargoPythonPackage (#2902, #2934)
- minor enhancements and updates, including:
- let MATLAB easyblock raise an error if the MATLAB installation key is not provided (#2905)
- print message to inform that GPU package (instead of Kokkos) is used for LAMMPS (#2906)
- enhance PyTorch easyblock to use FlexiBLAS for PyTorch >= 1.11.0 (#2915)
- various bug fixes, including:
- use custom RPATH sanity check for Go packages that doesn't actually check for an RPATH section in the binary (#2913)
- use string '0' to avoid problems when openssl version is not determined (#2914)
- update GCC easyblock to ensure that --sysroot is passed to linker (but only when it needs to be) (#2921)
- add output log to MATLAB installs, actually parse for common errors (#2924)
- enhance Gurobi easyblock to allow using $EB_GUROBI_LICENSE_FILE environment variable (#2926)
- force building torchvision with CUDA support if CUDA is included as dependency by setting `$FORCE_CUDA` (#2931)
- fix exec permission on files in arch bindir for COMSOL (#2932)

v4.7.1 (March 20th 2023)
------------------------
Expand Down
2 changes: 1 addition & 1 deletion easybuild/easyblocks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
# recent setuptools versions will *TRANSFORM* something like 'X.Y.Zdev' into 'X.Y.Z.dev0', with a warning like
# UserWarning: Normalizing '2.4.0dev' to '2.4.0.dev0'
# This causes problems further up the dependency chain...
VERSION = LooseVersion('4.7.1')
VERSION = LooseVersion('4.7.2')
UNKNOWN = 'UNKNOWN'


Expand Down
5 changes: 5 additions & 0 deletions easybuild/easyblocks/c/comsol.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ def install_step(self):
# make sure setup script is executable
adjust_permissions(setup_script, stat.S_IXUSR)

# make sure binaries in arch bindir is executable
archpath = os.path.join(self.start_dir, 'bin', 'glnxa64')
adjust_permissions(os.path.join(archpath, 'inflate'), stat.S_IXUSR)
adjust_permissions(os.path.join(archpath, 'setuplauncher'), stat.S_IXUSR)

# make sure $DISPLAY is not defined, which may lead to (hard to trace) problems
# this is a workaround for not being able to specify --nodisplay to the install scripts
env.unset_env_vars(['DISPLAY'])
Expand Down
17 changes: 14 additions & 3 deletions easybuild/easyblocks/g/gcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.config import build_option
from easybuild.tools.filetools import apply_regex_substitutions, change_dir, copy_file, move_file, symlink
from easybuild.tools.filetools import which, write_file
from easybuild.tools.filetools import which, read_file, write_file
from easybuild.tools.modules import get_software_root
from easybuild.tools.run import run_cmd
from easybuild.tools.systemtools import RISCV, check_os_dependency, get_cpu_architecture, get_cpu_family
Expand Down Expand Up @@ -357,8 +357,19 @@ def configure_step(self):
# (see https://gcc.gnu.org/install/configure.html)
self.cfg.update('configopts', '--with-sysroot=%s' % sysroot)

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

# prefix dynamic linkers with sysroot
# this patches lines like:
Expand Down
17 changes: 10 additions & 7 deletions easybuild/easyblocks/g/gurobi.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,25 @@ def __init__(self, *args, **kwargs):
"""Easyblock constructor, define custom class variables specific to Gurobi."""
super(EB_Gurobi, self).__init__(*args, **kwargs)

self.license_file = self.cfg['license_file']
# make sure license file is available
self.orig_license_file = self.cfg['license_file']
if self.orig_license_file is None:
self.orig_license_file = os.getenv('EB_GUROBI_LICENSE_FILE', None)

if self.cfg['copy_license_file']:
self.license_file = os.path.join(self.installdir, 'gurobi.lic')
else:
self.license_file = self.orig_license_file

def install_step(self):
"""Install Gurobi and license file."""

# make sure license file is available
if self.cfg['license_file'] is None or not os.path.exists(self.cfg['license_file']):
raise EasyBuildError("No existing license file specified: %s", self.cfg['license_file'])

super(EB_Gurobi, self).install_step()

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

copy_file(self.orig_license_file, self.license_file)

if get_software_root('Python'):
run_cmd("python setup.py install --prefix=%s" % self.installdir)
Expand Down
251 changes: 251 additions & 0 deletions easybuild/easyblocks/generic/cargo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
##
# Copyright 2009-2023 Ghent University
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
# with support of Ghent University (http://ugent.be/hpc),
# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be),
# Flemish Research Foundation (FWO) (http://www.fwo.be/en)
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
#
# https://github.com/easybuilders/easybuild
#
# EasyBuild is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation v2.
#
# EasyBuild is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EasyBuild. If not, see <http://www.gnu.org/licenses/>.
##
"""
EasyBuild support for installing Cargo packages (Rust lang package system)
@author: Mikael Oehman (Chalmers University of Technology)
"""

import os

import easybuild.tools.environment as env
from easybuild.tools.build_log import EasyBuildError
from easybuild.framework.easyconfig import CUSTOM
from easybuild.framework.extensioneasyblock import ExtensionEasyBlock
from easybuild.tools.filetools import extract_file, change_dir
from easybuild.tools.run import run_cmd
from easybuild.tools.config import build_option
from easybuild.tools.filetools import write_file, compute_checksum

CRATESIO_SOURCE = "https://crates.io/api/v1/crates"


class Cargo(ExtensionEasyBlock):
"""Support for installing Cargo packages (Rust)"""

@staticmethod
def extra_options(extra_vars=None):
"""Define extra easyconfig parameters specific to Cargo"""
extra_vars = ExtensionEasyBlock.extra_options(extra_vars)
extra_vars.update({
'enable_tests': [True, "Enable building of tests", CUSTOM],
'offline': [True, "Build offline", CUSTOM],
'lto': [None, "Override default LTO flag ('fat', 'thin', 'off')", CUSTOM],
'crates': [[], "List of (crate, version, [repo, rev]) tuples to use", CUSTOM],
})

return extra_vars

def __init__(self, *args, **kwargs):
"""Constructor for Cargo easyblock."""
super(Cargo, self).__init__(*args, **kwargs)
self.cargo_home = os.path.join(self.builddir, '.cargo')
env.setvar('CARGO_HOME', self.cargo_home)
env.setvar('RUSTC', 'rustc')
env.setvar('RUSTDOC', 'rustdoc')
env.setvar('RUSTFMT', 'rustfmt')
optarch = build_option('optarch')
if not optarch:
optarch = 'native'
env.setvar('RUSTFLAGS', '-C target-cpu=%s' % optarch)
env.setvar('RUST_LOG', 'DEBUG')
env.setvar('RUST_BACKTRACE', '1')

# Populate sources from "crates" list of tuples (only once)
if self.cfg['crates']:
# copy list of crates, so we can wipe 'crates' easyconfig parameter,
# to avoid that creates are processed into 'sources' easyconfig parameter again
# when easyblock is initialized again using same parsed easyconfig
# (for example when check_sha256_checksums function is called, like in easyconfigs test suite)
self.crates = self.cfg['crates'][:]
sources = []
for crate_info in self.cfg['crates']:
if len(crate_info) == 2:
crate, version = crate_info
sources.append({
'download_filename': crate + '/' + version + '/download',
'filename': crate + '-' + version + '.tar.gz',
'source_urls': [CRATESIO_SOURCE],
'alt_location': 'crates.io',
})
else:
crate, version, repo, rev = crate_info
url, repo_name_git = repo.rsplit('/', maxsplit=1)
sources.append({
'git_config': {'url': url, 'repo_name': repo_name_git[:-4], 'commit': rev},
'filename': crate + '-' + version + '.tar.gz',
'source_urls': [CRATESIO_SOURCE],
})

self.cfg.update('sources', sources)

# set 'crates' easyconfig parameter to empty list to prevent re-processing into sources
self.cfg['crates'] = []

def extract_step(self):
"""
Unpack the source files and populate them with required .cargo-checksum.json if offline
"""
if self.cfg['offline']:
self.log.info("Setting vendored crates dir")
# Replace crates-io with vendored sources using build dir wide toml file in CARGO_HOME
# because the rust source subdirectories might differ with python packages
config_toml = os.path.join(self.cargo_home, 'config.toml')
write_file(config_toml, '[source.vendored-sources]\ndirectory = "%s"\n\n' % self.builddir, append=True)
write_file(config_toml, '[source.crates-io]\nreplace-with = "vendored-sources"\n\n', append=True)

# also vendor sources from other git sources (could be many crates for one git source)
git_sources = set()
for crate_info in self.crates:
if len(crate_info) == 4:
_, _, repo, rev = crate_info
git_sources.add((repo, rev))
for repo, rev in git_sources:
write_file(config_toml, '[source."%s"]\ngit = "%s"\nrev = "%s"\n'
'replace-with = "vendored-sources"\n\n' % (repo, repo, rev), append=True)

# Use environment variable since it would also be passed along to builds triggered via python packages
env.setvar('CARGO_NET_OFFLINE', 'true')

# More work is needed here for git sources to work, especially those repos with multiple packages.
for src in self.src:
existing_dirs = set(os.listdir(self.builddir))
self.log.info("Unpacking source %s" % src['name'])
srcdir = extract_file(src['path'], self.builddir, cmd=src['cmd'],
extra_options=self.cfg['unpack_options'], change_into_dir=False)
change_dir(srcdir)
if srcdir:
self.src[self.src.index(src)]['finalpath'] = srcdir
else:
raise EasyBuildError("Unpacking source %s failed", src['name'])

# Create checksum file for all sources required by vendored crates.io sources
new_dirs = set(os.listdir(self.builddir)) - existing_dirs
if self.cfg['offline'] and len(new_dirs) == 1:
cratedir = new_dirs.pop()
self.log.info('creating .cargo-checksums.json file for : %s', cratedir)
chksum = compute_checksum(src['path'], checksum_type='sha256')
chkfile = os.path.join(self.builddir, cratedir, '.cargo-checksum.json')
write_file(chkfile, '{"files":{},"package":"%s"}' % chksum)

def configure_step(self):
"""Empty configuration step."""
pass

@property
def profile(self):
return 'debug' if self.toolchain.options.get('debug', None) else 'release'

def build_step(self):
"""Build with cargo"""
parallel = ''
if self.cfg['parallel']:
parallel = "-j %s" % self.cfg['parallel']

tests = ''
if self.cfg['enable_tests']:
tests = "--tests"

lto = ''
if self.cfg['lto'] is not None:
lto = '--config profile.%s.lto=\\"%s\\"' % (self.profile, self.cfg['lto'])

run_cmd('rustc --print cfg', log_all=True, simple=True) # for tracking in log file
cmd = ' '.join([
self.cfg['prebuildopts'],
'cargo build',
'--profile=' + self.profile,
lto,
tests,
parallel,
self.cfg['buildopts'],
])
run_cmd(cmd, log_all=True, simple=True)

def test_step(self):
"""Test with cargo"""
if self.cfg['enable_tests']:
cmd = ' '.join([
self.cfg['pretestopts'],
'cargo test',
'--profile=' + self.profile,
self.cfg['testopts'],
])
run_cmd(cmd, log_all=True, simple=True)

def install_step(self):
"""Install with cargo"""
cmd = ' '.join([
self.cfg['preinstallopts'],
'cargo install',
'--profile=' + self.profile,
'--root=' + self.installdir,
'--path=.',
self.cfg['installopts'],
])
run_cmd(cmd, log_all=True, simple=True)


def generate_crate_list(sourcedir):
"""Helper for generating crate list"""
import toml

cargo_toml = toml.load(os.path.join(sourcedir, 'Cargo.toml'))
cargo_lock = toml.load(os.path.join(sourcedir, 'Cargo.lock'))

app_name = cargo_toml['package']['name']
deps = cargo_lock['package']

app_in_cratesio = False
crates = []
other_crates = []
for dep in deps:
name = dep['name']
version = dep['version']
if 'source' in dep:
if name == app_name:
app_in_cratesio = True # exclude app itself, needs to be first in crates list or taken from pypi
else:
if dep['source'] == 'registry+https://github.com/rust-lang/crates.io-index':
crates.append((name, version))
else:
# Lock file has revision#revision in the url for some reason.
crates.append((name, version, dep['source'].rsplit('#', maxsplit=1)[0]))
else:
other_crates.append((name, version))
return app_in_cratesio, crates, other_crates


if __name__ == '__main__':
import sys
app_in_cratesio, crates, other = generate_crate_list(sys.argv[1])
print(other)
if app_in_cratesio or crates:
print('crates = [')
if app_in_cratesio:
print(' (name, version),')
for crate_info in crates:
print(" ('" + "', '".join(crate_info) + "'),")
print(']')
Loading

0 comments on commit fb11417

Please sign in to comment.