This follows on from my post on platform detection with Lmod, using that as a tool from which to create platform-specific builds of software with EasyBuild. This is a long one, as it contains the full setup for getting this running.

Using the scripts

The development of the scripts is described further down this post. The scripts themselves can be found in my GitHub repository.

To setup from scratch, first download the sources:

mkdir -p /apps/easybuild/sources
eb-download --dependencies /apps/easybuild/sources

Then prepare the main branch:

eb-toolchain-bootstrap --create-easybuild-sources-folder /apps/easybuild/2020b
# Initialise for the current platform (using the platform detection in hpc_environment module, e.g. EL-7-sky or Deb-10-has)
eb-platform-init --easybuild-source-path=/apps/easybuild/2020b/sources --install-sources=/apps/easybuild/sources /apps/easybuild/2020b/$HPC_OS_SHORT-$HPC_ARCH_CPU_SHORTNAME

And, for a development branch with it’s own easyconfig tree:

eb-toolchain-bootstrap --install-sources=/apps/easybuild/sources --clone-easyconfigs /apps/easybuild/2020b-some-app-test
# Initialise for current platform, using the base configuration's source space
eb-platform-init --easybuild-source-path=/apps/easybuild/2020b/sources --install-sources=/apps/easybuild/sources /apps/easybuild/2020b-some-app-test/$HPC_OS_SHORT-$HPC_ARCH_CPU_SHORTNAME

Module to load EasyBuild environments

This Lmod (lua) module presumes that you have a base branch, which can be named as you like (I used the toolchain version) provided it does not contain a hyphen (or dash, if you prefer) character. For each branch, this module file if suffixed with ‘-dev’ will also enable the virtualenv containing EasyBuild itself (so the non-dev version only makes the built software available). The non-dev variant will also enable any compatible architecture environments that exist, after the current architecture (so the current one’s software is used by preference with the others as fallback options if there is a missing piece of software).

If the module has a hyphen in it (with or without the ‘-dev’ suffix), the module will presume it is a sub-environment and stack with the base environment that does not contain the hyphen. This means the sub-environments, which I use to test/develop building individual software, does not need to rebuild any modules already in the base.

if (mode() == "load") then
	-- If loading, we want the dependency processed (loaded) early --
	depends_on('hpc_environment')
end

local toolchain_ver = myModuleVersion()

-- If version ends in '-dev' then load toolchain development environment
local dev = false
if toolchain_ver:sub(-4) == '-dev' then
    dev = true
    toolchain_ver = toolchain_ver:gsub('-dev$', '')
end

local eb_base_path = '/apps/easybuild/'
-- Constant to the path where the sources for EasyBuild live
local eb_sources = eb_base_path .. 'sources'
local eb_path_ver = os.getenv('HPC_OS_SHORT') .. '-'
local eb_path_plat = eb_path_ver .. os.getenv('HPC_ARCH_CPU_SHORTNAME')

local eb_this_toolchain_plat_path = eb_base_path .. toolchain_ver .. '/' .. eb_path_plat
append_path('MODULEPATH', eb_this_toolchain_plat_path .. '/modules/all')
append_path('MODULEPATH', eb_this_toolchain_plat_path .. '/modules')
setenv('EB_TOOLCHAIN_VERSION', toolchain_ver)
setenv('EB_BASE_PATH', eb_this_toolchain_plat_path)

-- If version contains a '-' then strip that off as a suffix and stack with the version without it
local suffix = toolchain_ver:match('-.*$')
local suffix_base
local suffix_plt_path
if suffix then
	suffix_base = toolchain_ver:gsub('-.*$', '')
	suffix_plt_path = eb_base_path .. suffix_base .. '/' .. eb_path_plat
	append_path('MODULEPATH', suffix_plt_path .. '/modules/all')
	append_path('MODULEPATH', suffix_plt_path .. '/modules')
end


