Skip to content
Snippets Groups Projects

Support new Gaudi CMake

Merged Marco Clemencic requested to merge support-new-gaudi-cmake into master
1 file
+ 214
1
Compare changes
  • Side-by-side
  • Inline
@@ -14,6 +14,7 @@ Module grouping the common build functions.
__author__ = 'Marco Clemencic <marco.clemencic@cern.ch>'
import os
import re
import logging
import shutil
from datetime import datetime
@@ -215,7 +216,7 @@ class cmt(make):
return 'CMT'
class cmake(make):
class cmake_old(make):
'''
Class to wrap the build/test semantics for CMT-based projects.
'''
@@ -325,6 +326,218 @@ class cmake(make):
return BuildResults(proj, *output)
def __str__(self):
'''
Conversion to string.
'''
return 'CMake (old)'
class cmake_new(object):
# known "kwargs" not to be propagated to subprocess
SPECIAL_ARGS = ('jobs', 'max_load', 'args', 'make_cmd', 'cache_entries',
'step')
def _cache_preload_file(self, proj):
'''
Name of the cache preload file to be passed to CMake.
'''
return os.path.join(proj.baseDir, 'cache_preload.cmake')
def _prepare_cache(self, proj, cache_entries=None):
'''
Prepare the cache_preload.cmake file passed to CMake during the
configuration.
'''
# prepare the cache to give to CMake: add the launcher rules commands,
# followed by what is found passed as argument
if cache_entries is None:
cache_entries = []
elif hasattr(cache_entries, 'items'):
cache_entries = cache_entries.items()
cache_file = self._cache_preload_file(proj)
ensure_dir(os.path.dirname(cache_file))
with open(cache_file, 'w') as cache:
cache.writelines([
'set(%s "%s" CACHE STRING "override")\n' % item
for item in cache_entries
])
# force use of ccache
cache.writelines(
'set(CMAKE_{}_COMPILER_LAUNCHER ccache CACHE STRING "override")\n'
.format(lang) for lang in ('C', 'CXX'))
# enable rule wrappers
cache.writelines((
'set(CMAKE_RULE_LAUNCH_{} "lbn-wrapcmd <CMAKE_CURRENT_BINARY_DIR> <TARGET_NAME>"\n'
' CACHE STRING "override")\n').format(action)
for action in ('COMPILE', 'LINK', 'CUSTOM'))
def _call(self, proj, cmd, **kwargs):
# strip special kwargs before command invocation
cmd_kwargs = {
n: v
for n, v in kwargs.items() if n not in self.SPECIAL_ARGS
}
__log__.debug('running %s', ' '.join(cmd))
started = datetime.now()
result = log_call(cmd, **cmd_kwargs)
completed = datetime.now()
__log__.debug('command exited with code %d', result['retcode'])
out = ('#### {0} {1} ####\n'
'# Start: {2}\n{3}'
'# End: {4}\n').format(self, kwargs.get('step',
'(no step name)'),
started.isoformat(), result['stdout'],
completed.isoformat())
return BuildResults(proj, result['retcode'], out, result['stderr'],
started, completed)
def configure(self, proj, **kwargs):
self._prepare_cache(proj, cache_entries=kwargs.get('cache_entries'))
# basic cmake command
cmd = [
'cmake', '-S', proj.baseDir, '-B',
os.path.join(proj.baseDir, 'build'), '-G',
kwargs.get('generator', 'Ninja'), '-C',
self._cache_preload_file(proj)
]
# get the toolchain to use
if proj.slot and hasattr(proj.slot, 'LCG'):
LCG_VERSION = proj.slot.LCG.version
elif 'LCG_VERSION' in os.environ:
LCG_VERSION = os.environ['LCG_VERSION']
else:
raise RuntimeError(
'version of LCG not defined (required for new CMake configuration)'
)
cmd.append('-DCMAKE_TOOLCHAIN_FILE=' + os.path.join(
os.getcwd(),
'../lcg-toolchains', # FIXME how to locate them?
'LCG_' + LCG_VERSION,
os.environ['BINARY_TAG'] + '.cmake'))
return self._call(proj, cmd, step='configure')
def install(self, proj, **kwargs):
return self._call(
proj, [
'cmake', '--install',
os.path.join(proj.baseDir, 'build'), '--prefix',
os.path.join(proj.baseDir, 'InstallArea',
os.environ['BINARY_TAG'])
],
step='install',
**kwargs)
def build(self, proj, **kwargs):
config_result = self.configure(proj, **kwargs)
cmd = ['cmake', '--build', os.path.join(proj.baseDir, 'build')]
if 'jobs' in kwargs:
cmd.extend(['-j', str(kwargs['jobs'])])
if 'args' in kwargs:
cmd.append('--')
cmd.extend(
(arg if arg != '-k' else '-k0') for arg in kwargs['args'])
build_result = self._call(proj, cmd, step='build', **kwargs)
install_result = self.install(proj, **kwargs)
output = (
build_result.returncode, # use the build return code
''.join(
step.stdout
for step in (config_result, build_result, install_result)),
''.join(
step.stderr
for step in (config_result, build_result, install_result)),
config_result.started,
install_result.completed)
return BuildResults(proj, *output)
def clean(self, proj, **kwargs):
return self._call(
proj, [
'cmake', '--build',
os.path.join(proj.baseDir, 'build'), '--target', 'clean'
],
step='clean',
**kwargs)
def test(self, proj, **kwargs):
config_result = self.configure(proj, **kwargs)
cmd = ['ctest', '-T', 'test']
if 'jobs' in kwargs:
cmd.extend(['-j', str(kwargs['jobs'])])
test_result = self._call(
proj,
cmd,
step='test',
cwd=os.path.join(proj.baseDir, 'build'),
**kwargs)
output = (
test_result.returncode, # use the test return code
''.join(step.stdout for step in (config_result, test_result)),
''.join(step.stderr for step in (config_result, test_result)),
config_result.started,
test_result.completed)
return BuildResults(proj, *output)
def __str__(self):
return 'CMake (new)'
def is_new_cmake_style(project_dir):
'''
Check if the project uses the old or new CMake configuration style.
'''
top_config = os.path.join(project_dir, 'CMakeLists.txt')
if not os.path.exists(top_config):
# no CMakeLists.tst -> it's a CMT project
return False
with open(top_config) as f:
content = f.read()
# new style projects do not call "find_package(GaudiProject)"
return not bool(re.search(r'find_package\s*\(\s*GaudiProject', content))
class cmake(object):
'''
Dispatcher to use the old or new style CMake build procedure
depending on the project.
'''
def __init__(self):
self._proj_dir = None
self._impl_instance = None
def _impl(self, project):
'''
Return the correct implementation of CMake builder for the given project.
'''
if project.baseDir != self._proj_dir:
self._impl_instance = (cmake_new() if is_new_cmake_style(
project.baseDir) else cmake_old())
return self._impl_instance
def build(self, proj, **kwargs):
return self._impl(proj).build(proj, **kwargs)
def clean(self, proj, **kwargs):
return self._impl(proj).clean(proj, **kwargs)
def test(self, proj, **kwargs):
return self._impl(proj).test(proj, **kwargs)
def __str__(self):
'''
Conversion to string.
Loading