Skip to content

Commit

Permalink
Merge pull request EESSI#358 from TopRichard/nessi-2023.06-Easyconfig…
Browse files Browse the repository at this point in the history
…_that_can_extend_EESSI

Easyconfig that can extend EESSI
  • Loading branch information
trz42 authored May 8, 2024
2 parents 7c23c3d + b5f3c95 commit 458d0e1
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 20 deletions.
170 changes: 170 additions & 0 deletions EESSI-extend-2023.06-easybuild.eb
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
easyblock = 'Bundle'

name = 'NESSI-extend'
version = '2023.06'
# May have different ways to extend NESSI in the future (manually, other tools,...)
versionsuffix = '-easybuild'

homepage = 'https://documentation.sigma2.no/software/nessi_eessi.html'

description = """
NESSI is an innovative service to make optimized scientific software
installations available on any machine anywhere in the world in near
real-time - without the need to build or install the software. NESSI
works similarly to popular streaming services for videos and music.
NESSI is a sibling of EESSI, the European Environment for Scientific
Software Installations (see https://eessi.io/docs).
This module allows you to extend NESSI using the same configuration for
EasyBuild as NESSI itself uses. A number of environment variables control the
behaviour of the module:
- NESSI_USER_INSTALL can be set to a location to install modules for use by
the user only. The location must already exist on the filesystem.
- NESSI_PROJECT_INSTALL can be set to a location to install modules for use by
a project. The location must already exist on the filesystem and you should
ensure that the location has the correct Linux group and the SGID permission
is set on that directory (`chmod g+s $NESSI_PROJECT_INSTALL`) so that all
members of the group have permission to read and write installations.
- NESSI_SITE_INSTALL is either defined or not and cannot be used with another
environment variable. A site installation is done in a defined location and
any installations there are (by default) world readable.
- NESSI_CVMFS_INSTALL is either defined or not and cannot be used with another
environment variable. A CVMFS installation targets a defined location which
will be ingested into CVMFS and is only useful for CVMFS administrators.
- If none of the environment variables above are defined, a NESSI_USER_INSTALL
is assumed with a value of $HOME/NESSI
If both NESSI_USER_INSTALL and NESSI_PROJECT_INSTALL are defined, both sets of
installations are exposed, but new installations are created as user
installations.
"""

toolchain = SYSTEM

# All the dependencies we filter in NESSI
local_deps_to_filter = "Autoconf,Automake,Autotools,binutils,bzip2,DBus,flex,gettext,gperf,help2man,intltool,libreadline,libtool,M4,makeinfo,ncurses,util-linux,XZ,zlib"
local_arch_specific_deps_to_filter = {'aarch64': ',yasm', 'x86_64': ''}
local_deps_to_filter += local_arch_specific_deps_to_filter[ARCH]

# Set the universal EasyBuild variables
modextravars = {
'EASYBUILD_FILTER_DEPS': local_deps_to_filter,
'EASYBUILD_IGNORE_OSDEPS': '1',
'EASYBUILD_DEBUG': '1',
'EASYBUILD_TRACE': '1',
'EASYBUILD_ZIP_LOGS': 'bzip2',
'EASYBUILD_RPATH': '1',
'EASYBUILD_FILTER_ENV_VARS': 'LD_LIBRARY_PATH',
'EASYBUILD_READ_ONLY_INSTALLDIR': '1',
'EASYBUILD_MODULE_EXTENSIONS': '1',
'EASYBUILD_EXPERIMENTAL': '1',
}