-- If needed, prepare the development environment
if dev then
    local eb_config_file = eb_this_toolchain_plat_path .. '/eb-config.cfg'
    append_path('PATH', eb_base_path .. '/scripts')
    setenv('EASYBUILD_CONFIGFILES', eb_config_file)
    if not isFile(eb_config_file) then
        if mode() == "load" then
            -- Attempt to initialise new environment
            if not isDir(eb_base_path .. toolchain_ver) then
                local init_path = eb_base_path .. toolchain_ver
                LmodMessage("Initialising new toolchain " .. toolchain_ver .. " in " .. init_path)
                -- Entirely new toolchain - bootstrap the toolchain first.
                local bootstrap_toolchain_args='"--install-sources=' .. eb_sources .. '"'
                
                -- If not the main branch then we will clone the easyconfigs
                -- to this new development branch.
                if suffix then
                    bootstrap_toolchain_args=bootstrap_toolchain_args .. ' --clone-easyconfigs'
                -- If this is the main branch, then create the sources folder for downloaded sources
                else
                    bootstrap_toolchain_args=bootstrap_toolchain_args .. ' --create-easybuild-sources-folder'
                end
                os.execute("bash -c 'eb-toolchain-bootstrap " .. bootstrap_toolchain_args .. " " .. init_path .. " > /tmp/module-eb-bootstrap ; mv /tmp/module-eb-bootstrap \"" .. init_path .. "/init.log\"'")
            end
            LmodMessage("Initialising new plaform " .. eb_path_plat .. " in " .. eb_this_toolchain_plat_path)
            local platform_init_args='"--install-sources=' .. eb_sources .. '"'

            -- Always use the sources folder in the main branch
            if suffix then
                platform_init_args=platform_init_args .. ' "--easybuild-source-path=' .. eb_base_path .. suffix_base .. '/sources"'
                platform_init_args=platform_init_args .. ' "--install-easyconfigs-source=' .. eb_base_path .. toolchain_ver .. '"'
            else
                platform_init_args=platform_init_args .. ' "--easybuild-source-path=' .. eb_base_path .. toolchain_ver .. '/sources"'
            end
            -- Initialise the platform
            os.execute("bash -c 'eb-platform-init " .. platform_init_args .. " " .. eb_this_toolchain_plat_path .. " > /tmp/module-eb-platform-init ; mv /tmp/module-eb-platform-init \"" .. eb_this_toolchain_plat_path .. "/init.log\"'")
        end
    end
    -- Equivalent of sourceing venv's activate script
    -- Do this after initialising a new environment, so the environment is made available even if it's just been initialised
    local venv = eb_this_toolchain_plat_path .. '/venv'
    prepend_path('PATH', venv .. '/bin')
    setenv('VIRTUAL_ENV', venv)
-- If not development mode, enable the fallback module paths
else
    -- Not for development as we want to build everything for the one platform
    -- (having fallback platform modules will confuse what is built for this one)
    for compatible_arch in string.gmatch(os.getenv('HPC_ARCH_CPU_COMPAT'), "[^%s]+") do
	local compatible_suffix = eb_path_ver .. compatible_arch
	local compatible_toolchain_plat_path = eb_base_path .. toolchain_ver .. '/' .. compatible_suffix
        append_path('MODULEPATH', compatible_toolchain_plat_path .. '/modules/all')
        append_path('MODULEPATH', compatible_toolchain_plat_path .. '/modules')
	-- Also stack compatible architectures main builds
	if suffix then
		local compatible_suffix_plt_path = eb_base_path .. suffix_base .. '/' .. compatible_suffix
        	append_path('MODULEPATH', compatible_suffix_plt_path.. '/modules/all')
	        append_path('MODULEPATH', compatible_suffix_plt_path .. '/modules')	
	end
    end
end

if (mode() == "unload") then
	-- If unloading, we want the dependency processed (unloaded) late --
	depends_on('hpc_environment')
end

