#!/usr/bin/env python # encoding: utf-8 # Copyright (c) 2019 a1batross ''' Subproject tool Helps you have standalone environment for each subproject(subdirectory) Usage: def options(opt): opt.load('subproject') def configure(conf): conf.add_subproject('folder1 folder2') def build(bld): bld.add_subproject('folder1 folder2') ''' from waflib import Configure, Logs, Options, Utils import os def opt(f): """ Decorator: attach new option functions to :py:class:`waflib.Options.OptionsContext`. :param f: method to bind :type f: function """ setattr(Options.OptionsContext, f.__name__, f) return f @opt def add_subproject(ctx, names): names_lst = Utils.to_list(names) for name in names_lst: if not os.path.isabs(name): # absolute paths only wscript_path = os.path.join(ctx.path.abspath(), name, 'wscript') else: wscript_path = os.path.join(name, 'wscript') if not os.path.isfile(wscript_path): # HACKHACK: this way we get warning message right in the help # so this just becomes more noticeable ctx.add_option_group('Cannot find wscript in ' + wscript_path + '. You probably missed submodule update') else: ctx.recurse(name) def options(opt): grp = opt.add_option_group('Subproject options') grp.add_option('-S', '--skip-subprojects', action='store', dest = 'SKIP_SUBDIRS', default=None, help = 'don\'t recurse into specified subprojects. Use only directory name.') def get_subproject_env(ctx, path, log=False): # remove top dir path path = str(path) if path.startswith(ctx.top_dir): if ctx.top_dir[-1] != os.pathsep: path = path[len(ctx.top_dir) + 1:] else: path = path[len(ctx.top_dir):] # iterate through possible subprojects names folders = os.path.normpath(path).split(os.sep) # print(folders) for i in range(1, len(folders)+1): name = folders[-i] # print(name) if name in ctx.all_envs: if log: Logs.pprint('YELLOW', 'env: changed to %s' % name) return ctx.all_envs[name] if log: Logs.pprint('YELLOW', 'env: changed to default env') raise IndexError('top env') @Configure.conf def add_subproject(ctx, dirs, prepend = None): ''' Recurse into subproject directory :param dirs: Directories we recurse into :type dirs: array or string :param prepend: Prepend virtual path, useful when managing projects with different environments :type prepend: string ''' if isinstance(ctx, Configure.ConfigurationContext): if not ctx.env.IGNORED_SUBDIRS and ctx.options.SKIP_SUBDIRS: ctx.env.IGNORED_SUBDIRS = ctx.options.SKIP_SUBDIRS.split(',') for prj in Utils.to_list(dirs): if ctx.env.SUBPROJECT_PATH: subprj_path = list(ctx.env.SUBPROJECT_PATH) else: subprj_path = [''] if prj in ctx.env.IGNORED_SUBDIRS: ctx.msg(msg='--X %s' % '/'.join(subprj_path), result='ignored', color='YELLOW') continue if prepend: subprj_path.append(prepend) subprj_path.append(prj) saveenv = ctx.env ctx.setenv('_'.join(subprj_path), ctx.env) # derive new env from previous ctx.env.ENVNAME = prj ctx.env.SUBPROJECT_PATH = list(subprj_path) ctx.msg(msg='--> %s' % '/'.join(subprj_path), result='in progress', color='BLUE') ctx.recurse(prj) ctx.msg(msg='<-- %s' % '/'.join(subprj_path), result='done', color='BLUE') ctx.setenv('') # save env changes ctx.env = saveenv # but use previous else: if not ctx.all_envs: ctx.load_envs() for prj in Utils.to_list(dirs): if prj in ctx.env.IGNORED_SUBDIRS: continue if ctx.env.SUBPROJECT_PATH: subprj_path = list(ctx.env.SUBPROJECT_PATH) else: subprj_path = [''] if prepend: subprj_path.append(prepend) subprj_path.append(prj) saveenv = ctx.env try: ctx.env = ctx.all_envs['_'.join(subprj_path)] except: ctx.fatal('Can\'t find env cache %s' % '_'.join(subprj_path)) ctx.recurse(prj) ctx.env = saveenv