# Need a few other variables, but they are more dynamic
# EASYBUILD_SYSROOT=${EPREFIX}
# EASYBUILD_PREFIX=${WORKDIR}/easybuild
# EASYBUILD_HOOKS=${EESSI_PREFIX}/init/easybuild/eb_hooks.py
# EASYBUILD_INSTALLPATH=${EESSI_PREFIX}/software/${EESSI_OS_TYPE}/${EESSI_SOFTWARE_SUBDIR}
# EASYBUILD_SOURCEPATH=${WORKDIR}/easybuild/sources:${EESSI_SOURCEPATH}
#
# And also some optional ones based on the kind of installation
# EASYBUILD_SET_GID_BIT
# EASYBUILD_GROUP_WRITABLE_INSTALLDIR
# EASYBUILD_UMASK
# EASYBUILD_STICKY_BIT
modluafooter = """
if (mode() == "load") then
-- Use a working directory for temporary build files
if (os.getenv("WORKING_DIR") == nil) then
LmodMessage("-- Using /tmp/$USER as a temporary working directory for installations, you can override this by setting the environment variable WORKING_DIR and reloading the module (e.g., /dev/shm is a common option)")
end
end
working_dir = os.getenv("WORKING_DIR") or pathJoin("/tmp", os.getenv("USER"))
-- Gather the EPREFIX to use as a sysroot
sysroot = os.getenv("EESSI_EPREFIX")
-- Use an installation prefix that we _should_ have write access to
if (os.getenv("NESSI_CVMFS_INSTALL") ~= nil) then
-- Make sure no other NESSI install environment variables are set
if ((os.getenv("NESSI_SITE_INSTALL") ~= nil) or (os.getenv("NESSI_PROJECT_INSTALL") ~= nil) or (os.getenv("NESSI_USER_INSTALL") ~= nil)) then
LmodError("You cannot use NESSI_CVMFS_INSTALL in combination with any other NESSI_*_INSTALL environment variables")
end
eessi_cvmfs_install = true
easybuild_installpath = os.getenv("EESSI_SOFTWARE_PATH")
elseif (os.getenv("NESSI_SITE_INSTALL") ~= nil) then
-- Make sure no other NESSI install environment variables are set
if ((os.getenv("NESSI_PROJECT_INSTALL") ~= nil) or (os.getenv("NESSI_USER_INSTALL") ~= nil)) then
LmodError("You cannot use NESSI_SITE_INSTALL in combination with any other NESSI_*_INSTALL environment variables")
end
easybuild_installpath = string.gsub(os.getenv("EESSI_SOFTWARE_PATH"), 'versions', 'host_injections')
else
-- Deal with user and project installs
project_install = os.getenv("NESSI_PROJECT_INSTALL")
project_modulepath = nil
if (project_install ~= nil) then
-- Check the folder exists
if not isDir(project_install) then
LmodError("The location of NESSI_PROJECT_INSTALL (" .. project_install .. ") does not exist or is not a folder")
end
if (mode() == "load") then
LmodMessage("Configuring for use of NESSI_PROJECT_INSTALL under " .. project_install)
end
easybuild_installpath = string.gsub(os.getenv("EESSI_SOFTWARE_PATH"), os.getenv("EESSI_CVMFS_REPO"), project_install)
project_modulepath = pathJoin(easybuild_installpath, 'modules', 'all')
end
user_install = os.getenv("NESSI_USER_INSTALL")
user_modulepath = nil
if (user_install ~= nil) then
-- Check the folder exists
if not isDir(user_install) then
LmodError("The location of NESSI_USER_INSTALL (" .. user_install .. ") does not exist or is not a folder")
end
elseif (user_install == nil) and (project_install == nil) then
-- No need to check for existence when we use a HOME subdir
user_install = pathJoin(os.getenv("HOME"), "nessi")
end
if (user_install ~= nil) then
if (mode() == "load") then
LmodMessage("Configuring for use of NESSI_USER_INSTALL under " .. user_install)
end
easybuild_installpath = string.gsub(os.getenv("EESSI_SOFTWARE_PATH"), os.getenv("EESSI_CVMFS_REPO"), user_install)
user_modulepath = pathJoin(easybuild_installpath, 'modules', 'all')
end
end
if (mode() == "load") then
LmodMessage("-- To create installations for NESSI, you _must_ have write permissions to " .. easybuild_installpath)
-- Advise them to reuse sources
if (os.getenv("EASYBUILD_SOURCEPATH") == nil) then
LmodMessage("-- You may wish to configure a sources directory for EasyBuild (for example, via setting the environment variable EASYBUILD_SOURCEPATH) to allow you to reuse existing sources for packages.")
end
end
-- Set the relevant universal environment variables for EasyBuild
setenv ("EASYBUILD_SYSROOT", sysroot)
setenv ("EASYBUILD_PREFIX", pathJoin(working_dir, "easybuild"))
setenv ("EASYBUILD_INSTALLPATH", easybuild_installpath)
setenv ("EASYBUILD_HOOKS", pathJoin(os.getenv("EESSI_PREFIX"), "init", "easybuild", "eb_hooks.py"))
setenv ("EASYBUILD_UMASK", "002")
-- Set all related environment variables if we have project or user installations (including extending MODULEPATH)
if (user_modulepath ~= nil) then
-- Use a more restrictive umask for this case
setenv ("EASYBUILD_UMASK", "022")
setenv ("EASYBUILD_STICKY_BIT", "1")
-- configure MODULEPATH
if (project_modulepath ~= nil) then
prepend_path("MODULEPATH", project_modulepath)
end
prepend_path("MODULEPATH", user_modulepath)
elseif (project_modulepath ~= nil) then
setenv ("EASYBUILD_SET_GID_BIT", "1")
setenv ("EASYBUILD_GROUP_WRITABLE_INSTALLDIR", "1")
setenv ("EASYBUILD_STICKY_BIT", "0")
-- configure MODULEPATH
prepend_path("MODULEPATH", project_modulepath)
end
-- Make sure EasyBuild itself is loaded
if not ( isloaded("EasyBuild") ) then
load("EasyBuild")
end
"""