Developing the scripts

Here I go on to describe the development of these scripts…

(Optional) download EasyBuild and dependencies

As part of my infrastructure has no internet access, I also need to download prerequisite packages on a machine that does have internet access in order to be able to bootstrap EasyBuild on each platform.

I created this script, saved as eb-download, to automate the process:

#!/bin/bash

# Copyright 2021 Laurence Alexander Hurst
#
# This program 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, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.

# Abort on error
set -e

opts=$( getopt -o h\?d --long help,depends,dependencies -n "$( basename "$0" )" -- "$@" )
eval set -- "$opts"
unset opts

usage() {
    cat - >&2 <<EOF
Usage: $0 [-h|-?|--help] [-d|--depends|--dependencies] target_directory
This script will download (or update with 'git pull') the EasyBuild
sources and (optionally) its dependencies, to aid bootstrapping an
EasyBuild install.
-h|-?|--help) Display this message and exit
-d|--depends|--dependencies) Fetch the dependencies too (requires 'pip')
target_directory) Directory into which to fetch EasyBuild sources
EOF
}

# Won't fetch dependencies unless user asks us to
FETCH_DEPS=0

while true
do
    case "$1" in
        -h|-\?|--help)
            usage
            exit
            ;;
        -d|--depends|--dependencies)
            FETCH_DEPS=1
            shift
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "ERROR: This code should be unreachable!" >&2
            exit 1
            ll
    esac
done

TARGET="$1"
if [[ -z "$TARGET" ]]
then
    echo "No target directory specified!" >&2
    usage
    exit 1
fi

# Do some basic checks before we get in too deep - "fail early"
if ! which git &>/dev/null
then
    echo "git command not found - is git installed?" >&2
    exit 1
fi

if [[ $FETCH_DEPS -eq 1 ]]
then
    if which pip &>/dev/null && pip --version | grep -q '(python 3'
    then
        # We checked the Python version is 3 above - pip --version outputs the
        # Python version which we can grep for, e.g.:
        # pip 9.0.3 from /usr/lib/python3.6/site-packages (python 3.6)
        # pip 20.3.4 from /tmp/venv/local/lib/python2.7/site-packages/pip (python 2.7)
        PIP_CMD=pip
    # On CentOS 7 the default system python is still Python 2 - checking for
    # version 3 specific command...
    elif which pip3 &>/dev/null
    then
        PIP_CMD=pip3
    else
        echo "pip (or pip3) command not found - is pip installed?" >&2
        exit 1
    fi
fi

# Make the target, if it does not exist
if ! [ -d "$TARGET" ]
then
    mkdir -p "$TARGET"
fi

# Go there
cd "$TARGET"

# Start by fetching dependencies, if requested
if [[ $FETCH_DEPS -eq 1 ]]
then
    if ! [ -d ext ]
    then
        mkdir ext
    else
        echo "Warning: $TARGET/ext directory already exists" >&2
        echo "downloading there anyway but this might not be what you wanted." >&2
    fi

    # We want vsc-base and virtualenv
    packages="vsc-base virtualenv"
    echo "Fetching dependencies:"
    cd ext
    for package in $packages
    do
        echo "$package..."
        $PIP_CMD download $package
    done
    cd ..
    echo "Fetched all dependencies."
fi

# Fetch EasyBuild
echo "Fetching EasyBuild:"
for component in easybuild-framework easybuild-easyblocks easybuild-easyconfigs
do
    if [ -e "$component" ]
    then
        echo "$component already downloaded - updating main branch for you..."
        cd "$component"
        if [[ $(git rev-parse --abbrev-ref HEAD) == "develop" ]]
        then
            # develop branch checked out
            git pull origin || echo '!!! Pull failed !!!  Carrying on regardless...' "($component)" >&2
        else
            # another branch checked out, as long as its a ff we can update
            git fetch origin develop:develop || echo '!!! Fetch failed !!!  Carrying on regardless...' "($component)" >&2
        fi
        cd ..
    else
        echo "Fetching $component..."
        git clone https://github.com/easybuilders/$component.git
    fi
