Accept relative prefix/bindir/libdir paths from launch_dir

Additionally:
- Scripting.parse_options is back for compatibility reasons
- The help message should only be displayed when this is intended
- OptionsContext is responsible for the full initialization, so
  the framework should be usable without requiring Scripting.py
- Make it clear that Options.options is an optparse.Values object
- Get rid of the state in Options.options
This commit is contained in:
Thomas Nagy 2017-09-10 19:16:02 +02:00
parent 8b49da2fa7
commit be05b6ee8b
No known key found for this signature in database
GPG Key ID: 49B4C67C05277AAA
3 changed files with 90 additions and 93 deletions

View File

@ -197,17 +197,17 @@ class ConfigurationContext(Context.Context):
"""
if not env.PREFIX:
if Options.options.prefix or Utils.is_win32:
env.PREFIX = Utils.sane_path(Options.options.prefix)
env.PREFIX = Options.options.prefix
else:
env.PREFIX = ''
env.PREFIX = '/'
if not env.BINDIR:
if Options.options.bindir:
env.BINDIR = Utils.sane_path(Options.options.bindir)
env.BINDIR = Options.options.bindir
else:
env.BINDIR = Utils.subst_vars('${PREFIX}/bin', env)
if not env.LIBDIR:
if Options.options.libdir:
env.LIBDIR = Utils.sane_path(Options.options.libdir)
env.LIBDIR = Options.options.libdir
else:
env.LIBDIR = Utils.subst_vars('${PREFIX}/lib%s' % Utils.lib64(), env)

View File

@ -13,7 +13,7 @@ that reads the ``options`` wscript function.
import os, tempfile, optparse, sys, re
from waflib import Logs, Utils, Context, Errors
options = {}
options = optparse.Values()
"""
A global dictionary representing user-provided command-line options::
@ -43,14 +43,10 @@ class opt_parser(optparse.OptionParser):
Command-line options parser.
"""
def __init__(self, ctx, allow_unknown=False):
# Create a option parser without help function because we need to be able ignore '-h|--help'
# at configuration phase, once the main script found, we will add the help option to make sure
# we if help is needed it will containt also user defined options
optparse.OptionParser.__init__(self, conflict_handler="resolve", add_help_option=False,
optparse.OptionParser.__init__(self, conflict_handler='resolve', add_help_option=False,
version='waf %s (%s)' % (Context.WAFVERSION, Context.WAFREVISION))
self.formatter.width = Logs.get_term_cols()
self.ctx = ctx
# By default do not allow unknown options
self.allow_unknown = allow_unknown
def _process_args(self, largs, rargs, values):
@ -136,6 +132,7 @@ class OptionsContext(Context.Context):
p('-v', '--verbose', dest='verbose', default=0, action='count', help='verbosity level -v -vv or -vvv [default: 0]')
p('--zones', dest='zones', default='', action='store', help='debugging zones (task_gen, deps, tasks, etc)')
p('--profile', dest='profile', default='', action='store_true', help=optparse.SUPPRESS_HELP)
p('-h', '--help', dest='whelp', default=0, action='store_true', help="show this help message and exit")
gr = self.add_option_group('Configuration options')
self.option_groups['configure options'] = gr
@ -261,35 +258,50 @@ class OptionsContext(Context.Context):
return group
return None
def parse_basic_args(self, _args=None):
"""
Parse basic arguments defined by this module.
This can be called at the very beginning of the Waf initialitation
procedure to initialize pre-configuration commmand line driven options.
"""
try:
if self.parsed_basic_args:
return
except AttributeError:
self.parsed_basic_args = True
def sanitize_path(self, path, cwd=None):
if not cwd:
cwd = Context.launch_dir
p = os.path.expanduser(path)
p = os.path.join(cwd, p)
p = os.path.normpath(p)
p = os.path.abspath(p)
return p
# Allow unknow argument, as at this point the command line may contain
# custom-specified options the context still doesn't know.
self.parser.allow_unknown = True
global options
def parse_cmd_args(self, _args=None, cwd=None, allow_unknown=False):
"""
Just parse the arguments
"""
self.parser.allow_unknown = allow_unknown
(options, leftover_args) = self.parser.parse_args(args=_args)
envvars = []
commands = []
for arg in leftover_args:
if '=' in arg:
envvars.append(arg)
elif arg != 'options':
commands.append(arg)
if options.destdir:
options.destdir = Utils.sane_path(options.destdir)
for name in 'top out destdir prefix bindir libdir'.split():
# those paths are usually expanded from Context.launch_dir
if getattr(options, name, None):
path = self.sanitize_path(getattr(options, name), cwd)
setattr(options, name, path)
return options, commands, envvars
if options.top:
options.top = Utils.sane_path(options.top)
def init_module_vars(self, arg_options, arg_commands, arg_envvars):
options.__dict__.clear()
del commands[:]
del envvars[:]
if options.out:
options.out = Utils.sane_path(options.out)
options.__dict__.update(arg_options.__dict__)
commands.extend(arg_commands)
envvars.extend(arg_envvars)
Logs.verbose = options.verbose
for var in envvars:
(name, value) = var.split('=', 1)
os.environ[name.strip()] = value
def init_logs(self, options, commands, envvars):
if options.verbose >= 1:
self.load('errcheck')
@ -298,48 +310,25 @@ class OptionsContext(Context.Context):
if options.zones:
Logs.zones = options.zones.split(',')
if not Logs.verbose:
Logs.verbose = 1
if not Logs.verbose:
Logs.verbose = 1
elif Logs.verbose > 0:
Logs.zones = ['runner']
if Logs.verbose > 2:
Logs.zones = ['*']
def parse_args(self, _args=None):
"""
Parses arguments from a list which is not necessarily the command-line.
Initializes the module variables options, commands and envvars
If help is requested, prints it and exit the application
:param _args: arguments
:type _args: list of strings
"""
self.parse_basic_args()
# Make sure all specified arguments make sense
self.parser.allow_unknown = False
self.parser._add_help_option()
global options, commands, envvars
(custom_options, leftover_args) = self.parser.parse_args(args=_args)
# Update global options with custom ones
for currOptionName, currOptionValue in vars(custom_options).iteritems():
try:
getattr(options, currOptionName)
except AttributeError:
setattr(options, currOptionName, currOptionValue)
for arg in leftover_args:
if '=' in arg:
envvars.append(arg)
(name, value) = var.split('=', 1)
os.environ[name.strip()] = value
else:
commands.append(arg)
if not commands:
commands = ['build']
commands = [x for x in commands if x != 'options'] # issue 1076
options, commands, envvars = self.parse_cmd_args()
self.init_logs(options, commands, envvars)
self.init_module_vars(options, commands, envvars)
def execute(self):
"""
@ -348,3 +337,4 @@ class OptionsContext(Context.Context):
super(OptionsContext, self).execute()
self.parse_args()
Utils.alloc_process_pool(options.jobs)