moduleclass = 'devel'
38 changes: 19 additions & 19 deletions create_lmodsitepackage.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@
end
local function from_eessi_prefix(t)
-- eessi_prefix is the prefix with official EESSI modules
-- e.g. /cvmfs/software.eessi.io/versions/2023.06
-- eessi_prefix is the prefix with official NESSI modules
-- e.g. /cvmfs/pilot.nessi.no/versions/2023.06
local eessi_prefix = os.getenv("EESSI_PREFIX")
-- If EESSI_PREFIX wasn't defined, we cannot check if this module was from the EESSI environment
-- If EESSI_PREFIX wasn't defined, we cannot check if this module was from the NESSI environment
-- In that case, we assume it isn't, otherwise EESSI_PREFIX would (probably) have been set
if eessi_prefix == nil then
return False
else
-- NOTE: exact paths for site so may need to be updated later.
-- See https://github.com/EESSI/software-layer/pull/371
-- See https://github.com/NorESSI/software-layer/pull/358
-- eessi_prefix_host_injections is the prefix with site-extensions (i.e. additional modules)
-- to the official EESSI modules, e.g. /cvmfs/software.eessi.io/host_injections/2023.06
-- to the official NESSI modules, e.g. /cvmfs/pilot.nessi.no/host_injections/2023.06
local eessi_prefix_host_injections = string.gsub(eessi_prefix, 'versions', 'host_injections')
-- Check if the full modulepath starts with the eessi_prefix_*
Expand All @@ -42,9 +42,9 @@
end
local function load_site_specific_hooks()
-- This function will be run after the EESSI hooks are registered
-- This function will be run after the NESSI hooks are registered
-- It will load a local SitePackage.lua that is architecture independent (if it exists) from e.g.
-- /cvmfs/software.eessi.io/host_injections/2023.06/.lmod/SitePackage.lua
-- /cvmfs/pilot.nessi.no/host_injections/2023.06/.lmod/SitePackage.lua
-- That can define a new hook
--
-- function site_specific_load_hook(t)
Expand All @@ -58,15 +58,15 @@
-- site_specific_load_hook(t)
-- end
--
-- Over overwrite the EESSI hook entirely:
-- Or overwrite the NESSI hook entirely:
--
-- hook.register("load", final_load_hook)
--
-- Note that the appending procedure can be simplified once we have an lmod >= 8.7.36
-- See https://github.com/TACC/Lmod/pull/696#issuecomment-1998765722
--
-- Subsequently, this function will look for an architecture-specific SitePackage.lua, e.g. from
-- /cvmfs/software.eessi.io/host_injections/2023.06/software/linux/x86_64/amd/zen2/.lmod/SitePackage.lua
-- /cvmfs/pilot.nessi.no/host_injections/2023.06/software/linux/x86_64/amd/zen2/.lmod/SitePackage.lua
-- This can then register an additional hook, e.g.
--
-- function arch_specific_load_hook(t)
Expand Down Expand Up @@ -112,7 +112,7 @@
local simpleName = string.match(t.modFullName, "(.-)/")
-- If we try to load CUDA itself, check if the full CUDA SDK was installed on the host in host_injections.
-- This is required for end users to build additional CUDA software. If the full SDK isn't present, refuse
-- to load the CUDA module and print an informative message on how to set up GPU support for EESSI
-- to load the CUDA module and print an informative message on how to set up GPU support for NESSI
local refer_to_docs = "For more information on how to do this, see https://www.eessi.io/docs/gpu/.\\n"
if simpleName == 'CUDA' then
-- get the full host_injections path
Expand All @@ -121,26 +121,26 @@
local cudaEasyBuildDir = hostInjections .. "/software/" .. t.modFullName .. "/easybuild"
local cudaDirExists = isDir(cudaEasyBuildDir)
if not cudaDirExists then
local advice = "but while the module file exists, the actual software is not entirely shipped with EESSI "
advice = advice .. "due to licencing. You will need to install a full copy of the CUDA SDK where EESSI "
local advice = "but while the module file exists, the actual software is not entirely shipped with NESSI "
advice = advice .. "due to licencing. You will need to install a full copy of the CUDA SDK where NESSI "
advice = advice .. "can find it.\\n"
advice = advice .. refer_to_docs
LmodError("\\nYou requested to load ", simpleName, " ", advice)
end
end
-- when loading CUDA enabled modules check if the necessary driver libraries are accessible to the EESSI linker,
-- when loading CUDA enabled modules check if the necessary driver libraries are accessible to the NESSI linker,
-- otherwise, refuse to load the requested module and print error message
local haveGpu = mt:haveProperty(simpleName,"arch","gpu")
if haveGpu then
local arch = os.getenv("EESSI_CPU_FAMILY") or ""
local cudaVersionFile = "/cvmfs/software.eessi.io/host_injections/nvidia/" .. arch .. "/latest/cuda_version.txt"
local cudaDriverFile = "/cvmfs/software.eessi.io/host_injections/nvidia/" .. arch .. "/latest/libcuda.so"
local cudaVersionFile = "/cvmfs/pilot.nessi.no/host_injections/nvidia/" .. arch .. "/latest/cuda_version.txt"
local cudaDriverFile = "/cvmfs/pilot.nessi.no/host_injections/nvidia/" .. arch .. "/latest/libcuda.so"
local cudaDriverExists = isFile(cudaDriverFile)
local singularityCudaExists = isFile("/.singularity.d/libs/libcuda.so")
if not (cudaDriverExists or singularityCudaExists) then
local advice = "which relies on the CUDA runtime environment and driver libraries. "
advice = advice .. "In order to be able to use the module, you will need "
advice = advice .. "to make sure EESSI can find the GPU driver libraries on your host system.\\n"
advice = advice .. "to make sure NESSI can find the GPU driver libraries on your host system.\\n"
advice = advice .. refer_to_docs
LmodError("\\nYou requested to load ", simpleName, " ", advice)
else
Expand All @@ -162,7 +162,7 @@
if driver_libs_need_update == true then
local advice = "but the module you want to load requires CUDA " .. cudaVersion_req .. ". "
advice = advice .. "Please update your CUDA driver libraries and then "
advice = advice .. "let EESSI know about the update.\\n"
advice = advice .. "let NESSI know about the update.\\n"
advice = advice .. refer_to_docs
LmodError("\\nYour driver CUDA version is ", cudaVersion, " ", advice)
end
Expand All @@ -174,7 +174,7 @@
-- Combine both functions into a single one, as we can only register one function as load hook in lmod
-- Also: make it non-local, so it can be imported and extended by other lmodrc files if needed
function eessi_load_hook(t)
-- Only apply CUDA hooks if the loaded module is in the EESSI prefix
-- Only apply CUDA hooks if the loaded module is in the NESSI prefix
-- This avoids getting an Lmod Error when trying to load a CUDA module from a local software stack
if from_eessi_prefix(t) then
eessi_cuda_enabled_load_hook(t)
Expand All @@ -183,7 +183,7 @@
hook.register("load", eessi_load_hook)
-- Note that this needs to happen at the end, so that any EESSI specific hooks can be overwritten by the site
-- Note that this needs to happen at the end, so that any NESSI specific hooks can be overwritten by the site
load_site_specific_hooks()
"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ easyconfigs:
- EasyBuild-4.9.1.eb:
options:
from-pr: 20299
- EESSI-extend-2023.06-easybuild.eb
2 changes: 1 addition & 1 deletion init/Magic_Castle/bash
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ source $(dirname "$BASH_SOURCE")/../eessi_environment_variables