done

echo "All done."

Creating our EasyBuild environment(s)

I chose to separate built software by toolchain version - it simplifies management for a few reasons, including:

  • from a user perspective there are less conflicting modules within a single environment (there are still conflicts, particularly with different toolchains e.g. ‘intel’ vs ‘foss’)
  • from an administrator perspective, I can consider each toolchain version a single collection of software to be supported and retired together

Each platform has its own virtualenv - while each platform is x86_64 compatible has a common operating system (and using the system python), this is not essential however once multiple OSs and/or more exotic processor architectures enter the mix the virtualenv stops being portable between all of the platforms.

The first time I did this, I gave each toolchain version its own copy of EasyBuild, thinking that I might want to create per-toolchain modifications. In practice, I found that a common easybuild, easyblock and easyconfig tree makes things easier to manage. I instead split the environments into a main one, into which the final builds are made on the development cluster for each platform before being copied to live, and separate ones as I developed the builds (which makes parallel builds possible without risking trying to build/install the same modules at the same time through different builds).

Essentially these development versions are branches but to facilitate working on different branches concurrently, then need their own copy of the easyconfigs (and easyblocks/easybuild if they also need modifying). In my setup, these are cloned from the central copy used for the main branch do I have a central point to pull/push upstream.

The workflow for the main branch works like this:

  1. Initialise the platform-specific virtualenv directly from the central sources (or Github if not using local sources - in which case environment isolation is inherent so the same model works for development but it requires internet to bootstrap and you end up with each platform’s virtualenv having its own sources in src within the virtualenv tree - a big headache when you have multiple platforms per toolchain environment to maintain)
  2. On an internet connected machine (with a compatible, initialised easybuild), use eb -Tr --fetch to download all the sources required for the build
  3. build the software you require for that platform
  4. once a stable set of software has been achieved, built software (and their corresponding modules) can be copied from the main branch to the live environment (assuming dev and live are identical)

The workflow for developing a new easyconfig, which only makes sense when using a common local central source, then works like this:

  1. Clone the easyconfigs (and optionally the easyblocks and freameowrk) from the central copy
  2. Initialise the platform-specific venv from the new clone(s) and use the central ones for components not cloned
  3. Modify the easyconfig(s)/easyblock(s) in a new easyconfig git branch
  4. On an internet connected machine (with a compatible, initialised easybuild), use eb -Tr --fetch to download all the sources required for the build
  5. build the software to check it works (iterating as necessary)
  6. Push the branch to the central copy (the copy’s origin)
  7. (Optionally) destroy the development environment, if not doing to start a new branch from main for a new piece of work
  8. Push the branch upstream (the central copy’s origin)

As for downloading the sources, I created scripts to automate this.

The first is called eb-toolchain-bootstrap and creates the basic directory structure for a new toolchain, optionally creating its own clone of a local clone of the EasyBuild sources.

#!/bin/bash

# Copyright 2021 Laurence Alexander Hurst
#
# This program 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, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.

# Abort on error
set -e

opts=$( getopt -o h\?efbcs: --long help,create-easybuild-sources-folder,clone-framework,clone-easyblocks,clone-easyconfigs,install-sources: -n "$( basename "$0" )" -- "$@" )
eval set -- "$opts"
unset opts

SOURCES_PATH=""  # Path to installation files
CREATE_EB_SOURCES=0
CLONE_FRAMEWORK=0
CLONE_EASYBLOCKS=0
CLONE_EASYCONFIGS=0

