The Meson Build System http://mesonbuild.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
7.3 KiB

# Copyright 2012-2016 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import os.path
import importlib
import traceback
import argparse
from . import mesonlib
from . import mlog
from . import mconf, minit, minstall, mintro, msetup, mtest, rewriter
from .mesonlib import MesonException
from .environment import detect_msys2_arch
from .wrap import wraptool
class CommandLineParser:
def __init__(self):
self.commands = {}
self.hidden_commands = []
self.parser = argparse.ArgumentParser(prog='meson')
self.subparsers = self.parser.add_subparsers(title='Commands',
description='If no command is specified it defaults to setup command.')
self.add_command('setup', msetup.add_arguments, msetup.run,
help='Configure the project')
self.add_command('configure', mconf.add_arguments, mconf.run,
help='Change project options',)
self.add_command('install', minstall.add_arguments, minstall.run,
help='Install the project')
self.add_command('introspect', mintro.add_arguments, mintro.run,
help='Introspect project')
self.add_command('init', minit.add_arguments, minit.run,
help='Create a new project')
self.add_command('test', mtest.add_arguments, mtest.run,
help='Run tests')
self.add_command('wrap', wraptool.add_arguments, wraptool.run,
help='Wrap tools')
self.add_command('help', self.add_help_arguments, self.run_help_command,
help='Print help of a subcommand')
# Hidden commands
self.add_command('rewrite', rewriter.add_arguments, rewriter.run,
help=argparse.SUPPRESS)
self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command,
help=argparse.SUPPRESS)
def add_command(self, name, add_arguments_func, run_func, help):
# FIXME: Cannot have hidden subparser:
# https://bugs.python.org/issue22848
if help == argparse.SUPPRESS:
p = argparse.ArgumentParser(prog='meson ' + name)
self.hidden_commands.append(name)
else:
p = self.subparsers.add_parser(name, help=help)
add_arguments_func(p)
p.set_defaults(run_func=run_func)
self.commands[name] = p
def add_runpython_arguments(self, parser):
parser.add_argument('script_file')
parser.add_argument('script_args', nargs=argparse.REMAINDER)
def run_runpython_command(self, options):
import runpy
sys.argv[1:] = options.script_args
runpy.run_path(options.script_file, run_name='__main__')
return 0
def add_help_arguments(self, parser):
parser.add_argument('command', nargs='?')
def run_help_command(self, options):
if options.command:
self.commands[options.command].print_help()
else:
self.parser.print_help()
return 0
def run(self, args):
# If first arg is not a known command, assume user wants to run the setup
# command.
known_commands = list(self.commands.keys()) + ['-h', '--help']
if len(args) == 0 or args[0] not in known_commands:
args = ['setup'] + args
# Hidden commands have their own parser instead of using the global one
if args[0] in self.hidden_commands:
parser = self.commands[args[0]]
args = args[1:]
else:
parser = self.parser
args = mesonlib.expand_arguments(args)
options = parser.parse_args(args)
try:
return options.run_func(options)
except MesonException as e:
mlog.exception(e)
logfile = mlog.shutdown()
if logfile is not None:
mlog.log("\nA full log can be found at", mlog.bold(logfile))
if os.environ.get('MESON_FORCE_BACKTRACE'):
raise
return 1
except Exception as e:
if os.environ.get('MESON_FORCE_BACKTRACE'):
raise
traceback.print_exc()
return 2
finally:
mlog.shutdown()
def run_script_command(script_name, script_args):
# Map script name to module name for those that doesn't match
script_map = {'exe': 'meson_exe',
'install': 'meson_install',
'delsuffix': 'delwithsuffix',
'gtkdoc': 'gtkdochelper',
'hotdoc': 'hotdochelper',
'regencheck': 'regen_checker'}
module_name = script_map.get(script_name, script_name)
try:
module = importlib.import_module('mesonbuild.scripts.' + module_name)
except ModuleNotFoundError as e:
mlog.exception(e)
return 1
try:
return module.run(script_args)
except MesonException as e:
mlog.error('Error in {} helper script:'.format(script_name))
mlog.exception(e)
return 1
def run(original_args, mainfile):
if sys.version_info < (3, 5):
print('Meson works correctly only with python 3.5+.')
print('You have python %s.' % sys.version)
print('Please update your environment')
return 1
# https://github.com/mesonbuild/meson/issues/3653
if sys.platform.lower() == 'msys':
mlog.error('This python3 seems to be msys/python on MSYS2 Windows, which is known to have path semantics incompatible with Meson')
msys2_arch = detect_msys2_arch()
if msys2_arch:
mlog.error('Please install and use mingw-w64-i686-python3 and/or mingw-w64-x86_64-python3 with Pacman')
else:
mlog.error('Please download and use Python as detailed at: https://mesonbuild.com/Getting-meson.html')
return 2
# Set the meson command that will be used to run scripts and so on
mesonlib.set_meson_command(mainfile)
args = original_args[:]
# Special handling of internal commands called from backends, they don't
# need to go through argparse.
if len(args) >= 2 and args[0] == '--internal':
if args[1] == 'regenerate':
# Rewrite "meson --internal regenerate" command line to
# "meson --reconfigure"
args = ['--reconfigure'] + args[2:]
else:
return run_script_command(args[1], args[2:])
return CommandLineParser().run(args)
def main():
# Always resolve the command path so Ninja can find it for regen, tests, etc.
if 'meson.exe' in sys.executable:
assert(os.path.isabs(sys.executable))
launcher = sys.executable
else:
launcher = os.path.realpath(sys.argv[0])
return run(sys.argv[1:], launcher)
if __name__ == '__main__':
sys.exit(main())