mintro: Allow introspect --projectinfo without build directory.

This variant was added to allow introspection before configuring a build
directory. This is useful for IDE integration to allow displaying and/or
setting options for the initial configuration of the build directory.

It also allows showing basic information about the project even if it's
not yet configured or configuring failed.

The project 'name' field in --projectinfo is used inconsistently:
For the top level project it always shows the name configured in
the top level meson.build file. For subprojects it's referring to the
name of the directory the subproject's meson.build is contained in.

To have a consistent output and preserve the existing behavior this adds
the 'descriptive_name' field which always shows the name set in the
project.

To be consistent the 'descriptive_name' field was also added to the
--projectfiles variant that uses an already configured build.

It also extends the information shown with the list of buildsystem-files.
This is currently only implemented in the variant for unconfigured
projects.
pull/4191/head
Daniel Schulte 6 years ago
parent 8c9c5199f9
commit 0e62193730
  1. 35
      docs/markdown/snippets/introspect_projectinfo.md
  2. 70
      mesonbuild/mintro.py
  3. 37
      run_unittests.py

@ -0,0 +1,35 @@
## `introspect --projectinfo` can now be used without configured build directory
This allows IDE integration to get information about the project before the user has configured a build directory.
Before you could use `meson.py introspect --projectinfo build-directory`.
Now you also can use `meson.py introspect --projectinfo project-dir/meson.build`.
The output is similiar to the output with a build directory but additionally also includes information from `introspect --buildsystem-files`.
For example `meson.py introspect --projectinfo test\ cases/common/47\ subproject\ options/meson.build`
This outputs (pretty printed for readability):
```
{
"buildsystem_files": [
"meson_options.txt",
"meson.build"
],
"name": "suboptions",
"version": null,
"descriptive_name": "suboptions",
"subprojects": [
{
"buildsystem_files": [
"subprojects/subproject/meson_options.txt",
"subprojects/subproject/meson.build"
],
"name": "subproject",
"version": "undefined",
"descriptive_name": "subproject"
}
]
}
```
Both usages now include a new `descriptive_name` property which always shows the name set in the project.

@ -22,6 +22,9 @@ project files and don't need this info."""
import json
from . import build, mtest, coredata as cdata
from . import mesonlib
from . import astinterpreter
from . import mparser
from .interpreterbase import InvalidArguments
from .backend import ninjabackend
import sys, os
import pathlib
@ -223,19 +226,82 @@ def list_tests(testdata):
print(json.dumps(result))
def list_projinfo(builddata):
result = {'name': builddata.project_name, 'version': builddata.project_version}
result = {'name': builddata.project_name,
'version': builddata.project_version,
'descriptive_name': builddata.project_name}
subprojects = []
for k, v in builddata.subprojects.items():
c = {'name': k,
'version': v}
'version': v,
'descriptive_name': builddata.projects.get(k)}
subprojects.append(c)
result['subprojects'] = subprojects
print(json.dumps(result))
class ProjectInfoInterperter(astinterpreter.AstInterpreter):
def __init__(self, source_root, subdir):
super().__init__(source_root, subdir)
self.funcs.update({'project': self.func_project})
self.project_name = None
self.project_version = None
def func_project(self, node, args, kwargs):
if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.')
self.project_name = args[0]
self.project_version = kwargs.get('version', 'undefined')
if isinstance(self.project_version, mparser.ElementaryNode):
self.project_version = self.project_version.value
def set_variable(self, varname, variable):
pass
def analyze(self):
self.load_root_meson_file()
self.sanity_check_ast()
self.parse_project()
self.run()
def list_projinfo_from_source(sourcedir):
files = find_buildsystem_files_list(sourcedir)
result = {'buildsystem_files': []}
subprojects = {}
for f in files:
f = f.replace('\\', '/')
if f == 'meson.build':
interpreter = ProjectInfoInterperter(sourcedir, '')
interpreter.analyze()
version = None
if interpreter.project_version is str:
version = interpreter.project_version
result.update({'name': interpreter.project_name, 'version': version, 'descriptive_name': interpreter.project_name})
result['buildsystem_files'].append(f)
elif f.startswith('subprojects/'):
subproject_id = f.split('/')[1]
subproject = subprojects.setdefault(subproject_id, {'buildsystem_files': []})
subproject['buildsystem_files'].append(f)
if f.count('/') == 2 and f.endswith('meson.build'):
interpreter = ProjectInfoInterperter(os.path.join(sourcedir, 'subprojects', subproject_id), '')
interpreter.analyze()
subproject.update({'name': subproject_id, 'version': interpreter.project_version, 'descriptive_name': interpreter.project_name})
else:
result['buildsystem_files'].append(f)
subprojects = [obj for name, obj in subprojects.items()]
result['subprojects'] = subprojects
print(json.dumps(result))
def run(options):
datadir = 'meson-private'
if options.builddir is not None:
datadir = os.path.join(options.builddir, datadir)
if options.builddir.endswith('/meson.build') or options.builddir.endswith('\\meson.build') or options.builddir == 'meson.build':
if options.projectinfo:
sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11]
list_projinfo_from_source(sourcedir)
return 0
if not os.path.isdir(datadir):
print('Current directory is not a build dir. Please specify it or '
'change the working directory to it.')