usage() {
    cat - >&2 <<EOF
Usage: $0 [-h|-?|--help] [-e|--create-easybuild-sources-folder] [-f|--clone-framework] [-b|--clone-easyblocks] [-c|--clone-easyconfigs] [-ssource_directory|--install-sources=source_directory] target_directory
This script will prepare a new toolchain environment, ready for
platform-specific environments to be created.
If given a directory containing EasyBuild source files, it can
(optionally) clone each of the framework, easyblocks or easyconfigs to
provide an isolated copy for development without impacting other
toolchain environments.
-h|-?|--help) Display this message and exit
-e|--create-easybuild-sources-folder) Create a folder for EasyBuild to
    store its source files in within the toolchain folder.
-f|--clone-framework) Clone the framework sources from the sources path
    (requires -s/--install-sources)
-b|--clone-easyblocks) Clone the easyblocks sources from the sources
    path (requires -s/--install-sources)
-c|--clone-easyconfigs) Clone the easyconfigs sources from the sources
    path (requires -s/--install-sources)
-s|--install-sources) Specify the path to where the EasyBuild sources (
    easybuild-framework, easybuild-easyconfigs, easybuild-easyconfigs)
    can be found.
target_directory) Directory into which to create the new environment
EOF
}

while true
do
    case "$1" in
        -h|-\?|--help)
            usage
            exit
            ;;
        -e|--create-easybuild-sources-folder)
            CREATE_EB_SOURCES=1
            shift
            ;;
        -f|--clone-framework)
            CLONE_FRAMEWORK=1
            shift
            ;;
        -b|--clone-easyblocks)
            CLONE_EASYBLOCKS=1
            shift
            ;;
        -c|--clone-easyconfigs)
            CLONE_EASYCONFIGS=1
            shift
            ;;
        -s|--install-sources)
            SOURCES_PATH="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "ERROR: This code should be unreachable!" >&2
            exit 1
            ll
    esac
done

TARGET="$1"
if [[ -z "$TARGET" ]]
then
    echo "No target directory specified!" >&2
    usage
    exit 1
fi

if [[ $CLONE_FRAMEWORK -eq 1 ]] || [[ $CLONE_EASYBLOCKS -eq 1 ]] || [[ $CLONE_EASYCONFIGS -eq 1 ]]
then
    if [[ -z "$SOURCES_PATH" ]]
    then
        echo "--install-sources is not optional if you wish to clone any part of EasyBuild" >&2
        usage
        exit 1
    fi
fi

mkdir "$TARGET"

if [[ $CREATE_EB_SOURCES -eq 1 ]]
then
    mkdir "$TARGET/sources"
fi

if [[ $CLONE_FRAMEWORK -eq 1 ]]
then
    git clone $SOURCES_PATH/easybuild-framework $TARGET/easybuild-framework
fi

if [[ $CLONE_EASYBLOCKS -eq 1 ]]
then
    git clone $SOURCES_PATH/easybuild-easyblocks $TARGET/easybuild-easyblocks
fi

if [[ $CLONE_EASYCONFIGS -eq 1 ]]
then
    git clone $SOURCES_PATH/easybuild-easyconfigs $TARGET/easybuild-easyconfigs
fi

The next initialises a new virtualenv on a platform and is called eb-platform-init:

#!/bin/bash

# Copyright 2021 Laurence Alexander Hurst
#
# This program 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, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.

# Abort on error
set -e

opts=$( getopt -o h\?e:f:b:c:s: --long help,easybuild-source-path:,install-sources:,install-framework-source:,install-easyblocks-source:,install-easyconfigs-source: -n "$( basename "$0" )" -- "$@" )
eval set -- "$opts"
unset opts

SOURCES_PATH=""  # Path to installation files
FRAMEWORK_SOURCE_PATH=""  # Override path for the framework
EASYBLOCKS_SOURCE_PATH=""  # Override path for the framework
EASYCONFIGS_SOURCE_PATH=""  # Override path for the framework
EB_SOURCES_PATH=""  # Path to tell EB to use for its sources
EB_CONFIG_FILE="eb-config.cfg"  # Might want to make this configurable?
EB_VENV_DIR="venv" # Might want to make this configurable?

