def EXPAND_VARIABLES(ctx, varstr, vars=None): '''expand variables from a user supplied dictionary This is most useful when you pass vars=locals() to expand all your local variables in strings ''' if isinstance(varstr, list): ret = [] for s in varstr: ret.append(EXPAND_VARIABLES(ctx, s, vars=vars)) return ret if not isinstance(varstr, str): return varstr env = ConfigSet.ConfigSet() ret = varstr # substitute on user supplied dict if avaiilable if vars is not None: for v in vars.keys(): env[v] = vars[v] ret = SUBST_VARS_RECURSIVE(ret, env) # if anything left, subst on the environment as well if ret.find('${') != -1: ret = SUBST_VARS_RECURSIVE(ret, ctx.env) # make sure there is nothing left. Also check for the common # typo of $( instead of ${ if ret.find('${') != -1 or ret.find('$(') != -1: Logs.error('Failed to substitute all variables in varstr=%s' % ret) sys.exit(1) return ret
def load_envs(self): """ The configuration command creates files of the form ``build/c4che/NAMEcache.py``. This method creates a :py:class:`waflib.ConfigSet.ConfigSet` instance for each ``NAME`` by reading those files. The config sets are then stored in the dict :py:attr:`waflib.Build.BuildContext.allenvs`. """ try: lst = Utils.listdir(self.cache_dir) except OSError as e: if e.errno == errno.ENOENT: raise Errors.WafError( 'The project was not configured: run "waf configure" first!' ) else: raise if not lst: raise Errors.WafError( 'The cache directory is empty: reconfigure the project') for fname in lst: if fname.endswith(CACHE_SUFFIX): env = ConfigSet.ConfigSet(os.path.join(self.cache_dir, fname)) name = fname[:-len(CACHE_SUFFIX)] self.all_envs[name] = env for f in env[CFG_FILES]: newnode = self.root.find_resource(f) try: h = Utils.h_file(newnode.abspath()) except (IOError, AttributeError): Logs.error('cannot find %r' % f) h = Utils.SIG_NIL newnode.sig = h
def init_dirs(self): if not (os.path.isabs(self.top_dir) and os.path.isabs(self.out_dir)): raise Errors.WafError( 'The project was not configured: run "waf configure" first!') srcdir = None db = os.path.join(self.variant_dir, EXTRA_LOCK) env = ConfigSet.ConfigSet() try: env.load(db) srcdir = env.SRCDIR except: pass if srcdir: d = self.root.find_node(srcdir) if d and srcdir != self.top_dir and getattr(d, 'children', ''): srcnode = self.root.make_node(self.top_dir) print("relocating the source directory %r -> %r" % (srcdir, self.top_dir)) srcnode.children = {} for (k, v) in d.children.items(): srcnode.children[k] = v v.parent = srcnode d.children = {} old2(self)
def restore(self): """ Load the data from a previous run, sets the attributes listed in :py:const:`waflib.Build.SAVED_ATTRS` """ try: env = ConfigSet.ConfigSet(os.path.join(self.cache_dir, 'build.config.py')) except EnvironmentError: pass else: if env['version'] < Context.HEXVERSION: raise Errors.WafError('Version mismatch! reconfigure the project') for t in env['tools']: self.setup(**t) dbfn = os.path.join(self.variant_dir, Context.DBFILE) try: data = Utils.readf(dbfn, 'rb') except (IOError, EOFError): # handle missing file/empty file Logs.debug('build: Could not load the build cache %s (missing)' % dbfn) else: try: waflib.Node.pickle_lock.acquire() waflib.Node.Nod3 = self.node_class try: data = cPickle.loads(data) except Exception as e: Logs.debug('build: Could not pickle the build cache %s: %r' % (dbfn, e)) else: for x in SAVED_ATTRS: setattr(self, x, data[x]) finally: waflib.Node.pickle_lock.release() self.init_dirs()
def execute(self): if not Configure.autoconfig: return execute_method(self) env = ConfigSet.ConfigSet() do_config = False try: env.load(os.path.join(Context.top_dir, Options.lockfile)) except Exception: Logs.warn('Configuring the project') do_config = True else: if env.run_dir != Context.run_dir: do_config = True else: h = 0 for f in env['files']: h = Utils.h_list((h, Utils.readf(f, 'rb'))) do_config = h != env.hash if do_config: cmd = env['config_cmd'] or 'configure' if Configure.autoconfig == 'clobber': tmp = Options.options.__dict__ Options.options.__dict__ = env.options try: run_command(cmd) finally: Options.options.__dict__ = tmp else: run_command(cmd) run_command(self.cmd) else: return execute_method(self)
def __init__(self, *k, **kw): self.source = [] self.target = '' self.meths = [] self.features = [] self.tasks = [] if not 'bld' in kw: self.env = ConfigSet.ConfigSet() self.idx = 0 self.path = None else: self.bld = kw['bld'] self.env = self.bld.env.derive() self.path = self.bld.path path = self.path.abspath() try: self.idx = self.bld.idx[path] = self.bld.idx.get(path, 0) + 1 except AttributeError: self.bld.idx = {} self.idx = self.bld.idx[path] = 1 try: self.tg_idx_count = self.bld.tg_idx_count = self.bld.tg_idx_count + 1 except AttributeError: self.tg_idx_count = self.bld.tg_idx_count = 1 for key, val in kw.items(): setattr(self, key, val)
def execute(self): if not Configure.autoconfig: return execute_method(self) env = ConfigSet.ConfigSet() do_config = False if self.root.find_node(self.cache_dir) == None: do_config = True else: try: env.load(os.path.join(Context.lock_dir, Options.lockfile)) except Exception: Logs.warn('Configuring the project') do_config = True else: if env.run_dir != Context.run_dir: do_config = True else: h = 0 for f in env['files']: try: h = hash((h, Utils.readf(f, 'rb'))) except (IOError, EOFError): pass # ignore missing files (will cause a rerun cause of the changed hash) do_config = h != env.hash if do_config and 'configure' not in Options.commands: Options.commands.insert(0, self.cmd) Options.commands.insert(0, 'configure') self.skip_finish_message = True return return execute_method(self)
def LoadOptionsFromStorage(): # LOAD THE OPTIONS FROM STORAGE. # The values are preserved between commands using the file system. options_storage_path = 'build/c4che/options.py' option_values = ConfigSet.ConfigSet() try: option_values.load(options_storage_path) except: pass # LOAD THE VARIANT OPTION. variant_option_specified = (hasattr(Options.options, 'variant') and (Options.options.variant is not None)) if variant_option_specified: # The option is set by the user. variant = Options.options.variant else: # The option is set to the last known value or the static default. DEFAULT_VARIANT = 'default' variant = option_values.variant or DEFAULT_VARIANT # STORE THE OPTIONS IN THE CACHE. # The cache is created if it does not exist. option_values.variant = variant option_values.store(options_storage_path) # UPDATE THE OPTIONS IN MEMORY. Options.options.variant = variant
def load_envs(self): """ The configuration command creates files of the form ``build/c4che/NAMEcache.py``. This method creates a :py:class:`waflib.ConfigSet.ConfigSet` instance for each ``NAME`` by reading those files and stores them in :py:attr:`waflib.Build.BuildContext.allenvs`. """ node = self.root.find_node(self.cache_dir) if not node: raise Errors.WafError( 'The project was not configured: run "waf configure" first!') lst = node.ant_glob('**/*%s' % CACHE_SUFFIX, quiet=True) if not lst: raise Errors.WafError( 'The cache directory is empty: reconfigure the project') for x in lst: name = x.path_from(node).replace(CACHE_SUFFIX, '').replace('\\', '/') env = ConfigSet.ConfigSet(x.abspath()) self.all_envs[name] = env for f in env[CFG_FILES]: newnode = self.root.find_resource(f) if not newnode or not newnode.exists(): raise Errors.WafError( 'Missing configuration file %r, reconfigure the project!' % f)
def restore(self): try: env = ConfigSet.ConfigSet( os.path.join(self.cache_dir, 'build.config.py')) except (IOError, OSError): pass else: if env['version'] < Context.HEXVERSION: raise Errors.WafError( 'Version mismatch! reconfigure the project') for t in env['tools']: self.setup(**t) f = None try: try: f = open(os.path.join(self.variant_dir, Context.DBFILE), 'rb') except (IOError, EOFError): Logs.debug('build: could not load the build cache (missing)') else: try: waflib.Node.pickle_lock.acquire() waflib.Node.Nod3 = self.node_class try: data = cPickle.load(f) except Exception, e: Logs.debug('build: could not load the build cache %r' % e) else: for x in SAVED_ATTRS: setattr(self, x, data[x])
def distclean(ctx): '''removes the build directory''' lst = os.listdir('.') for f in lst: if f == Options.lockfile: try: proj = ConfigSet.ConfigSet(f) except IOError: Logs.warn('Could not read %r' % f) continue if proj['out_dir'] != proj['top_dir']: try: shutil.rmtree(proj['out_dir']) except IOError: pass except OSError as e: if e.errno != errno.ENOENT: Logs.warn('project %r cannot be removed' % proj[Context.OUT]) else: distclean_dir(proj['out_dir']) for k in (proj['out_dir'], proj['top_dir'], proj['run_dir']): try: os.remove(os.path.join(k, Options.lockfile)) except OSError as e: if e.errno != errno.ENOENT: Logs.warn('file %r cannot be removed' % f) # remove the local waf cache if f.startswith('.waf') and not Options.commands: shutil.rmtree(f, ignore_errors=True)
def update_review_set(self, old_set): """ Merge the options passed on the command line with those imported from the previous review set and return the corresponding preview set. """ # Convert value to string. It's important that 'None' maps to # the empty string. def val_to_str(val): if val == None or val == '': return '' return str(val) new_set = ConfigSet.ConfigSet() opt_dict = Options.options.__dict__ for name in review_options.keys(): # the option is specified explicitly on the command line if name in opt_dict: # if the option is the default, pretend it was never specified if val_to_str(opt_dict[name]) != val_to_str( review_defaults[name]): new_set[name] = opt_dict[name] # the option was explicitly specified in a previous command elif name in old_set: new_set[name] = old_set[name] return new_set
def execute(self): if not Configure.autoconfig: return execute_method(self) env=ConfigSet.ConfigSet() do_config=False try: env.load(os.path.join(Context.top_dir,Options.lockfile)) except Exception: Logs.warn('Configuring the project') do_config=True else: if env.run_dir!=Context.run_dir: do_config=True else: h=0 for f in env['files']: h=Utils.h_list((h,Utils.readf(f,'rb'))) do_config=h!=env.hash if do_config: Options.commands.insert(0,self.cmd) Options.commands.insert(0,'configure') if Configure.autoconfig=='clobber': Options.options.__dict__=env.options return return execute_method(self)
def fake_build_environment(info=True, flush=False): """create all the tasks for the project, but do not run the build return the build context in use""" bld = getattr(Context.g_module, 'build_context', Utils.Context)() bld = Scripting.check_configured(bld) Options.commands['install'] = False Options.commands['uninstall'] = False bld.is_install = 0 # False try: proj = ConfigSet.ConfigSet(Options.lockfile) except IOError: raise Errors.WafError( "Project not configured (run './configure' first)") bld.load_envs() if info: Logs.info("Waf: Entering directory `%s'" % bld.bldnode.abspath()) bld.add_subdirs([os.path.split(Context.g_module.root_path)[0]]) bld.pre_build() if flush: bld.flush() return bld
def load_envs(self): """ The configuration command creates files of the form ``build/c4che/NAMEcache.py``. This method creates a :py:class:`waflib.ConfigSet.ConfigSet` instance for each ``NAME`` by reading those files. The config sets are then stored in the dict :py:attr:`waflib.Build.BuildContext.allenvs`. """ node = self.root.find_node(self.cache_dir) if not node: raise Errors.WafError( 'The project was not configured: run "waf configure" first!') lst = node.ant_glob('**/*%s' % CACHE_SUFFIX, quiet=True) if not lst: raise Errors.WafError( 'The cache directory is empty: reconfigure the project') for x in lst: name = x.path_from(node).replace(CACHE_SUFFIX, '').replace('\\', '/') env = ConfigSet.ConfigSet(x.abspath()) self.all_envs[name] = env for f in env[CFG_FILES]: newnode = self.root.find_resource(f) try: h = Utils.h_file(newnode.abspath()) except (IOError, AttributeError): Logs.error('cannot find %r' % f) h = Utils.SIG_NIL newnode.sig = h
def distclean(ctx): '''removes the build directory''' lst=os.listdir('.') for f in lst: if f==Options.lockfile: try: proj=ConfigSet.ConfigSet(f) except IOError: Logs.warn('Could not read %r'%f) continue if proj['out_dir']!=proj['top_dir']: try: shutil.rmtree(proj['out_dir']) except IOError: pass except OSError ,e: if e.errno!=errno.ENOENT: Logs.warn('Could not remove %r'%proj['out_dir']) else: distclean_dir(proj['out_dir']) for k in(proj['out_dir'],proj['top_dir'],proj['run_dir']): p=os.path.join(k,Options.lockfile) try: os.remove(p) except OSError ,e: if e.errno!=errno.ENOENT: Logs.warn('Could not remove %r'%p)
def restore(self): try: env=ConfigSet.ConfigSet(os.path.join(self.cache_dir,'build.config.py')) except EnvironmentError: pass else: if env.version<Context.HEXVERSION: raise Errors.WafError('Project was configured with a different version of Waf, please reconfigure it') for t in env.tools: self.setup(**t) dbfn=os.path.join(self.variant_dir,Context.DBFILE) try: data=Utils.readf(dbfn,'rb') except(EnvironmentError,EOFError): Logs.debug('build: Could not load the build cache %s (missing)',dbfn) else: try: Node.pickle_lock.acquire() Node.Nod3=self.node_class try: data=cPickle.loads(data) except Exception as e: Logs.debug('build: Could not pickle the build cache %s: %r',dbfn,e) else: for x in SAVED_ATTRS: setattr(self,x,data.get(x,{})) finally: Node.pickle_lock.release() self.init_dirs()
def setenv(self, name, env=None): """ Set a new config set for conf.env. If a config set of that name already exists, recall it without modification. The name is the filename prefix to save to ``c4che/NAME_cache.py``, and it is also used as *variants* by the build commands. Though related to variants, whatever kind of data may be stored in the config set:: def configure(cfg): cfg.env.ONE = 1 cfg.setenv('foo') cfg.env.ONE = 2 def build(bld): 2 == bld.env_of_name('foo').ONE :param name: name of the configuration set :type name: string :param env: ConfigSet to copy, or an empty ConfigSet is created :type env: :py:class:`waflib.ConfigSet.ConfigSet` """ if name not in self.all_envs or env: if not env: env = ConfigSet.ConfigSet() self.prepare_env(env) else: env = env.derive() self.all_envs[name] = env self.variant = name
def __init__(self, **kwargs): self.line_just = 45 if hasattr(Context.g_module, 'line_just'): self.line_just = Context.g_module.line_just super(ConfigureContext, self).__init__(**kwargs) self.run_env = ConfigSet.ConfigSet()
def distclean(ctx): '''removes the build directory''' lst = os.listdir('.') for f in lst: if f == Options.lockfile: try: proj = ConfigSet.ConfigSet(f) except IOError: Logs.warn('Could not read %r', f) continue if proj['out_dir'] != proj['top_dir']: try: shutil.rmtree(proj['out_dir']) except EnvironmentError as e: if e.errno != errno.ENOENT: Logs.warn('Could not remove %r', proj['out_dir']) else: distclean_dir(proj['out_dir']) for k in (proj['out_dir'], proj['top_dir'], proj['run_dir']): p = os.path.join(k, Options.lockfile) try: os.remove(p) except OSError as e: if e.errno != errno.ENOENT: Logs.warn('Could not remove %r', p) # remove local waf cache folders if not Options.commands: for x in '.waf-1. waf-1. .waf3-1. waf3-1.'.split(): if f.startswith(x): shutil.rmtree(f, ignore_errors=True)
def restore(self): try: env=ConfigSet.ConfigSet(os.path.join(self.cache_dir,'build.config.py')) except(IOError,OSError): pass else: if env['version']<Context.HEXVERSION: raise Errors.WafError('Version mismatch! reconfigure the project') for t in env['tools']: self.setup(**t) dbfn=os.path.join(self.variant_dir,Context.DBFILE) try: data=Utils.readf(dbfn,'rb') except(IOError,EOFError): Logs.debug('build: Could not load the build cache %s (missing)'%dbfn) else: try: waflib.Node.pickle_lock.acquire() waflib.Node.Nod3=self.node_class try: data=cPickle.loads(data) except Exception ,e: Logs.debug('build: Could not pickle the build cache %s: %r'%(dbfn,e)) else: for x in SAVED_ATTRS:
def execute(self): """ See :py:func:`waflib.Context.Context.execute` """ self.init_dirs() self.cachedir = self.bldnode.make_node(Build.CACHE_DIR) self.cachedir.mkdir() path = os.path.join(self.bldnode.abspath(), WAF_CONFIG_LOG) self.logger = Logs.make_logger(path, 'cfg') app = getattr(Context.g_module, 'APPNAME', '') if app: ver = getattr(Context.g_module, 'VERSION', '') if ver: app = "%s (%s)" % (app, ver) params = {'now': time.ctime(), 'pyver': sys.hexversion, 'systype': sys.platform, 'args': " ".join(sys.argv), 'wafver': Context.WAFVERSION, 'abi': Context.ABI, 'app': app} self.to_log(conf_template % params) self.msg('Setting top to', self.srcnode.abspath()) self.msg('Setting out to', self.bldnode.abspath()) if id(self.srcnode) == id(self.bldnode): Logs.warn('Setting top == out') elif id(self.path) != id(self.srcnode): if self.srcnode.is_child_of(self.path): Logs.warn('Are you certain that you do not want to set top="." ?') super(ConfigurationContext, self).execute() self.store() Context.top_dir = self.srcnode.abspath() Context.out_dir = self.bldnode.abspath() # this will write a configure lock so that subsequent builds will # consider the current path as the root directory (see prepare_impl). # to remove: use 'waf distclean' env = ConfigSet.ConfigSet() env.argv = sys.argv env.options = Options.options.__dict__ env.config_cmd = self.cmd env.run_dir = Context.run_dir env.top_dir = Context.top_dir env.out_dir = Context.out_dir # conf.hash & conf.files hold wscript files paths and hash # (used only by Configure.autoconfig) env.hash = self.hash env.files = self.files env.environ = dict(self.environ) if not self.env.NO_LOCK_IN_RUN and not getattr(Options.options, 'no_lock_in_run'): env.store(os.path.join(Context.run_dir, Options.lockfile)) if not self.env.NO_LOCK_IN_TOP and not getattr(Options.options, 'no_lock_in_top'): env.store(os.path.join(Context.top_dir, Options.lockfile)) if not self.env.NO_LOCK_IN_OUT and not getattr(Options.options, 'no_lock_in_out'): env.store(os.path.join(Context.out_dir, Options.lockfile))
def setenv(self, name, env=None): if not env: env = ConfigSet.ConfigSet() self.prepare_env(env) else: env = env.derive() self.all_envs[name] = env self.variant = name
def __init__(self, *k, **kw): """ Task generator objects predefine various attributes (source, target) for possible processing by process_rule (make-like rules) or process_source (extensions, misc methods) Tasks are stored on the attribute 'tasks'. They are created by calling methods listed in ``self.meths`` or referenced in the attribute ``features`` A topological sort is performed to execute the methods in correct order. The extra key/value elements passed in ``kw`` are set as attributes """ self.source = [] self.target = '' self.meths = [] """ List of method names to execute (internal) """ self.features = [] """ List of feature names for bringing new methods in """ self.tasks = [] """ Tasks created are added to this list """ if not 'bld' in kw: # task generators without a build context :-/ self.env = ConfigSet.ConfigSet() self.idx = 0 self.path = None else: self.bld = kw['bld'] self.env = self.bld.env.derive() self.path = kw.get( 'path', self.bld.path ) # by default, emulate chdir when reading scripts # Provide a unique index per folder # This is part of a measure to prevent output file name collisions path = self.path.abspath() try: self.idx = self.bld.idx[path] = self.bld.idx.get(path, 0) + 1 except AttributeError: self.bld.idx = {} self.idx = self.bld.idx[path] = 1 # Record the global task generator count try: self.tg_idx_count = self.bld.tg_idx_count = self.bld.tg_idx_count + 1 except AttributeError: self.tg_idx_count = self.bld.tg_idx_count = 1 for key, val in kw.items(): setattr(self, key, val)
def LOAD_ENVIRONMENT(): '''load the configuration environment, allowing access to env vars from new commands''' env = ConfigSet.ConfigSet() try: p = os.path.join(Context.g_module.out, 'c4che/default'+CACHE_SUFFIX) env.load(p) except (OSError, IOError): pass return env
def restore(self): self.top_dir = os.path.abspath(Context.g_module.top) self.srcnode = self.root.find_node(self.top_dir) self.path = self.srcnode self.out_dir = os.path.join(self.top_dir, Context.g_module.out) self.bldnode = self.root.make_node(self.out_dir) self.bldnode.mkdir() self.env = ConfigSet.ConfigSet()
def retrieve(self, name, fromenv=None): try: env = self.all_envs[name] except KeyError: env = ConfigSet.ConfigSet() self.prepare_env(env) self.all_envs[name] = env else: if fromenv: Logs.warn("The environment %s may have been configured already" % name) return env
def execute(self): if not Configure.autoconfig: return execute_method(self) env=ConfigSet.ConfigSet() do_config=False try: env.load(os.path.join(Context.top_dir,Options.lockfile)) except Exception ,e: Logs.warn('Configuring the project') do_config=True
def distclean(ctx): '''removes build folders and data''' def remove_and_log(k, fun): try: fun(k) except EnvironmentError as e: if e.errno != errno.ENOENT: Logs.warn('Could not remove %r', k) # remove waf cache folders on the top-level if not Options.commands: for k in os.listdir('.'): for x in '.waf-2 waf-2 .waf3-2 waf3-2'.split(): if k.startswith(x): remove_and_log(k, shutil.rmtree) # remove a build folder, if any cur = '.' if os.environ.get('NO_LOCK_IN_TOP') or ctx.options.no_lock_in_top: cur = ctx.options.out try: lst = os.listdir(cur) except OSError: Logs.warn('Could not read %r', cur) return if Options.lockfile in lst: f = os.path.join(cur, Options.lockfile) try: env = ConfigSet.ConfigSet(f) except EnvironmentError: Logs.warn('Could not read %r', f) return if not env.out_dir or not env.top_dir: Logs.warn('Invalid lock file %r', f) return if env.out_dir == env.top_dir: distclean_dir(env.out_dir) else: remove_and_log(env.out_dir, shutil.rmtree) env_dirs = [env.out_dir] if not (os.environ.get('NO_LOCK_IN_TOP') or ctx.options.no_lock_in_top): env_dirs.append(env.top_dir) if not (os.environ.get('NO_LOCK_IN_RUN') or ctx.options.no_lock_in_run): env_dirs.append(env.run_dir) for k in env_dirs: p = os.path.join(k, Options.lockfile) remove_and_log(p, os.remove)
def __init__(self, *k, **kw): """ The task generator objects predefine various attributes (source, target) for possible processing by process_rule (make-like rules) or process_source (extensions, misc methods) The tasks are stored on the attribute 'tasks'. They are created by calling methods listed in self.meths *or* referenced in the attribute features A topological sort is performed to ease the method re-use. The extra key/value elements passed in kw are set as attributes """ # so we will have to play with directed acyclic graphs # detect cycles, etc self.source = '' self.target = '' self.meths = [] """ List of method names to execute (it is usually a good idea to avoid touching this) """ self.prec = Utils.defaultdict(list) """ Precedence table for sorting the methods in self.meths """ self.mappings = {} """ List of mappings {extension -> function} for processing files by extension """ self.features = [] """ List of feature names for bringing new methods in """ self.tasks = [] """ List of tasks created. """ if not 'bld' in kw: # task generators without a build context :-/ self.env = ConfigSet.ConfigSet() self.idx = 0 self.path = None else: self.bld = kw['bld'] self.env = self.bld.env.derive() self.path = self.bld.path # emulate chdir when reading scripts for key, val in kw.items(): setattr(self, key, val)