@ -1165,6 +1165,13 @@ class BasePlatformTests(unittest.TestCase):
universal_newlines=True)
return json.loads(out)
def introspect_directory(self, directory, args):
if isinstance(args, str):
args = [args]
out = subprocess.check_output(self.mintro_command + args + [directory],
universal_newlines=True)
return json.loads(out)
def assertPathEqual(self, path1, path2):
'''
Handles a lot of platform-specific quirks related to paths such as
@ -2913,6 +2920,36 @@ recommended as it is not supported on some platforms''')
'target2-id', '@other')
self.assertEqual('81d46d1@@target2-id@other', target_id)
def test_introspect_projectinfo_without_configured_build(self):
testfile = os.path.join(self.common_test_dir, '36 run program', 'meson.build')
res = self.introspect_directory(testfile, '--projectinfo')
self.assertEqual(set(res['buildsystem_files']), set(['meson.build']))
self.assertEqual(res['name'], 'run command')
self.assertEqual(res['version'], None)
self.assertEqual(res['descriptive_name'], 'run command')
self.assertEqual(res['subprojects'], [])
testfile = os.path.join(self.common_test_dir, '44 options', 'meson.build')
res = self.introspect_directory(testfile, '--projectinfo')
self.assertEqual(set(res['buildsystem_files']), set(['meson_options.txt', 'meson.build']))
self.assertEqual(res['name'], 'options')
self.assertEqual(res['version'], None)
self.assertEqual(res['descriptive_name'], 'options')
self.assertEqual(res['subprojects'], [])
testfile = os.path.join(self.common_test_dir, '47 subproject options', 'meson.build')
res = self.introspect_directory(testfile, '--projectinfo')
self.assertEqual(set(res['buildsystem_files']), set(['meson_options.txt', 'meson.build']))
self.assertEqual(res['name'], 'suboptions')
self.assertEqual(res['version'], None)
self.assertEqual(res['descriptive_name'], 'suboptions')
self.assertEqual(len(res['subprojects']), 1)
subproject_files = set(f.replace('\\', '/') for f in res['subprojects'][0]['buildsystem_files'])
self.assertEqual(subproject_files, set(['subprojects/subproject/meson_options.txt', 'subprojects/subproject/meson.build']))
self.assertEqual(res['subprojects'][0]['name'], 'subproject')
self.assertEqual(res['subprojects'][0]['version'], 'undefined')
self.assertEqual(res['subprojects'][0]['descriptive_name'], 'subproject')
class FailureTests(BasePlatformTests):
'''

Loading…
Cancel
Save