Skip to content

Commit

Permalink
VERY EARLY DRAFT.
Browse files Browse the repository at this point in the history
  • Loading branch information
Petar Forai committed Oct 15, 2014
1 parent 55dc002 commit 9919c3a
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 29 deletions.
18 changes: 11 additions & 7 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
from easybuild.tools.filetools import write_file, compute_checksum, verify_checksum
from easybuild.tools.run import run_cmd
from easybuild.tools.jenkins import write_to_xml
from easybuild.tools.module_generator import ModuleGenerator
from easybuild.tools.module_generator import ModuleGeneratorLua
from easybuild.tools.module_naming_scheme.utilities import det_full_ec_version
from easybuild.tools.modules import ROOT_ENV_VAR_NAME_PREFIX, VERSION_ENV_VAR_NAME_PREFIX, DEVEL_ENV_VAR_NAME_PREFIX
from easybuild.tools.modules import get_software_root, modules_tool
Expand Down Expand Up @@ -131,7 +131,7 @@ def __init__(self, ec):
# modules interface with default MODULEPATH
self.modules_tool = modules_tool()
# module generator
self.moduleGenerator = ModuleGenerator(self, fake=True)
self.moduleGenerator = ModuleGeneratorLua(self, fake=True)

# modules footer
self.modules_footer = None
Expand Down Expand Up @@ -728,7 +728,7 @@ def make_devel_module(self, create_in_builddir=False):
# load fake module
fake_mod_data = self.load_fake_module(purge=True)

mod_gen = ModuleGenerator(self)
mod_gen = ModuleGeneratorLua(self)
header = "#%Module\n"

env_txt = ""
Expand Down Expand Up @@ -829,9 +829,12 @@ def make_module_extra(self):

# EBROOT + EBVERSION + EBDEVEL
environment_name = convert_name(self.name, upper=True)
txt += self.moduleGenerator.set_environment(ROOT_ENV_VAR_NAME_PREFIX + environment_name, "$root")
#todo this is only valid for Lua now
# This is a bit different in Lua due to string quoting rules in Lua and in Tcl - so $root cannot be used easily.
# so we resort to rendering our internal variables and quote them in the set_environment() like all other values.
txt += self.moduleGenerator.set_environment(ROOT_ENV_VAR_NAME_PREFIX + environment_name, self.installdir)
txt += self.moduleGenerator.set_environment(VERSION_ENV_VAR_NAME_PREFIX + environment_name, self.version)
devel_path = os.path.join("$root", log_path(), ActiveMNS().det_devel_module_filename(self.cfg))
devel_path = os.path.join(self.installdir, log_path(), ActiveMNS().det_devel_module_filename(self.cfg))
txt += self.moduleGenerator.set_environment(DEVEL_ENV_VAR_NAME_PREFIX + environment_name, devel_path)

txt += "\n"
Expand Down Expand Up @@ -872,7 +875,8 @@ def make_module_footer(self):
"""
Insert a footer section in the modulefile, primarily meant for contextual information
"""
txt = '\n# Built with EasyBuild version %s\n' % VERBOSE_VERSION
#@todo fix this as it is lua specific
txt = '\n -- Built with EasyBuild version %s\n' % VERBOSE_VERSION

# add extra stuff for extensions (if any)
if self.cfg['exts_list']:
Expand Down Expand Up @@ -1658,7 +1662,7 @@ def make_module_step(self, fake=False):
txt += self.make_module_extra()
txt += self.make_module_footer()

write_file(self.moduleGenerator.filename, txt)
write_file(self.moduleGenerator.filename+".lua", txt)

self.log.info("Module file %s written" % self.moduleGenerator.filename)

Expand Down
185 changes: 168 additions & 17 deletions easybuild/tools/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,9 @@
_log = fancylogger.getLogger('module_generator', fname=False)


class ModuleGenerator(object):
"""
Class for generating module files.
"""
class ModuleGenerator:
def __init__(self, application, fake=False):
self.fake = fake
self.app = application
self.fake = fake
self.tmpdir = None
Expand Down Expand Up @@ -92,6 +90,29 @@ def create_symlinks(self):
except OSError, err:
_log.error("Failed to create symlinks from %s to %s: %s" % (self.class_mod_files, self.filename, err))

