aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/mintro.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2019-03-04 01:48:58 +0200
committerGitHub <noreply@github.com>2019-03-04 01:48:58 +0200
commit0de4f199b3aa30939436742cfcd706412ea498e9 (patch)
tree9e8e3bae6cee8ce439febfd18a8d26c71eb54380 /mesonbuild/mintro.py
parent7f9fb6a08481c26698fb9c8ce4920c456e46bbbd (diff)
parent586ec5a28cc2ea854f726cb7a6075b0918595bf5 (diff)
downloadmeson-0de4f199b3aa30939436742cfcd706412ea498e9.zip
meson-0de4f199b3aa30939436742cfcd706412ea498e9.tar.gz
meson-0de4f199b3aa30939436742cfcd706412ea498e9.tar.bz2
Merge pull request #4949 from mensinda/introTargetNoBD
mintro: Introspect targets and deps without a build directory
Diffstat (limited to 'mesonbuild/mintro.py')
-rw-r--r--mesonbuild/mintro.py148
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