diff options
Diffstat (limited to 'mesonbuild/mintro.py')
-rw-r--r-- | mesonbuild/mintro.py | 148 |
1 files changed, 106 insertions, 42 deletions
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index b1e6509..243dc5d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -22,9 +22,11 @@ project files and don't need this info.""" import json from . import build, coredata as cdata from . import mesonlib -from .ast import IntrospectionInterpreter +from .ast import IntrospectionInterpreter, build_target_functions, AstConditionLevel, AstIDGenerator, AstIndentationGenerator from . import mlog from .backend import backends +from .mparser import FunctionNode, ArrayNode, ArgumentNode, StringNode +from typing import List, Optional import sys, os import pathlib @@ -37,7 +39,10 @@ def get_meson_introspection_version(): def get_meson_introspection_required_version(): return ['>=1.0', '<2.0'] -def get_meson_introspection_types(coredata: cdata.CoreData = None, builddata: build.Build = None, backend: backends.Backend = None): +def get_meson_introspection_types(coredata: Optional[cdata.CoreData] = None, + builddata: Optional[build.Build] = None, + backend: Optional[backends.Backend] = None, + sourcedir: Optional[str] = None): if backend and builddata: benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) testdata = backend.create_test_serialisation(builddata.get_tests()) @@ -52,6 +57,7 @@ def get_meson_introspection_types(coredata: cdata.CoreData = None, builddata: bu }, 'buildoptions': { 'func': lambda: list_buildoptions(coredata), + 'no_bd': lambda intr: list_buildoptions_from_source(intr), 'desc': 'List all build options.', }, 'buildsystem_files': { @@ -61,18 +67,26 @@ def get_meson_introspection_types(coredata: cdata.CoreData = None, builddata: bu }, 'dependencies': { 'func': lambda: list_deps(coredata), + 'no_bd': lambda intr: list_deps_from_source(intr), 'desc': 'List external dependencies.', }, + 'scan_dependencies': { + 'no_bd': lambda intr: list_deps_from_source(intr), + 'desc': 'Scan for dependencies used in the meson.build file.', + 'key': 'scan-dependencies', + }, 'installed': { 'func': lambda: list_installed(installdata), 'desc': 'List all installed files and directories.', }, 'projectinfo': { 'func': lambda: list_projinfo(builddata), + 'no_bd': lambda intr: list_projinfo_from_source(sourcedir, intr), 'desc': 'Information about projects.', }, 'targets': { 'func': lambda: list_targets(builddata, installdata, backend), + 'no_bd': lambda intr: list_targets_from_source(intr), 'desc': 'List top level targets.', }, 'tests': { @@ -113,6 +127,46 @@ def list_installed(installdata): res[path] = os.path.join(installdata.prefix, installpath) return res +def list_targets_from_source(intr: IntrospectionInterpreter): + tlist = [] + for i in intr.targets: + sources = [] + for n in i['sources']: + args = [] + if isinstance(n, FunctionNode): + args = list(n.args.arguments) + if n.func_name in build_target_functions: + args.pop(0) + elif isinstance(n, ArrayNode): + args = n.args.arguments + elif isinstance(n, ArgumentNode): + args = n.arguments + for j in args: + if isinstance(j, StringNode): + sources += [j.value] + elif isinstance(j, str): + sources += [j] + + tlist += [{ + 'name': i['name'], + 'id': i['id'], + 'type': i['type'], + 'defined_in': i['defined_in'], + 'filename': [os.path.join(i['subdir'], x) for x in i['outputs']], + 'build_by_default': i['build_by_default'], + 'target_sources': [{ + 'language': 'unknown', + 'compiler': [], + 'parameters': [], + 'sources': [os.path.normpath(os.path.join(os.path.abspath(intr.source_root), i['subdir'], x)) for x in sources], + 'generated_sources': [] + }], + 'subproject': None, # Subprojects are not supported + 'installed': i['installed'] + }] + + return tlist + def list_targets(builddata: build.Build, installdata, backend: backends.Backend): tlist = [] build_dir = builddata.environment.get_build_dir() @@ -147,15 +201,8 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) tlist.append(t) return tlist -def list_buildoptions_from_source(sourcedir, backend, indent): - # Make sure that log entries in other parts of meson don't interfere with the JSON output - mlog.disable() - backend = backends.get_backend_from_name(backend, None) - intr = IntrospectionInterpreter(sourcedir, '', backend.name) - intr.analyze() - # Reenable logging just in case - mlog.enable() - print(json.dumps(list_buildoptions(intr.coredata), indent=indent)) +def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> List[dict]: + return list_buildoptions(intr.coredata) def list_target_files(target_name: str, targets: list, source_dir: str): sys.stderr.write("WARNING: The --target-files introspection API is deprecated. Use --targets instead.\n") @@ -178,7 +225,7 @@ def list_target_files(target_name: str, targets: list, source_dir: str): return result -def list_buildoptions(coredata: cdata.CoreData): +def list_buildoptions(coredata: cdata.CoreData) -> List[dict]: optlist = [] dir_option_names = ['bindir', @@ -250,6 +297,12 @@ def list_buildsystem_files(builddata: build.Build): filelist = [os.path.join(src_dir, x) for x in filelist] return filelist +def list_deps_from_source(intr: IntrospectionInterpreter): + result = [] + for i in intr.dependencies: + result += [{k: v for k, v in i.items() if k in ['name', 'required', 'has_fallback', 'conditional']}] + return result + def list_deps(coredata: cdata.CoreData): result = [] for d in coredata.deps.values(): @@ -299,15 +352,10 @@ def list_projinfo(builddata: build.Build): result['subprojects'] = subprojects return result -def list_projinfo_from_source(sourcedir, indent): +def list_projinfo_from_source(sourcedir: str, intr: IntrospectionInterpreter): files = find_buildsystem_files_list(sourcedir) files = [os.path.normpath(x) for x in files] - mlog.disable() - intr = IntrospectionInterpreter(sourcedir, '', 'ninja') - intr.analyze() - mlog.enable() - for i in intr.project_data['subprojects']: basedir = os.path.join(intr.subproject_dir, i['name']) i['buildsystem_files'] = [x for x in files if x.startswith(basedir)] @@ -315,23 +363,47 @@ def list_projinfo_from_source(sourcedir, indent): intr.project_data['buildsystem_files'] = files intr.project_data['subproject_dir'] = intr.subproject_dir - print(json.dumps(intr.project_data, indent=indent)) + return intr.project_data + +def print_results(options, results, indent): + if len(results) == 0 and not options.force_dict: + print('No command specified') + return 1 + elif len(results) == 1 and not options.force_dict: + # Make to keep the existing output format for a single option + print(json.dumps(results[0][1], indent=indent)) + else: + out = {} + for i in results: + out[i[0]] = i[1] + print(json.dumps(out, indent=indent)) + return 0 def run(options): datadir = 'meson-private' infodir = 'meson-info' - indent = 4 if options.indent else None if options.builddir is not None: datadir = os.path.join(options.builddir, datadir) infodir = os.path.join(options.builddir, infodir) + indent = 4 if options.indent else None + results = [] + sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11] + intro_types = get_meson_introspection_types(sourcedir=sourcedir) + if 'meson.build' in [os.path.basename(options.builddir), options.builddir]: - sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11] - if options.projectinfo: - list_projinfo_from_source(sourcedir, indent) - return 0 - if options.buildoptions: - list_buildoptions_from_source(sourcedir, options.backend, indent) - return 0 + # Make sure that log entries in other parts of meson don't interfere with the JSON output + mlog.disable() + backend = backends.get_backend_from_name(options.backend, None) + intr = IntrospectionInterpreter(sourcedir, '', backend.name, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()]) + intr.analyze() + # Reenable logging just in case + mlog.enable() + for key, val in intro_types.items(): + if (not options.all and not getattr(options, key, False)) or 'no_bd' not in val: + continue + results += [(key, val['no_bd'](intr))] + return print_results(options, results, indent) + infofile = get_meson_info_file(infodir) if not os.path.isdir(datadir) or not os.path.isdir(infodir) or not os.path.isfile(infofile): print('Current directory is not a meson build directory.' @@ -355,9 +427,6 @@ def run(options): .format(intro_vers, ' and '.join(vers_to_check))) return 1 - results = [] - intro_types = get_meson_introspection_types() - # Handle the one option that does not have its own JSON file (meybe deprecate / remove this?) if options.target_files is not None: targets_file = os.path.join(infodir, 'intro-targets.json') @@ -367,6 +436,8 @@ def run(options): # Extract introspection information from JSON for i in intro_types.keys(): + if 'func' not in intro_types[i]: + continue if not options.all and not getattr(options, i, False): continue curr = os.path.join(infodir, 'intro-{}.json'.format(i)) @@ -376,18 +447,7 @@ def run(options): with open(curr, 'r') as fp: results += [(i, json.load(fp))] - if len(results) == 0 and not options.force_dict: - print('No command specified') - return 1 - elif len(results) == 1 and not options.force_dict: - # Make to keep the existing output format for a single option - print(json.dumps(results[0][1], indent=indent)) - else: - out = {} - for i in results: - out[i[0]] = i[1] - print(json.dumps(out, indent=indent)) - return 0 + return print_results(options, results, indent) updated_introspection_files = [] @@ -408,6 +468,8 @@ def generate_introspection_file(builddata: build.Build, backend: backends.Backen intro_info = [] for key, val in intro_types.items(): + if 'func' not in val: + continue intro_info += [(key, val['func']())] write_intro_info(intro_info, builddata.environment.info_dir) @@ -436,6 +498,8 @@ def write_meson_info_file(builddata: build.Build, errors: list, build_files_upda intro_info = {} for i in intro_types.keys(): + if 'func' not in intro_types[i]: + continue intro_info[i] = { 'file': 'intro-{}.json'.format(i), 'updated': i in updated_introspection_files |