def is_fake(self):
"""Return whether this ModuleGeneratorTcl instance generates fake modules or not."""
return self.fake

def set_fake(self, fake):
"""Determine whether this ModuleGeneratorTcl instance should generate fake modules."""
_log.debug("Updating fake for this ModuleGeneratorTcl instance to %s (was %s)" % (fake, self.fake))
self.fake = fake
# fake mode: set installpath to temporary dir
if self.fake:
self.tmpdir = tempfile.mkdtemp()
_log.debug("Fake mode: using %s (instead of %s)" % (self.tmpdir, self.module_path))
self.module_path = self.tmpdir
else:
self.module_path = config.install_path('mod')


class ModuleGeneratorTcl(ModuleGenerator):
"""
Class for generating Tcl module files.
"""


def get_description(self, conflict=True):
"""
Generate a description.
Expand Down Expand Up @@ -189,6 +210,7 @@ def prepend_paths(self, key, paths, allow_abs=False):
statements = [template % (key, p) for p in paths]
return ''.join(statements)


def use(self, paths):
"""
Generate module use statements for given list of module paths.
Expand Down Expand Up @@ -231,18 +253,147 @@ def set_alias(self, key, value):
# quotes are needed, to ensure smooth working of EBDEVEL* modulefiles
return 'set-alias\t%s\t\t%s\n' % (key, quote_str(value))

def set_fake(self, fake):
"""Determine whether this ModuleGenerator instance should generate fake modules."""
_log.debug("Updating fake for this ModuleGenerator instance to %s (was %s)" % (fake, self.fake))
self.fake = fake
# fake mode: set installpath to temporary dir
if self.fake:
self.tmpdir = tempfile.mkdtemp()
_log.debug("Fake mode: using %s (instead of %s)" % (self.tmpdir, self.module_path))
self.module_path = self.tmpdir

class ModuleGeneratorLua(ModuleGenerator):
"""
Class for generating Lua module files.
"""

def get_description(self, conflict=True):
"""
Generate a description.
"""

description = "%s - Homepage: %s" % (self.app.cfg['description'], self.app.cfg['homepage'])


lines = [
"local pkg = {}",
"help = [["
"%(description)s"
"]]",
"whatis([[Name: %(name)s]])",
"whatis([[Version: %(version)s]])",
"whatis([[Description: %(description)s]])",
"whatis([[Homepage: %(homepage)s]])"
"whatis([[License: N/A ]])",
"whatis([[Keywords: Not set]])",
"",
"",
'pkg.root="%(installdir)s"',
"",
]

#@todo check if this is really needed, imho Lmod doesnt need this at all.
if self.app.cfg['moduleloadnoconflict']:
lines.extend([
'if ( not isloaded("%(name)s/%(version)s")) then',
' load("%(name)s/%(version)s")',
'end',
])

elif conflict:
# conflicts are not needed in lua module files, as Lmod "conflict" by default
pass

txt = '\n'.join(lines) % {
'name': self.app.name,
'version': self.app.version,
'description': description,
'installdir': self.app.installdir,
'homepage': self.app.cfg['homepage'],
}


return txt

def load_module(self, mod_name):
"""
Generate load statements for module.
"""
if build_option('recursive_mod_unload'):
# not wrapping the 'module load' with an is-loaded guard ensures recursive unloading;
# when "module unload" is called on the module in which the depedency "module load" is present,
# it will get translated to "module unload"
load_statement = ['load("%(mod_name)s")']
else:
self.module_path = config.install_path('mod')
load_statement = [
'if ( not isloaded("%(mod_name)s")) then',
' load("%(mod_name)s")',
'end',
]
return '\n'.join([""] + load_statement + [""]) % {'mod_name': mod_name}

def is_fake(self):
"""Return whether this ModuleGenerator instance generates fake modules or not."""
return self.fake
def unload_module(self, mod_name):
"""
Generate unload statements for module.
"""
return '\n'.join([
"",
"if (isloaded(%(mod_name)s)) then",
" unload(%(mod_name)s)",
"end",
"",
]) % {'mod_name': mod_name}