View File

@ -32,42 +32,39 @@ def waf_entry_point(current_directory, version, wafdir):
# Store current directory before any chdir
Context.waf_dir = wafdir
Context.launch_dir = current_directory
Context.run_dir = Context.launch_dir = current_directory
start_dir = current_directory
no_climb = os.environ.get('NOCLIMB')
if len(sys.argv) > 1:
# os.path.join handles absolute paths in sys.argv[1] accordingly (it discards the previous ones)
# os.path.join handles absolute paths
# if sys.argv[1] is not an absolute path, then it is relative to the current working directory
potential_wscript = os.path.join(current_directory, sys.argv[1])
# maybe check if the file is executable
# perhaps extract 'wscript' as a constant
if os.path.basename(potential_wscript) == 'wscript' and os.path.isfile(potential_wscript):
if os.path.basename(potential_wscript) == Context.WSCRIPT_FILE and os.path.isfile(potential_wscript):
# need to explicitly normalize the path, as it may contain extra '/.'
# TODO abspath?
current_directory = os.path.normpath(os.path.dirname(potential_wscript))
path = os.path.normpath(os.path.dirname(potential_wscript))
start_dir = os.path.abspath(path)
no_climb = True
sys.argv.pop(1)
# Parse basic args to find out pre-configuration phase options
optsCtx = Context.create_context('options')
optsCtx.parse_basic_args()
ctx = Context.create_context('options')
(options, commands, env) = ctx.parse_cmd_args(allow_unknown=True)
if options.top:
Context.run_dir = Context.top_dir = options.top
if options.out:
Context.out_dir = options.out
# if 'configure' is in the commands, do not search any further
no_climb = os.environ.get('NOCLIMB')
if not no_climb:
for k in no_climb_commands:
for y in sys.argv:
for y in commands:
if y.startswith(k):
no_climb = True
break
# if --top is provided assume the build started in the top directory
if Options.options.top:
Context.run_dir = Context.top_dir = Options.options.top
if Options.options.out:
Context.out_dir = Options.options.out
# try to find a lock file (if the project was configured)
# at the same time, store the first wscript file seen
cur = current_directory
cur = start_dir
while cur and not Context.top_dir:
try:
lst = os.listdir(cur)
@ -123,9 +120,11 @@ def waf_entry_point(current_directory, version, wafdir):
break
if not Context.run_dir:
Logs.warn('No wscript file found: the help message may be incomplete')
optsCtx.parser.print_help()
Logs.error('Waf: Run from a directory containing a file named %r', Context.WSCRIPT_FILE)
if options.whelp:
Logs.warn('These are the generic options (no wscript/project found)')
ctx.parser.print_help()
sys.exit(0)
Logs.error('Waf: Run from a folder containing a %r file (or try -h for the generic options)', Context.WSCRIPT_FILE)
sys.exit(1)
try:
@ -145,14 +144,12 @@ def waf_entry_point(current_directory, version, wafdir):
traceback.print_exc(file=sys.stdout)
sys.exit(2)
if Options.options.profile:
if options.profile:
import cProfile, pstats
cProfile.runctx('from waflib import Scripting; Scripting.run_commands()', {}, {}, 'profi.txt')
p = pstats.Stats('profi.txt')
p.sort_stats('time').print_stats(75) # or 'cumulative'
else:
# Execute 'option' context to load custom defined commands
optsCtx.execute()
try:
run_commands()
except Errors.WafError as e:
@ -198,6 +195,19 @@ def set_main_module(file_path):
if not 'options' in Context.g_module.__dict__:
Context.g_module.options = Utils.nada
def parse_options():
"""
Parses the command-line options and initialize the logging system.
Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization.
"""
ctx = Context.create_context('options')
ctx.execute()
if not Options.commands:
Options.commands.append(default_cmd)
if Options.options.whelp:
ctx.parser.print_help()
sys.exit(0)
def run_command(cmd_name):
"""
Executes a single Waf command. Called by :py:func:`waflib.Scripting.run_commands`.
@ -222,6 +232,7 @@ def run_commands():
Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization, and executed
after :py:func:`waflib.Scripting.parse_options`.
"""
parse_options()
run_command('init')
while Options.commands:
cmd_name = Options.commands.pop(0)
@ -494,11 +505,7 @@ def dist(ctx):
pass
class DistCheck(Dist):
"""
Creates an archive of the project, then attempts to build the project in a temporary directory::
$ waf distcheck
"""
"""creates an archive with dist, then tries to build it"""
fun = 'distcheck'
cmd = 'distcheck'