waf: cmake: use CMakeConfig class instead of taskgen

That will make it easier to allow reconfiguration of cmake builds on a single
waf build run.
This commit is contained in:
Gustavo Jose de Sousa 2016-05-24 18:11:11 -03:00 committed by Lucas De Marchi
parent fe0c49ec54
commit b6a5052a97
3 changed files with 107 additions and 98 deletions

View File

@ -17,17 +17,14 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Waf tool for external builds with cmake. This tool defines two features:
- cmake_configure: for defining task generators that do cmake build
configuration, i.e., build system generation.
- cmake_build: for actual build through the cmake interface.
Waf tool for external builds with cmake. This tool defines the feature
'cmake_build', for building through the cmake interface.
Example::
def build(bld):
# cmake configuration
foo = bld(
features='cmake_configure',
foo = bld.cmake(
name='foo',
cmake_src='path/to/foosrc', # where is the source tree
cmake_bld='path/to/foobld', # where to generate the build system
@ -37,7 +34,7 @@ Example::
),
)
# cmake build for target (cmake target) 'bar'
# cmake build for external target 'bar'
bld(
features='cmake_build',
cmake_config='foo', # this build depends on the cmake generation above defined
@ -45,7 +42,7 @@ Example::
)
# cmake build for target 'baz' (syntactic sugar)
foo.cmake_build('baz')
foo.build('baz')
The keys of cmake_vars are sorted so that unnecessary execution is avoided. If
you want to ensure an order in which the variables are passed to cmake, use an
@ -58,8 +55,7 @@ OrderedDict. Example::
foo_vars['BAR'] = 'value_of_bar'
# cmake configuration
foo = bld(
features='cmake_configure',
foo = bld.cmake(
cmake_vars=foo_vars,
...
)
@ -73,14 +69,14 @@ cmake_build task generator. Example::
...
# declaring on target only what I'm interested in
foo.cmake_build('baz', target='path/to/foobld/include/baz.h')
foo.build('baz', target='path/to/foobld/include/baz.h')
# myprogram.c includes baz.h, so the dependency is (implicitly)
# established
bld.program(target='myprogram', source='myprogram.c')
# another example
foo.cmake_build('another', target='another.txt')
foo.build('another', target='another.txt')
bld(
rule='${CP} ${SRC} ${TGT}',
@ -116,12 +112,13 @@ build task, so that they get a signature. Example::
def build(bld):
...
foo.cmake_build('baz', cmake_output_patterns='include/*.h')
foo.build('baz', cmake_output_patterns='include/*.h')
...
"""
from waflib import Node, Task, Utils
from waflib.Configure import conf
from waflib.TaskGen import feature, taskgen_method
from collections import OrderedDict
@ -150,7 +147,7 @@ class cmake_configure_task(Task.Task):
return self.uid_
def __str__(self):
return self.generator.name
return self.cmake.name
def keyword(self):
return 'CMake Configure'
@ -185,9 +182,7 @@ class cmake_build_task(Task.Task):
return self.uid_
def __str__(self):
config_name = self.config_taskgen.name
target = self.cmake_target
return '%s %s' % (config_name, target)
return '%s %s' % (self.cmake.name, self.cmake_target)
def keyword(self):
return 'CMake Build'
@ -201,7 +196,7 @@ def _cmake_build_task_post_run(self):
self.output_patterns = Utils.to_list(self.output_patterns)
if not self.output_patterns:
return self.original_post_run()
bldnode = self.config_taskgen.cmake_bld
bldnode = self.cmake.bldnode
for node in bldnode.ant_glob(self.output_patterns, remove=False):
self.set_outputs(node)
return self.original_post_run()
@ -211,35 +206,82 @@ cmake_build_task.post_run = _cmake_build_task_post_run
# dependencies
cmake_build_task = Task.always_run(cmake_build_task)
@feature('cmake_configure')
def process_cmake_configure(self):
if not hasattr(self, 'name'):
self.bld.fatal('cmake_configure: taskgen is missing name')
if not hasattr(self, 'cmake_src'):
self.bld.fatal('cmake_configure: taskgen is missing cmake_src')
class CMakeConfig(object):
'''
CMake configuration. This object shouldn't be instantiated directly. Use
bld.cmake().
'''
def __init__(self, bld, name, srcnode, bldnode, cmake_vars):
self.bld = bld
self.name = name
self.srcnode = srcnode
self.bldnode = bldnode
self.vars = cmake_vars
if not isinstance(self.cmake_src, Node.Node):
self.cmake_src = self.bld.path.find_dir(self.cmake_src)
self._config_task = None
self.last_build_task = None
self.get_cmake_bldnode()
self.cmake_bld.mkdir()
def config_task(self, taskgen):
if self._config_task:
return self._config_task
self.last_build_task = None
# NOTE: we'll probably need to use the full class name in waf 1.9
self._config_task = taskgen.create_task('cmake_configure')
self._config_task.cwd = self.bldnode.abspath()
self._config_task.cmake = self
self.cmake_vars = getattr(self, 'cmake_vars', {})
env = self._config_task.env
env.CMAKE_BLD_DIR = self.bldnode.abspath()
env.CMAKE_SRC_DIR = self.srcnode.abspath()
# NOTE: we'll probably need to use the full class name in waf 1.9
tsk = self.cmake_config_task = self.create_task('cmake_configure')
tsk.cwd = self.cmake_bld.abspath()
tsk.env.CMAKE_BLD_DIR = self.cmake_bld.abspath()
tsk.env.CMAKE_SRC_DIR = self.cmake_src.abspath()
keys = list(self.vars.keys())
if not isinstance(self.vars, OrderedDict):
keys.sort()
env.CMAKE_VARS = ["-D%s='%s'" % (k, self.vars[k]) for k in keys]
keys = list(self.cmake_vars.keys())
if not isinstance(self.cmake_vars, OrderedDict):
keys.sort()
tsk.env.CMAKE_VARS = ["-D%s='%s'" % (k, self.cmake_vars[k]) for k in keys]
self._config_task.set_outputs(
self.bldnode.find_or_declare('CMakeCache.txt'),
)
tsk.set_outputs(self.cmake_bld.find_or_declare('CMakeCache.txt'))
self.bldnode.mkdir()
return self._config_task
def build(self, cmake_target, **kw):
return self.bld.cmake_build(self.name, cmake_target, **kw)
_cmake_instances = {}
def get_cmake(name):
if name not in _cmake_instances:
raise Exception('cmake: configuration named "%s" not found' % name)
return _cmake_instances[name]
@conf
def cmake(bld, name, cmake_src=None, cmake_bld=None, cmake_vars={}):
'''
This function has two signatures:
- bld.cmake(name, cmake_src, cmake_bld, cmake_vars):
Create a cmake configuration.
- bld.cmake(name):
Get the cmake configuration with name.
'''
if not cmake_src and not cmake_bld and not cmake_vars:
return get_cmake(name)
if name in _cmake_instances:
bld.fatal('cmake: configuration named "%s" already exists' % name)
if not isinstance(cmake_src, Node.Node):
cmake_src = bld.path.find_dir(cmake_src)
if not cmake_bld:
cmake_bld = cmake_src.get_bld()
elif not isinstance(cmake_bld, Node.Node):
cmake_bld = bld.bldnode.make_node(cmake_bld)
c = CMakeConfig(bld, name, cmake_src, cmake_bld, cmake_vars)
_cmake_instances[name] = c
return c
@feature('cmake_build')
def process_cmake_build(self):
@ -248,11 +290,6 @@ def process_cmake_build(self):
if not hasattr(self, 'cmake_config'):
self.bld.fatal('cmake_build: taskgen is missing cmake_config')
self.config_taskgen = self.bld.get_tgen_by_name(self.cmake_config)
if not getattr(self.config_taskgen, 'posted', False):
self.config_taskgen.post()
tsk = self.create_cmake_build_task(self.cmake_config, self.cmake_target)
self.cmake_build_task = tsk
@ -267,59 +304,35 @@ def process_cmake_build(self):
tsk.output_patterns = getattr(self, 'cmake_output_patterns', [])
def _get_config_tg(self, cmake_config):
if not cmake_config:
if 'cmake_configure' in self.features:
cmake_config = self.name
elif 'cmake_build' in self.features:
if hasattr(self, 'cmake_config'):
cmake_config = self.cmake_config
if not cmake_config:
self.bld.fatal('cmake: cmake_config is missing or invalid')
return self.bld.get_tgen_by_name(cmake_config)
@taskgen_method
def cmake_build(self, cmake_target, cmake_config=None, **kw):
tg = _get_config_tg(self, cmake_config)
kw['cmake_config'] = tg.name
@conf
def cmake_build(bld, cmake_config, cmake_target, **kw):
kw['cmake_config'] = cmake_config
kw['cmake_target'] = cmake_target
kw['features'] = Utils.to_list(kw.get('features', [])) + ['cmake_build']
if 'name' not in kw:
kw['name'] = '%s_%s' % (tg.name, cmake_target)
kw['name'] = '%s_%s' % (cmake_config, cmake_target)
kw['features'] = Utils.to_list(kw.get('features', []))
kw['features'].append('cmake_build')
return self.bld(**kw)
@taskgen_method
def get_cmake_bldnode(self, cmake_config=None):
tg = _get_config_tg(self, cmake_config)
if not hasattr(tg, 'cmake_bld'):
tg.cmake_bld = tg.cmake_src.get_bld()
elif not isinstance(tg.cmake_bld, Node.Node):
tg.cmake_bld = tg.bld.bldnode.make_node(tg.cmake_bld)
return tg.cmake_bld
return bld(**kw)
@taskgen_method
def create_cmake_build_task(self, cmake_config, cmake_target):
cmake = get_cmake(cmake_config)
# NOTE: we'll probably need to use the full class name in waf 1.9
tsk = self.create_task('cmake_build')
config_tg = self.bld.get_tgen_by_name(cmake_config)
tsk.config_taskgen = config_tg
tsk.cmake = cmake
tsk.cmake_target = cmake_target
tsk.output_patterns = []
tsk.env.CMAKE_BLD_DIR = config_tg.cmake_bld.abspath()
tsk.env.CMAKE_BLD_DIR = cmake.bldnode.abspath()
tsk.env.CMAKE_TARGET = cmake_target
tsk.set_run_after(config_tg.cmake_config_task)
if config_tg.last_build_task:
tsk.set_run_after(config_tg.last_build_task)
config_tg.last_build_task = tsk
self.cmake_config_task = cmake.config_task(self)
tsk.set_run_after(self.cmake_config_task)
if cmake.last_build_task:
tsk.set_run_after(cmake.last_build_task)
cmake.last_build_task = tsk
return tsk

View File

@ -40,15 +40,14 @@ def configure(cfg):
def libbenchmark(bld):
prefix_node = bld.bldnode.make_node(bld.env.GBENCHMARK_PREFIX_REL)
gbenchmark_cmake = bld(
features='cmake_configure',
gbenchmark = bld.cmake(
name='gbenchmark',
cmake_src='modules/gbenchmark',
cmake_bld='gbenchmark_build',
name='gbenchmark',
cmake_vars=dict(
CMAKE_BUILD_TYPE='Release',
CMAKE_INSTALL_PREFIX=prefix_node.abspath(),
)
),
)
prefix_node = bld.bldnode.make_node(bld.env.GBENCHMARK_PREFIX_REL)
@ -60,8 +59,7 @@ def libbenchmark(bld):
'include/benchmark/reporter.h',
)
outputs = [prefix_node.make_node(path) for path in output_paths]
gbenchmark_cmake.cmake_build('install', target=outputs)
gbenchmark.build('install', target=outputs)
@feature('gbenchmark')
@before_method('process_use')

View File

@ -132,7 +132,7 @@ def px4_firmware(self):
if self.env.PX4_USE_PX4IO and not _cp_px4io:
px4io_task = self.create_cmake_build_task('px4', 'fw_io')
px4io = px4io_task.config_taskgen.get_cmake_bldnode().make_node(
px4io = px4io_task.cmake.bldnode.make_node(
'src/modules/px4iofirmware/px4io-v%s.bin' % version,
)
px4io_task.set_outputs(px4io)
@ -159,7 +159,7 @@ def px4_firmware(self):
if self.env.PX4_USE_PX4IO and _cp_px4io.generator is self:
fw_task.set_run_after(_cp_px4io)
firmware = fw_task.config_taskgen.cmake_bld.make_node(
firmware = fw_task.cmake.bldnode.make_node(
'src/firmware/nuttx/nuttx-px4fmu-v%s-apm.px4' % version,
)
_update_firmware_sig(fw_task, firmware)
@ -285,20 +285,18 @@ def configure(cfg):
def build(bld):
version = bld.env.get_flat('PX4_VERSION')
px4 = bld(
features='cmake_configure',
px4 = bld.cmake(
name='px4',
cmake_src=bld.srcnode.find_dir('modules/PX4Firmware'),
cmake_vars=bld.env.PX4_CMAKE_VARS,
group='dynamic_sources',
)
px4.cmake_build(
px4.build(
'msg_gen',
group='dynamic_sources',
cmake_output_patterns='src/modules/uORB/topics/*.h',
)
px4.cmake_build(
px4.build(
'prebuild_targets',
group='dynamic_sources',
cmake_output_patterns='px4fmu-v%s/NuttX/nuttx-export/**/*.h' % version,