def prepend_paths(self, key, paths, allow_abs=False):
"""
Generate prepend-path statements for the given list of paths.
"""
template = 'prepend_path(%s,%s)\n'

if isinstance(paths, basestring):
_log.info("Wrapping %s into a list before using it to prepend path %s" % (paths, key))
paths = [paths]

# make sure only relative paths are passed
for i in xrange(len(paths)):
if os.path.isabs(paths[i]) and not allow_abs:
_log.error("Absolute path %s passed to prepend_paths which only expects relative paths." % paths[i])
elif not os.path.isabs(paths[i]):
# prepend $root (= installdir) for relative paths
paths[i] = ' pathJoin(pkg.root,"%s")' % paths[i]

statements = [template % (quote_str(key), p) for p in paths]
return ''.join(statements)

def use(self, paths):
"""
Generate module use statements for given list of module paths.
"""
use_statements = []
for path in paths:
use_statements.append('use("%s")' % path)
return '\n'.join(use_statements)


def set_environment(self, key, value):

"""
Generate setenv statement for the given key/value pair.
"""
# quotes are needed, to ensure smooth working of EBDEVEL* modulefiles
return 'setenv("%s", %s)\n' % (key, quote_str(value))


def msg_on_load(self, msg):
"""
Add a message that should be printed when loading the module.
"""
pass


def add_tcl_footer(self, tcltxt):
"""
Append whatever Tcl code you want to your modulefile
"""
# nothing to do here, but this should fail in the context of generating Lua modules
pass


def set_alias(self, key, value):
"""
Generate set-alias statement in modulefile for the given key/value pair.
"""
# quotes are needed, to ensure smooth working of EBDEVEL* modulefiles
return 'setalias(%s,"%s")\n' % (key, quote_str(value))
4 changes: 3 additions & 1 deletion easybuild/tools/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,9 @@ def dependencies_for(self, mod_name, depth=sys.maxint):
@param depth: recursion depth (default is sys.maxint, which should be equivalent to infinite recursion depth)
"""
modtxt = self.read_module_file(mod_name)
loadregex = re.compile(r"^\s*module\s+load\s+(\S+)", re.M)
#@todo: this was removed for Lmod
#loadregex = re.compile(r"^\s*module\s+load\s+(\S+)", re.M)
loadregex = re.compile(r"load\(\"(\S*)\"", re.M)
mods = loadregex.findall(modtxt)

if depth > 0:
Expand Down
8 changes: 4 additions & 4 deletions test/framework/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import easybuild.tools.module_generator
from easybuild.framework.easyconfig.tools import process_easyconfig
from easybuild.tools import config
from easybuild.tools.module_generator import ModuleGenerator
from easybuild.tools.module_generator import ModuleGeneratorTcl
from easybuild.tools.module_naming_scheme.utilities import is_valid_module_name
from easybuild.framework.easyblock import EasyBlock
from easybuild.framework.easyconfig.easyconfig import EasyConfig, ActiveMNS
Expand All @@ -50,10 +50,10 @@


class ModuleGeneratorTest(EnhancedTestCase):
""" testcase for ModuleGenerator """
""" testcase for ModuleGeneratorTcl """

def setUp(self):
""" initialize ModuleGenerator with test Application """
""" initialize ModuleGeneratorTcl with test Application """
super(ModuleGeneratorTest, self).setUp()
# find .eb file
eb_path = os.path.join(os.path.join(os.path.dirname(__file__), 'easyconfigs'), 'gzip-1.4.eb')
Expand All @@ -62,7 +62,7 @@ def setUp(self):

ec = EasyConfig(eb_full_path)
self.eb = EasyBlock(ec)
self.modgen = ModuleGenerator(self.eb)
self.modgen = ModuleGeneratorTcl(self.eb)
self.modgen.app.installdir = tempfile.mkdtemp(prefix='easybuild-modgen-test-')

self.orig_module_naming_scheme = config.get_module_naming_scheme()
Expand Down

0 comments on commit 9919c3a

Please sign in to comment.