# Provide a clean MODULEPATH
export MODULEPATH_ROOT=$EESSI_MODULEPATH
export MODULEPATH=$EESSI_MODULEPATH
export MODULEPATH=$EESSI_SITE_MODULEPATH:$EESSI_MODULEPATH

# Extensions are too many, let's not print them by default (requires Lmod 8.4.12)
export LMOD_AVAIL_EXTENSIONS=no
Expand Down
2 changes: 2 additions & 0 deletions init/bash
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ if [ $? -eq 0 ]; then
# prepend location of modules for EESSI software stack to $MODULEPATH
show_msg "Prepending $EESSI_MODULEPATH to \$MODULEPATH..."
module use $EESSI_MODULEPATH
show_msg "Prepending site path $EESSI_SITE_MODULEPATH to \$MODULEPATH..."
module use $EESSI_SITE_MODULEPATH

#show_msg ""
#show_msg "*** Known problems in the ${EESSI_VERSION} software stack ***"
Expand Down
2 changes: 2 additions & 0 deletions init/eessi_environment_variables
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ if [ -d $EESSI_PREFIX ]; then
if [ -d $EESSI_MODULEPATH ]; then
export EESSI_MODULEPATH=$EESSI_MODULEPATH
show_msg "Using ${EESSI_MODULEPATH} as the directory to be added to MODULEPATH."
export EESSI_SITE_MODULEPATH=${EESSI_MODULEPATH/versions/host_injections}
show_msg "Using ${EESSI_SITE_MODULEPATH} as the site extension directory to be added to MODULEPATH."
else
error "NESSI module path at $EESSI_MODULEPATH not found!"
false
Expand Down
6 changes: 6 additions & 0 deletions install_scripts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,9 @@ nvidia_files=(
install_cuda_host_injections.sh link_nvidia_host_libraries.sh
)
copy_files_by_list ${TOPDIR}/scripts/gpu_support/nvidia ${INSTALL_PREFIX}/scripts/gpu_support/nvidia "${nvidia_files[@]}"

# Copy over EasyBuild hooks file used for installations
hook_files=(
eb_hooks.py
)
copy_files_by_list ${TOPDIR} ${INSTALL_PREFIX}/init/easybuild "${hook_files[@]}"

0 comments on commit 458d0e1

Please sign in to comment.