1#!/usr/bin/env python 2# coding=utf-8 3# Mathieu Courtois - EDF R&D, 2013 - http://www.code-aster.org 4 5""" 6When a project has a lot of options the 'waf configure' command line can be 7very long and it becomes a cause of error. 8This tool provides a convenient way to load a set of configuration parameters 9from a local file or from a remote url. 10 11The configuration parameters are stored in a Python file that is imported as 12an extra waf tool can be. 13 14Example: 15$ waf configure --use-config-dir=http://www.anywhere.org --use-config=myconf1 ... 16 17The file 'myconf1' will be downloaded from 'http://www.anywhere.org' 18(or 'http://www.anywhere.org/wafcfg'). 19If the files are available locally, it could be: 20$ waf configure --use-config-dir=/somewhere/myconfigurations --use-config=myconf1 ... 21 22The configuration of 'myconf1.py' is automatically loaded by calling 23its 'configure' function. In this example, it defines environment variables and 24set options: 25 26def configure(self): 27 self.env['CC'] = 'gcc-4.8' 28 self.env.append_value('LIBPATH', [...]) 29 self.options.perlbinary = '/usr/local/bin/perl' 30 self.options.pyc = False 31 32The corresponding command line should have been: 33$ CC=gcc-4.8 LIBPATH=... waf configure --nopyc --with-perl-binary=/usr/local/bin/perl 34 35 36This is an extra tool, not bundled with the default waf binary. 37To add the use_config tool to the waf file: 38$ ./waf-light --tools=use_config 39 40When using this tool, the wscript will look like: 41 42 def options(opt): 43 opt.load('use_config') 44 45 def configure(conf): 46 conf.load('use_config') 47""" 48 49import sys 50import os.path as osp 51import os 52 53local_repo = '' 54"""Local repository containing additional Waf tools (plugins)""" 55remote_repo = 'https://gitlab.com/ita1024/waf/raw/master/' 56""" 57Remote directory containing downloadable waf tools. The missing tools can be downloaded by using:: 58 59 $ waf configure --download 60""" 61 62remote_locs = ['waflib/extras', 'waflib/Tools'] 63""" 64Remote directories for use with :py:const:`waflib.extras.use_config.remote_repo` 65""" 66 67 68try: 69 from urllib import request 70except ImportError: 71 from urllib import urlopen 72else: 73 urlopen = request.urlopen 74 75 76from waflib import Errors, Context, Logs, Utils, Options, Configure 77 78try: 79 from urllib.parse import urlparse 80except ImportError: 81 from urlparse import urlparse 82 83 84 85 86DEFAULT_DIR = 'wafcfg' 87# add first the current wafcfg subdirectory 88sys.path.append(osp.abspath(DEFAULT_DIR)) 89 90def options(self): 91 group = self.add_option_group('configure options') 92 group.add_option('--download', dest='download', default=False, action='store_true', help='try to download the tools if missing') 93 94 group.add_option('--use-config', action='store', default=None, 95 metavar='CFG', dest='use_config', 96 help='force the configuration parameters by importing ' 97 'CFG.py. Several modules may be provided (comma ' 98 'separated).') 99 group.add_option('--use-config-dir', action='store', default=DEFAULT_DIR, 100 metavar='CFG_DIR', dest='use_config_dir', 101 help='path or url where to find the configuration file') 102 103def download_check(node): 104 """ 105 Hook to check for the tools which are downloaded. Replace with your function if necessary. 106 """ 107 pass 108 109 110def download_tool(tool, force=False, ctx=None): 111 """ 112 Download a Waf tool from the remote repository defined in :py:const:`waflib.extras.use_config.remote_repo`:: 113 114 $ waf configure --download 115 """ 116 for x in Utils.to_list(remote_repo): 117 for sub in Utils.to_list(remote_locs): 118 url = '/'.join((x, sub, tool + '.py')) 119 try: 120 web = urlopen(url) 121 try: 122 if web.getcode() != 200: 123 continue 124 except AttributeError: 125 pass 126 except Exception: 127 # on python3 urlopen throws an exception 128 # python 2.3 does not have getcode and throws an exception to fail 129 continue 130 else: 131 tmp = ctx.root.make_node(os.sep.join((Context.waf_dir, 'waflib', 'extras', tool + '.py'))) 132 tmp.write(web.read(), 'wb') 133 Logs.warn('Downloaded %s from %s', tool, url) 134 download_check(tmp) 135 try: 136 module = Context.load_tool(tool) 137 except Exception: 138 Logs.warn('The tool %s from %s is unusable', tool, url) 139 try: 140 tmp.delete() 141 except Exception: 142 pass 143 continue 144 return module 145 146 raise Errors.WafError('Could not load the Waf tool') 147 148def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True): 149 try: 150 module = Context.load_tool_default(tool, tooldir, ctx, with_sys_path) 151 except ImportError as e: 152 if not ctx or not hasattr(Options.options, 'download'): 153 Logs.error('Could not load %r during options phase (download unavailable at this point)' % tool) 154 raise 155 if Options.options.download: 156 module = download_tool(tool, ctx=ctx) 157 if not module: 158 ctx.fatal('Could not load the Waf tool %r or download a suitable replacement from the repository (sys.path %r)\n%s' % (tool, sys.path, e)) 159 else: 160 ctx.fatal('Could not load the Waf tool %r from %r (try the --download option?):\n%s' % (tool, sys.path, e)) 161 return module 162 163Context.load_tool_default = Context.load_tool 164Context.load_tool = load_tool 165Configure.download_tool = download_tool 166 167def configure(self): 168 opts = self.options 169 use_cfg = opts.use_config 170 if use_cfg is None: 171 return 172 url = urlparse(opts.use_config_dir) 173 kwargs = {} 174 if url.scheme: 175 kwargs['download'] = True 176 kwargs['remote_url'] = url.geturl() 177 # search first with the exact url, else try with +'/wafcfg' 178 kwargs['remote_locs'] = ['', DEFAULT_DIR] 179 tooldir = url.geturl() + ' ' + DEFAULT_DIR 180 for cfg in use_cfg.split(','): 181 Logs.pprint('NORMAL', "Searching configuration '%s'..." % cfg) 182 self.load(cfg, tooldir=tooldir, **kwargs) 183 self.start_msg('Checking for configuration') 184 self.end_msg(use_cfg) 185 186