From 7e2edad8efea55e8df1faa695d1389ef4e326d7c Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 16 Jul 2013 13:28:28 +0200 Subject: switch the build system to waf This commit adds a new build system based on waf. configure and Makefile are deprecated effective immediately and someday in the future they will be removed (they are still available by running ./old-configure). You can find how the choice for waf came to be in `DOCS/waf-buildsystem.rst`. TL;DR: we couldn't get the same level of abstraction and customization with other build systems we tried (CMake and autotools). For guidance on how to build the software now, take a look at README.md and the cross compilation guide. CREDITS: This is a squash of ~250 commits. Some of them are not by me, so here is the deserved attribution: - @wm4 contributed some Windows fixes, renamed configure to old-configure and contributed to the bootstrap script. Also, GNU/Linux testing. - @lachs0r contributed some Windows fixes and the bootstrap script. - @Nikoli contributed a lot of testing and discovered many bugs. - @CrimsonVoid contributed changes to the bootstrap script. --- waftools/dependencies.py | 210 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 waftools/dependencies.py (limited to 'waftools/dependencies.py') diff --git a/waftools/dependencies.py b/waftools/dependencies.py new file mode 100644 index 0000000000..cd85180fae --- /dev/null +++ b/waftools/dependencies.py @@ -0,0 +1,210 @@ +from waflib.Errors import ConfigurationError, WafError +from waflib.Configure import conf +from waflib.Build import BuildContext +from waflib.Logs import pprint +from inflectors import DependencyInflector + +class DependencyError(Exception): + pass + +class Dependency(object): + def __init__(self, ctx, known_deps, satisfied_deps, dependency): + self.ctx = ctx + self.known_deps = known_deps + self.satisfied_deps = satisfied_deps + self.identifier, self.desc = dependency['name'], dependency['desc'] + self.attributes = self.__parse_attributes__(dependency) + + ctx.env.known_deps.add(self.identifier) + for dep_key in ['deps', 'deps_any', 'deps_neg']: + if dep_key in self.attributes: + deps = self.attributes[dep_key] + self.ctx.ensure_dependency_is_known(*deps) + + + def __parse_attributes__(self, dependency): + if 'os_specific_checks' in dependency: + all_chks = dependency['os_specific_checks'] + chks = [check for check in all_chks if check in self.satisfied_deps] + if any(chks): + return all_chks[chks[0]] + return dependency + + def check(self): + self.ctx.start_msg('Checking for {0}'.format(self.desc)) + + try: + self.check_disabled() + self.check_any_dependencies() + self.check_dependencies() + self.check_negative_dependencies() + except DependencyError: + # No check was run, since the prerequisites of the dependency are + # not satisfied. Make sure the define is 'undefined' so that we + # get a `#define YYY 0` in `config.h`. + def_key = DependencyInflector(self.identifier).define_key() + self.ctx.undefine(def_key) + self.fatal_if_needed() + return + + self.check_autodetect_func() + + def check_disabled(self): + if self.enabled_option() == False: + self.skip() + raise DependencyError + + if self.enabled_option() == True: + self.attributes['req'] = True + self.attributes['fmsg'] = "You manually enabled the feature '{0}', but \ +the autodetection check failed.".format(self.identifier) + + def check_any_dependencies(self): + if 'deps_any' in self.attributes: + deps = set(self.attributes['deps_any']) + if len(deps & self.satisfied_deps) == 0: + self.skip("not found any of {0}".format(", ".join(deps))) + raise DependencyError + + def check_dependencies(self): + if 'deps' in self.attributes: + deps = set(self.attributes['deps']) + if not deps <= self.satisfied_deps: + missing_deps = deps - self.satisfied_deps + self.skip("{0} not found".format(", ".join(missing_deps))) + raise DependencyError + + def check_negative_dependencies(self): + if 'deps_neg' in self.attributes: + deps = set(self.attributes['deps_neg']) + if deps <= self.satisfied_deps: + conflicting_deps = deps & self.satisfied_deps + self.skip("{0} found".format(", ".join(conflicting_deps)), 'CYAN') + raise DependencyError + + def check_autodetect_func(self): + if self.attributes['func'](self.ctx, self.identifier): + self.success(self.identifier) + else: + self.fail() + self.fatal_if_needed() + + def enabled_option(self): + try: + return getattr(self.ctx.options, self.enabled_option_repr()) + except AttributeError: + pass + return None + + def enabled_option_repr(self): + return "enable_{0}".format(self.identifier) + + def success(self, depname): + self.ctx.mark_satisfied(depname) + self.ctx.end_msg(self.__message__('yes')) + + def fail(self, reason='no'): + self.ctx.end_msg(self.__message__(reason), 'RED') + + def fatal_if_needed(self): + if self.enabled_option() == False: + return + if self.attributes.get('req', False): + raise ConfigurationError(self.attributes['fmsg']) + + def skip(self, reason='disabled', color='YELLOW'): + self.ctx.end_msg(self.__message__(reason), color) + + def __message__(self, message): + optional_message = self.ctx.deps_msg.get(self.identifier) + if optional_message: + return "{0} ({1})".format(message, optional_message) + else: + return message + +def configure(ctx): + def __detect_target_os_dependency__(ctx): + target = "os-{0}".format(ctx.env.DEST_OS) + ctx.start_msg('Detected target OS:') + ctx.end_msg(target) + ctx.env.known_deps.add(target) + ctx.env.satisfied_deps.add(target) + + ctx.deps_msg = {} + ctx.env['known_deps'] = set() + ctx.env['satisfied_deps'] = set() + __detect_target_os_dependency__(ctx) + +@conf +def ensure_dependency_is_known(ctx, *depnames): + deps = set([d for d in depnames if not d.startswith('os-')]) + if not deps <= ctx.env.known_deps: + raise ConfigurationError( + "error in dependencies definition: some dependencies in" + " {0} are unknown.".format(deps)) + + +@conf +def mark_satisfied(ctx, dependency_identifier): + ctx.env.satisfied_deps.add(dependency_identifier) + +@conf +def add_optional_message(ctx, dependency_identifier, message): + ctx.deps_msg[dependency_identifier] = message + +@conf +def parse_dependencies(ctx, dependencies): + def __check_dependency__(ctx, dependency): + Dependency(ctx, + ctx.env.known_deps, + ctx.env.satisfied_deps, + dependency).check() + + [__check_dependency__(ctx, dependency) for dependency in dependencies] + +@conf +def dependency_satisfied(ctx, dependency_identifier): + ctx.ensure_dependency_is_known(dependency_identifier) + return dependency_identifier in ctx.env.satisfied_deps + +def filtered_sources(ctx, sources): + def __source_file__(source): + if isinstance(source, tuple): + return source[0] + else: + return source + + def __check_filter__(dependency): + if dependency.find('!') == 0: + dependency = dependency.lstrip('!') + ctx.ensure_dependency_is_known(dependency) + return dependency not in ctx.env.satisfied_deps + else: + ctx.ensure_dependency_is_known(dependency) + return dependency in ctx.env.satisfied_deps + + def __unpack_and_check_filter__(source): + try: + _, dependency = source + return __check_filter__(dependency) + except ValueError: + return True + + return [__source_file__(source) for source in sources \ + if __unpack_and_check_filter__(source)] + +def env_fetch(tx): + def fn(ctx): + deps = list(ctx.env.satisfied_deps) + lists = [ctx.env[tx(dep)] for dep in deps if (tx(dep) in ctx.env)] + return [item for sublist in lists for item in sublist] + return fn + +def dependencies_use(ctx): + return [DependencyInflector(dep).storage_key() for \ + dep in ctx.env.satisfied_deps] + +BuildContext.filtered_sources = filtered_sources +BuildContext.dependencies_use = dependencies_use +BuildContext.dependencies_includes = env_fetch(lambda x: "INCLUDES_{0}".format(x)) +BuildContext.dependency_satisfied = dependency_satisfied -- cgit v1.2.3