usage() {
    cat - >&2 <<EOF
Usage: $0 [-h|-?|--help] [-esource_storage_path|--easybuild-source-path=source_storage_path] [-ssource_directory|--install-sources=source_directory] [-fsource_storage_path|--install-framework-source=source_storage_path] [-bsource_storage_path|--install-easyblocks-source=source_storage_path] [-csource_storage_path|--install-easyconfigs-source=source_storage_path] target_directory
This script will create a new EasyBuild environment, with its own 
virtualenv containing EasyBuild in $EB_VENV_DIR subdirectory.
If given a directory containing EasyBuild source files, it will install
EasyBuild from there (in 'editable mode', that is using the sources
directly), otherwise it will try to install from GitHub.
If the sources directory contains a subdirectory called 'ext', pip will
be instructed to only install packages found there - make sure it
contains all the dependencies required if you want to use this feature.
(The eb-download script will help you with this.)
-h|-?|--help) Display this message and exit
-e|--easybuild-source-path) The path to configure EasyBuild to store/
    look for package sources.
-s|--install-sources) Specify the path to where the EasyBuild sources (
    easybuild-framework, easybuild-easyconfigs, easybuild-easyconfigs)
    can be found.
-f|--install-framework-source) Override the sources path just for
    the framework.
-b|--install-easyblocks-source) Override the sources path just
    for the easyblocks.
-c|--install-easyconfigs-source) Override the sources path just
    for the easyconfigs.
target_directory) Directory into which to create the new environment
EOF
}

while true
do
    case "$1" in
        -h|-\?|--help)
            usage
            exit
            ;;
        -s|--install-sources)
            SOURCES_PATH="$2"
            shift 2
            ;;
        -f|--install-framework-source)
            FRAMEWORK_SOURCE_PATH="$2"
            shift 2
            ;;
        -b|--install-easyblocks-source)
            EASYBLOCKS_SOURCE_PATH="$2"
            shift 2
            ;;
        -c|--install-easyconfigs-source)
            EASYCONFIGS_SOURCE_PATH="$2"
            shift 2
            ;;
        -e|--easybuild-source-path)
            EB_SOURCES_PATH="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "ERROR: This code should be unreachable!" >&2
            exit 1
            ll
    esac
done

TARGET="$1"
if [[ -z "$TARGET" ]]
then
    echo "No target directory specified!" >&2
    usage
    exit 1
fi

# Do some basic checks before we get in too deep - "fail early"
if which pip &>/dev/null && pip --version | grep -q '(python 3'
then
    # We checked the Python version is 3 above - pip --version outputs the
    # Python version which we can grep for, e.g.:
    # pip 9.0.3 from /usr/lib/python3.6/site-packages (python 3.6)
    # pip 20.3.4 from /tmp/venv/local/lib/python2.7/site-packages/pip (python 2.7)
    PIP_CMD=pip
# On CentOS 7 the default system python is still Python 2 - checking for
# version 3 specific command...
elif which pip3 &>/dev/null
then
    PIP_CMD=pip3
    PYTHON_CMD=python3  # Match the pip command
else
    echo "pip (or pip3) command not found - is pip installed?" >&2
    exit 1
fi

if ! which mktemp &>/dev/null
then
    echo "mktemp command not found - is mktemp installed?" >&2
    exit 1
fi

if [[ -e "$TARGET/$EB_CONFIG_FILE" ]] || [[ -e "$TARGET/$EB_VENV_DIR" ]]
then
    echo "Target $TARGET looks like it is already setup?" >&2
    echo "(remove $EB_CONFIG_FILE and $EB_VENV_DIR to re-do setup)" >&2
    exit 1
fi

# Make the target, if it does not exist
if ! [ -d "$TARGET" ]
then
    mkdir -p "$TARGET"
fi

# Extra arguments for pip if using local sources
if [[ -n "$SOURCES_PATH" ]] && [[ -e "$SOURCES_PATH/ext" ]]
then
    # Make pip run entirely offline using packages from "$SOURCES_PATH/ext"
    echo "Using $SOURCES_PATH/ext for pip packages."
    PIP_INSTALL_OPTS="--no-index --find-links=$SOURCES_PATH/ext"
else
    PIP_INSTALL_OPTS=""
fi

# Create the virtualenv:
echo "Creating virtualenv in $EB_VENV_DIR..."
# I found it to be hit and miss whether virtualenv or the venv module
# were available on systems, so install it to a temporary location.
venv_tmp="$( mktemp -d )"
$PIP_CMD install $PIP_INSTALL_OPTS --prefix="$venv_tmp" virtualenv
if [[ ! -z "$PYTHON_CMD" ]]
then
    VENV_OPTS="--python=$PYTHON_CMD"
else
    VENV_OPTS=""
fi
# Assume there is only 1 python version there
PYTHONPATH="$venv_tmp/lib/$( ls "$venv_tmp/lib" | head -n1 )/site-packages" "$venv_tmp/bin/virtualenv" $VENV_OPTS "$TARGET/$EB_VENV_DIR"
# Tidy up
rm -rf "$venv_tmp"
unset venv_tmp

# Load the environment
source "$TARGET/$EB_VENV_DIR/bin/activate"

# Install re-requisites - now inside the venv so "pip" will always be
# the right version to use.
echo "Installing prerequisites..."
pip install $PIP_INSTALL_OPTS vsc-install
pip install $PIP_INSTALL_OPTS vsc-base

# Install EasyBuild
if [[ -z "$SOURCES_PATH" ]]
then
    # Installing from github
    EB_SOURCE="https://github.com/easybuilders"
    EB_SOURCE_SUFFIX=".git"
else
    EB_SOURCE="$SOURCES_PATH"
    EB_SOURCE_SUFFIX=""
fi
echo "Installing EasyBuild from $EB_SOURCE..."
if [[ -n "$FRAMEWORK_SOURCE_PATH" ]]
then
    pip install $PIP_INSTALL_OPTS -e $FRAMEWORK_SOURCE_PATH/easybuild-framework
else
    pip install $PIP_INSTALL_OPTS -e $EB_SOURCE/easybuild-framework$EB_SOURCE_SUFFIX
fi
if [[ -n "$EASYBLOCKS_SOURCE_PATH" ]]
then
    pip install $PIP_INSTALL_OPTS -e $EASYBLOCKS_SOURCE_PATH/easybuild-easyblocks
else
    pip install $PIP_INSTALL_OPTS -e $EB_SOURCE/easybuild-easyblocks$EB_SOURCE_SUFFIX
fi
if [[ -n "$EASYCONFIGS_SOURCE_PATH" ]]
then
    pip install $PIP_INSTALL_OPTS -e $EASYCONFIGS_SOURCE_PATH/easybuild-easyconfigs
else
    pip install $PIP_INSTALL_OPTS -e $EB_SOURCE/easybuild-easyconfigs$EB_SOURCE_SUFFIX
fi

# Create a configuration file for EasyBuild
cat - >"$TARGET/$EB_CONFIG_FILE" <<EOF
[config]
# Using /dev/shm (ramdisk) will massively reduce the build time, provide it is large enough
buildpath = /dev/shm/eb-build
installpath = $TARGET
EOF

if [[ -n "$EB_SOURCES_PATH" ]]
then
    echo "sourcepath = $EB_SOURCES_PATH" >> "$TARGET/$EB_CONFIG_FILE"
fi

echo "All done."
echo "To use:"
echo "source $TARGET/$EB_VENV_DIR/bin/activate"
echo "export EASYBUILD_CONFIGFILES=\"$TARGET/$EB_CONFIG_FILE\""
echo "eb --version  # To check it is running"