From 0a125948478095bd762a7669a453fc9d6d4477c7 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 24 Nov 2018 17:42:35 +0100 Subject: [PATCH 01/63] Added include_directories and extra_args keys to target introspection --- mesonbuild/mintro.py | 79 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 3896c9276..55094449e 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -30,8 +30,11 @@ from . import compilers from . import optinterpreter from .interpreterbase import InvalidArguments from .backend import ninjabackend, backends +from .dependencies import base +from .compilers import compilers import sys, os import pathlib +import re def add_arguments(parser): parser.add_argument('--targets', action='store_true', dest='list_targets', default=False, @@ -88,11 +91,85 @@ def list_installed(installdata): res[path] = os.path.join(installdata.prefix, installpath) print(json.dumps(res)) +def include_dirs_to_path(inc_dirs, src_root, build_root): + result = [] + if isinstance(inc_dirs, build.IncludeDirs): + for i in inc_dirs.get_incdirs(): + abs_src = os.path.join(src_root, inc_dirs.get_curdir(), i) + abs_build = os.path.join(build_root, inc_dirs.get_curdir(), i) + + if os.path.isdir(abs_src): + result += [abs_src] + + if os.path.isdir(abs_build): + result += [abs_build] + + return result + +def extract_dependency_infromation(dep): + inc_dirs = [] + args = [] + if isinstance(dep, base.Dependency): + reg = re.compile(r'-I(.*)') + for i in dep.get_compile_args(): + match = reg.match(i) + if match: + inc_dirs += [match.group(1)] + else: + args += [i] + + return inc_dirs, args def list_targets(coredata, builddata, installdata): tlist = [] for (idname, target) in builddata.get_targets().items(): - t = {'name': target.get_basename(), 'id': idname} + src_root = builddata.environment.get_source_dir() + build_root = builddata.environment.get_build_dir() + + inc_dirs = [] + extra_args = {} + dep_args = [] + + def climb_stack(tgt, inc_dirs, extra_args, dep_args): + if isinstance(tgt, build.BuildTarget): + # The build directory is always in the include directories + absbase_src = os.path.join(src_root, tgt.subdir) + absbase_build = os.path.join(build_root, tgt.subdir) + inc_dirs += [absbase_src, absbase_build] + + for i in tgt.include_dirs: + inc_dirs += include_dirs_to_path(i, src_root, build_root) + + for i in tgt.external_deps: + dep_inc_dirs, args = extract_dependency_infromation(i) + inc_dirs += dep_inc_dirs + dep_args += args + + for i, comp in tgt.compilers.items(): + if isinstance(comp, compilers.Compiler): + lang = comp.get_language() + if lang not in extra_args: + extra_args[lang] = [] + + extra_args[i] += tgt.get_extra_args(lang) + extra_args[i] += builddata.get_global_args(comp, False) + extra_args[i] += builddata.get_project_args(comp, tgt.subproject, False) + + for i in tgt.link_targets: + climb_stack(i, inc_dirs, extra_args, dep_args) + + climb_stack(target, inc_dirs, extra_args, dep_args) + + # Add the dep_args, sort and remove duplicates + for i in extra_args: + extra_args[i] += dep_args + extra_args[i] = list(sorted(list(set(extra_args[i])))) + + # Remove duplicates, sort and make paths pretty + inc_dirs = list(sorted(list(set(inc_dirs)))) + inc_dirs = list(map(lambda x: os.path.realpath(x), inc_dirs)) + + t = {'name': target.get_basename(), 'id': idname, 'include_directories': inc_dirs, 'extra_args': extra_args} fname = target.get_filename() if isinstance(fname, list): fname = [os.path.join(target.subdir, x) for x in fname] From 80ee0f85f97b145058e2ca93090501f33a980a23 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 24 Nov 2018 17:51:13 +0100 Subject: [PATCH 02/63] Added release snippet --- docs/markdown/snippets/introspect_target_new.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/markdown/snippets/introspect_target_new.md diff --git a/docs/markdown/snippets/introspect_target_new.md b/docs/markdown/snippets/introspect_target_new.md new file mode 100644 index 000000000..cf0fd6a52 --- /dev/null +++ b/docs/markdown/snippets/introspect_target_new.md @@ -0,0 +1,7 @@ +## New `include_directories` and `extra_args` keys for the target introspection + +Meson now also prints the include directories and extra compiler arguments for +the target introspection (`meson introspect --targets`). + +The `include_directories` key stores a list of absolute paths and the `extra_args` +key holds a dict of compiler arguments for each language. From 9d13855544cd4af446b589d77342cdbb7fd2456c Mon Sep 17 00:00:00 2001 From: mensinda Date: Sat, 24 Nov 2018 19:45:49 +0100 Subject: [PATCH 03/63] Fixed hardcode is_cross = False --- mesonbuild/mintro.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 55094449e..0459b4e40 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -147,13 +147,12 @@ def list_targets(coredata, builddata, installdata): for i, comp in tgt.compilers.items(): if isinstance(comp, compilers.Compiler): - lang = comp.get_language() - if lang not in extra_args: - extra_args[lang] = [] + if i not in extra_args: + extra_args[i] = [] - extra_args[i] += tgt.get_extra_args(lang) - extra_args[i] += builddata.get_global_args(comp, False) - extra_args[i] += builddata.get_project_args(comp, tgt.subproject, False) + extra_args[i] += tgt.get_extra_args(i) + extra_args[i] += builddata.get_global_args(comp, tgt.is_cross) + extra_args[i] += builddata.get_project_args(comp, tgt.subproject, tgt.is_cross) for i in tgt.link_targets: climb_stack(i, inc_dirs, extra_args, dep_args) From 1a0e1afbddd51c8ddc4e78e8df4c9ab8e795b8d6 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 27 Nov 2018 17:55:59 +0100 Subject: [PATCH 04/63] Updated format to include sources --- mesonbuild/mintro.py | 66 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 0459b4e40..5372b3024 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -123,12 +123,16 @@ def extract_dependency_infromation(dep): def list_targets(coredata, builddata, installdata): tlist = [] for (idname, target) in builddata.get_targets().items(): + if not isinstance(target, build.Target): + raise RuntimeError('Something weird happened. File a bug.') + src_root = builddata.environment.get_source_dir() build_root = builddata.environment.get_build_dir() inc_dirs = [] extra_args = {} dep_args = [] + sources = {} def climb_stack(tgt, inc_dirs, extra_args, dep_args): if isinstance(tgt, build.BuildTarget): @@ -157,18 +161,54 @@ def list_targets(coredata, builddata, installdata): for i in tgt.link_targets: climb_stack(i, inc_dirs, extra_args, dep_args) - climb_stack(target, inc_dirs, extra_args, dep_args) - - # Add the dep_args, sort and remove duplicates - for i in extra_args: - extra_args[i] += dep_args - extra_args[i] = list(sorted(list(set(extra_args[i])))) - - # Remove duplicates, sort and make paths pretty - inc_dirs = list(sorted(list(set(inc_dirs)))) - inc_dirs = list(map(lambda x: os.path.realpath(x), inc_dirs)) - - t = {'name': target.get_basename(), 'id': idname, 'include_directories': inc_dirs, 'extra_args': extra_args} + if isinstance(target, build.BuildTarget): + climb_stack(target, inc_dirs, extra_args, dep_args) + + # Add the dep_args, sort and remove duplicates + for i in extra_args: + extra_args[i] += dep_args + extra_args[i] = list(sorted(list(set(extra_args[i])))) + + # Remove duplicates, sort and make paths pretty + inc_dirs = list(sorted(list(set(inc_dirs)))) + inc_dirs = list(map(lambda x: os.path.realpath(x), inc_dirs)) + + comp_list = target.compilers.values() + source_list = target.sources + target.extra_files + source_list = list(map(lambda x: (mesonlib.get_compiler_for_source(comp_list, x), x), source_list)) + + for comp, src in source_list: + if isinstance(comp, compilers.Compiler) and isinstance(src, mesonlib.File): + lang = comp.get_language() + if lang not in sources: + parameters = [] + + # Generate include directories + # Not all compilers have the get_include_args method + get_include_args = getattr(comp, 'get_include_args', None) + if callable(get_include_args): + for i in inc_dirs: + parameters += comp.get_include_args(i, False) + + # Extra args + if lang in extra_args: + parameters += extra_args[lang] + + sources[lang] = { + 'compiler': comp.get_exelist(), + 'parameters': parameters, + 'source_files': [] + } + + sources[lang]['source_files'] += [os.path.join(src.subdir, src.fname)] + + # Convert the dict to a list and add the language key. + # This list approach will also work if the gurantee is removed that all + # files in a target are compiled with the same parameters + # see https://github.com/mesonbuild/meson/pull/4547 + sources = list(map(lambda x: {'language': x[0], **x[1]}, sources.items())) + + t = {'name': target.get_basename(), 'id': idname, 'sources': sources} fname = target.get_filename() if isinstance(fname, list): fname = [os.path.join(target.subdir, x) for x in fname] @@ -195,7 +235,7 @@ def list_targets(coredata, builddata, installdata): t['installed'] = False t['build_by_default'] = target.build_by_default tlist.append(t) - print(json.dumps(tlist)) + print(json.dumps(tlist, indent=2)) def list_target_files(target_name, coredata, builddata): try: From 8288555aa1dcab1ae38831d40529c6a2fbe3c8fd Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 25 Nov 2018 21:40:38 +0100 Subject: [PATCH 05/63] mintro: Added option to introspect multiple parameters at once --- docs/markdown/snippets/introspect_multiple.md | 12 ++++ mesonbuild/mintro.py | 71 ++++++++++++------- 2 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 docs/markdown/snippets/introspect_multiple.md diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md new file mode 100644 index 000000000..b7266f805 --- /dev/null +++ b/docs/markdown/snippets/introspect_multiple.md @@ -0,0 +1,12 @@ +## Added option to introspect multiple parameters at once + +Meson introspect can now print the results of multiple parameters +in a single call. The results are then printed as a single JSON +object. + +The format for a single command was not changed to keep backward +compatibility. + +Furthermore the option `-a,--all` and `-i,--indent` was added to +print all introspection information in one go and format the +JSON output (the default is still compact JSON). \ No newline at end of file diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 5372b3024..70f0f09df 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -57,6 +57,10 @@ def add_arguments(parser): help='Information about projects.') parser.add_argument('--backend', choices=cdata.backendlist, dest='backend', default='ninja', help='The backend to use for the --buildoptions introspection.') + parser.add_argument('-a', '--all', action='store_true', dest='all', default=False, + help='Print all available information.') + parser.add_argument('-i', '--indent', dest='indent', type=int, default=0, + help='Number of spaces used for indentation.') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') def determine_installed_path(target, installdata): @@ -89,7 +93,7 @@ def list_installed(installdata): res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path)) for path, installpath, unused_custom_install_mode in installdata.man: res[path] = os.path.join(installdata.prefix, installpath) - print(json.dumps(res)) + return res def include_dirs_to_path(inc_dirs, src_root, build_root): result = [] @@ -235,7 +239,7 @@ def list_targets(coredata, builddata, installdata): t['installed'] = False t['build_by_default'] = target.build_by_default tlist.append(t) - print(json.dumps(tlist, indent=2)) + return tlist def list_target_files(target_name, coredata, builddata): try: @@ -249,7 +253,7 @@ def list_target_files(target_name, coredata, builddata): if isinstance(i, mesonlib.File): i = os.path.join(i.subdir, i.fname) out.append(i) - print(json.dumps(out)) + return out class BuildoptionsOptionHelper: # mimic an argparse namespace @@ -429,7 +433,7 @@ def list_buildoptions(coredata): add_keys(optlist, dir_options, 'directory') add_keys(optlist, coredata.user_options, 'user') add_keys(optlist, test_options, 'test') - print(json.dumps(optlist)) + return optlist def add_keys(optlist, options, section): keys = list(options.keys()) @@ -466,7 +470,7 @@ def find_buildsystem_files_list(src_dir): def list_buildsystem_files(builddata): src_dir = builddata.environment.get_source_dir() filelist = find_buildsystem_files_list(src_dir) - print(json.dumps(filelist)) + return filelist def list_deps(coredata): result = [] @@ -475,7 +479,7 @@ def list_deps(coredata): result += [{'name': d.name, 'compile_args': d.get_compile_args(), 'link_args': d.get_link_args()}] - print(json.dumps(result)) + return result def list_tests(testdata): result = [] @@ -496,7 +500,7 @@ def list_tests(testdata): to['suite'] = t.suite to['is_parallel'] = t.is_parallel result.append(to) - print(json.dumps(result)) + return result def list_projinfo(builddata): result = {'version': builddata.project_version, @@ -508,7 +512,7 @@ def list_projinfo(builddata): 'descriptive_name': builddata.projects.get(k)} subprojects.append(c) result['subprojects'] = subprojects - print(json.dumps(result)) + return result class ProjectInfoInterperter(astinterpreter.AstInterpreter): def __init__(self, source_root, subdir): @@ -593,25 +597,38 @@ def run(options): except FileNotFoundError: installdata = None - if options.list_targets: - list_targets(coredata, builddata, installdata) - elif options.list_installed: - list_installed(installdata) - elif options.target_files is not None: - list_target_files(options.target_files, coredata, builddata) - elif options.buildsystem_files: - list_buildsystem_files(builddata) - elif options.buildoptions: - list_buildoptions(coredata) - elif options.tests: - list_tests(testdata) - elif options.benchmarks: - list_tests(benchmarkdata) - elif options.dependencies: - list_deps(coredata) - elif options.projectinfo: - list_projinfo(builddata) - else: + results = [] + + if options.all or options.list_targets: + results += [('targets', list_targets(coredata, builddata, installdata))] + if options.all or options.list_installed: + results += [('installed', list_installed(installdata))] + if options.target_files is not None: + results += [('target_files', list_target_files(options.target_files, coredata, builddata))] + if options.all or options.buildsystem_files: + results += [('buildsystem_files', list_buildsystem_files(builddata))] + if options.all or options.buildoptions: + results += [('buildoptions', list_buildoptions(coredata))] + if options.all or options.tests: + results += [('tests', list_tests(testdata))] + if options.all or options.benchmarks: + results += [('benchmarks', list_tests(benchmarkdata))] + if options.all or options.dependencies: + results += [('dependencies', list_deps(coredata))] + if options.all or options.projectinfo: + results += [('projectinfo', list_projinfo(builddata))] + + indent = options.indent if options.indent > 0 else None + + if len(results) == 0: print('No command specified') return 1 + elif len(results) == 1: + # 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 From a6034d1f198cd08a48026d2478c4e4539a3d9b36 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 26 Nov 2018 18:58:03 +0100 Subject: [PATCH 06/63] Added option to force use the new format --- mesonbuild/mintro.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 70f0f09df..8d106c9d0 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -61,6 +61,8 @@ def add_arguments(parser): help='Print all available information.') parser.add_argument('-i', '--indent', dest='indent', type=int, default=0, help='Number of spaces used for indentation.') + parser.add_argument('-f', '--force-new', action='store_true', dest='force_new', default=False, + help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') def determine_installed_path(target, installdata): @@ -620,10 +622,10 @@ def run(options): indent = options.indent if options.indent > 0 else None - if len(results) == 0: + if len(results) == 0 and not options.force_new: print('No command specified') return 1 - elif len(results) == 1: + elif len(results) == 1 and not options.force_new: # Make to keep the existing output format for a single option print(json.dumps(results[0][1], indent=indent)) else: From f4285f350e1acf6ffe10a6dafff4028c0d142924 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 27 Nov 2018 13:35:28 +0100 Subject: [PATCH 07/63] Initial automatic target file generation --- mesonbuild/backend/backends.py | 10 +++--- mesonbuild/mintro.py | 64 +++++++++++++++++++++++++--------- mesonbuild/msetup.py | 4 +++ 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 22920f49b..bf83ab1b8 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -974,9 +974,7 @@ class Backend: cmd = s['exe'] + s['args'] subprocess.check_call(cmd, env=child_env) - def create_install_data_files(self): - install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat') - + def create_install_data(self): strip_bin = self.environment.binaries.host.lookup_entry('strip') if strip_bin is None: if self.environment.is_cross_build(): @@ -997,8 +995,12 @@ class Backend: self.generate_data_install(d) self.generate_custom_install_script(d) self.generate_subdir_install(d) + return d + + def create_install_data_files(self): + install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat') with open(install_data_file, 'wb') as ofile: - pickle.dump(d, ofile) + pickle.dump(self.create_install_data(), ofile) def generate_target_install(self, d): for t in self.build.get_targets().values(): diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 8d106c9d0..2f8b3f394 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -36,6 +36,8 @@ import sys, os import pathlib import re +INTROSPECTION_OUTPUT_FILE = 'meson-introspection.json' + def add_arguments(parser): parser.add_argument('--targets', action='store_true', dest='list_targets', default=False, help='List top level targets.') @@ -95,7 +97,7 @@ def list_installed(installdata): res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path)) for path, installpath, unused_custom_install_mode in installdata.man: res[path] = os.path.join(installdata.prefix, installpath) - return res + return ('installed', res) def include_dirs_to_path(inc_dirs, src_root, build_root): result = [] @@ -241,7 +243,7 @@ def list_targets(coredata, builddata, installdata): t['installed'] = False t['build_by_default'] = target.build_by_default tlist.append(t) - return tlist + return ('targets', tlist) def list_target_files(target_name, coredata, builddata): try: @@ -255,7 +257,7 @@ def list_target_files(target_name, coredata, builddata): if isinstance(i, mesonlib.File): i = os.path.join(i.subdir, i.fname) out.append(i) - return out + return ('target_files', out) class BuildoptionsOptionHelper: # mimic an argparse namespace @@ -435,7 +437,7 @@ def list_buildoptions(coredata): add_keys(optlist, dir_options, 'directory') add_keys(optlist, coredata.user_options, 'user') add_keys(optlist, test_options, 'test') - return optlist + return ('buildoptions', optlist) def add_keys(optlist, options, section): keys = list(options.keys()) @@ -467,7 +469,7 @@ def find_buildsystem_files_list(src_dir): for f in files: if f == 'meson.build' or f == 'meson_options.txt': filelist.append(os.path.relpath(os.path.join(root, f), src_dir)) - return filelist + return ('buildsystem_files', filelist) def list_buildsystem_files(builddata): src_dir = builddata.environment.get_source_dir() @@ -481,9 +483,9 @@ def list_deps(coredata): result += [{'name': d.name, 'compile_args': d.get_compile_args(), 'link_args': d.get_link_args()}] - return result + return ('dependencies', result) -def list_tests(testdata): +def get_test_list(testdata): result = [] for t in testdata: to = {} @@ -504,6 +506,12 @@ def list_tests(testdata): result.append(to) return result +def list_tests(testdata): + return ('tests', get_test_list(testdata)) + +def list_benchmarks(benchdata): + return ('benchmarks', get_test_list(benchdata)) + def list_projinfo(builddata): result = {'version': builddata.project_version, 'descriptive_name': builddata.project_name} @@ -514,7 +522,7 @@ def list_projinfo(builddata): 'descriptive_name': builddata.projects.get(k)} subprojects.append(c) result['subprojects'] = subprojects - return result + return ('projectinfo', result) class ProjectInfoInterperter(astinterpreter.AstInterpreter): def __init__(self, source_root, subdir): @@ -602,23 +610,23 @@ def run(options): results = [] if options.all or options.list_targets: - results += [('targets', list_targets(coredata, builddata, installdata))] + results += [list_targets(coredata, builddata, installdata)] if options.all or options.list_installed: - results += [('installed', list_installed(installdata))] + results += [list_installed(installdata)] if options.target_files is not None: - results += [('target_files', list_target_files(options.target_files, coredata, builddata))] + results += [list_target_files(options.target_files, coredata, builddata)] if options.all or options.buildsystem_files: - results += [('buildsystem_files', list_buildsystem_files(builddata))] + results += [list_buildsystem_files(builddata)] if options.all or options.buildoptions: - results += [('buildoptions', list_buildoptions(coredata))] + results += [list_buildoptions(coredata)] if options.all or options.tests: - results += [('tests', list_tests(testdata))] + results += [list_tests(testdata)] if options.all or options.benchmarks: - results += [('benchmarks', list_tests(benchmarkdata))] + results += [list_benchmarks(benchmarkdata)] if options.all or options.dependencies: - results += [('dependencies', list_deps(coredata))] + results += [list_deps(coredata)] if options.all or options.projectinfo: - results += [('projectinfo', list_projinfo(builddata))] + results += [list_projinfo(builddata)] indent = options.indent if options.indent > 0 else None @@ -634,3 +642,25 @@ def run(options): out[i[0]] = i[1] print(json.dumps(out, indent=indent)) return 0 + +def generate_introspection_file(coredata, builddata, testdata, benchmarkdata, installdata): + intro_info = [ + #list_benchmarks(benchmarkdata), + list_buildoptions(coredata, builddata), + list_buildsystem_files(builddata), + list_deps(coredata), + list_installed(installdata), + list_projinfo(builddata), + list_targets(coredata, builddata, installdata), + #list_tests(testdata) + ] + + outdict = {} + for i in intro_info: + outdict[i[0]] = i[1] + + outfile = os.path.join(builddata.environment.get_build_dir(), INTROSPECTION_OUTPUT_FILE) + outfile = os.path.abspath(outfile) + + with open(outfile, 'w') as fp: + json.dump(outdict, fp, indent=2) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 56a0e9a79..10eecd8e7 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -23,6 +23,7 @@ import argparse from . import environment, interpreter, mesonlib from . import build from . import mlog, coredata +from . import mintro from .mesonlib import MesonException def add_arguments(parser): @@ -215,6 +216,9 @@ class MesonApp: coredata.write_cmd_line_file(self.build_dir, self.options) else: coredata.update_cmd_line_file(self.build_dir, self.options) + + # Generate an IDE introspection file with the exact same syntax as the introspection API defined in mintro + mintro.generate_introspection_file(b.environment.get_coredata(), b, b.get_tests(), b.get_benchmarks(), intr.backend.create_install_data()) except: if 'cdf' in locals(): old_cdf = cdf + '.prev' From 111e596200ac613f9e50a05d60410604fdd0ca61 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 27 Nov 2018 15:11:47 +0100 Subject: [PATCH 08/63] Fixed tests and benchmarks --- mesonbuild/backend/backends.py | 7 +++++-- mesonbuild/mintro.py | 4 ++-- mesonbuild/msetup.py | 7 +++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index bf83ab1b8..8cc6f2f8a 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -683,7 +683,7 @@ class Backend: def write_test_file(self, datafile): self.write_test_serialisation(self.build.get_tests(), datafile) - def write_test_serialisation(self, tests, datafile): + def create_test_serialisation(self, tests): arr = [] for t in tests: exe = t.get_exe() @@ -730,7 +730,10 @@ class Backend: exe_wrapper, t.is_parallel, cmd_args, t.env, t.should_fail, t.timeout, t.workdir, extra_paths) arr.append(ts) - pickle.dump(arr, datafile) + return arr + + def write_test_serialisation(self, tests, datafile): + pickle.dump(self.create_test_serialisation(tests), datafile) def generate_depmf_install(self, d): if self.build.dep_manifest_name is None: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 2f8b3f394..5ab1d1fee 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -645,14 +645,14 @@ def run(options): def generate_introspection_file(coredata, builddata, testdata, benchmarkdata, installdata): intro_info = [ - #list_benchmarks(benchmarkdata), + list_benchmarks(benchmarkdata), list_buildoptions(coredata, builddata), list_buildsystem_files(builddata), list_deps(coredata), list_installed(installdata), list_projinfo(builddata), list_targets(coredata, builddata, installdata), - #list_tests(testdata) + list_tests(testdata) ] outdict = {} diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 10eecd8e7..d5709f1bd 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -217,8 +217,11 @@ class MesonApp: else: coredata.update_cmd_line_file(self.build_dir, self.options) - # Generate an IDE introspection file with the exact same syntax as the introspection API defined in mintro - mintro.generate_introspection_file(b.environment.get_coredata(), b, b.get_tests(), b.get_benchmarks(), intr.backend.create_install_data()) + # Generate an IDE introspection file with the same syntax as the already existing API + intro_tests = intr.backend.create_test_serialisation(b.get_tests()) + intro_benchmarks = intr.backend.create_test_serialisation(b.get_benchmarks()) + intro_install = intr.backend.create_install_data() + mintro.generate_introspection_file(b.environment.get_coredata(), b, intro_tests, intro_benchmarks, intro_install) except: if 'cdf' in locals(): old_cdf = cdf + '.prev' From b2854e9edc8da3d945b4e53f80afc4c9f7129fd4 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 27 Nov 2018 21:40:31 +0100 Subject: [PATCH 09/63] Fixed rebase error --- mesonbuild/mintro.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 5ab1d1fee..4f0e9288f 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -469,12 +469,12 @@ def find_buildsystem_files_list(src_dir): for f in files: if f == 'meson.build' or f == 'meson_options.txt': filelist.append(os.path.relpath(os.path.join(root, f), src_dir)) - return ('buildsystem_files', filelist) + return filelist def list_buildsystem_files(builddata): src_dir = builddata.environment.get_source_dir() filelist = find_buildsystem_files_list(src_dir) - return filelist + return ('buildsystem_files', filelist) def list_deps(coredata): result = [] From 98eb7a48ab3de0122e8c5e8a188527d71a23a914 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 27 Nov 2018 22:27:34 +0100 Subject: [PATCH 10/63] Added unit test --- mesonbuild/mintro.py | 2 +- run_unittests.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 4f0e9288f..d37211235 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -663,4 +663,4 @@ def generate_introspection_file(coredata, builddata, testdata, benchmarkdata, in outfile = os.path.abspath(outfile) with open(outfile, 'w') as fp: - json.dump(outdict, fp, indent=2) + json.dump(outdict, fp) diff --git a/run_unittests.py b/run_unittests.py index 9259b03c8..9088a868e 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3110,6 +3110,36 @@ recommended as it is not supported on some platforms''') self.maxDiff = None self.assertListEqual(res_nb, res_wb) + def test_introspect_all_command(self): + testdir = os.path.join(self.common_test_dir, '6 linkshared') + self.init(testdir) + res = self.introspect('--all') + keylist = [ + 'benchmarks', + 'buildoptions', + 'buildsystem_files', + 'dependencies', + 'installed', + 'projectinfo', + 'targets', + 'tests' + ] + + for i in keylist: + self.assertIn(i, res) + + def test_introspect_file_dump_eauals_all(self): + testdir = os.path.join(self.common_test_dir, '6 linkshared') + self.init(testdir) + res_all = self.introspect('--all') + res_file = {} + + introfile = os.path.join(self.builddir, 'meson-introspection.json') + self.assertPathExists(introfile) + with open(introfile, 'r') as fp: + res_file = json.load(fp) + + self.assertEqual(res_all, res_file) class FailureTests(BasePlatformTests): ''' From a0d478da39dbab0ce265f5b4997ccf29baa87242 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 28 Nov 2018 12:28:35 +0100 Subject: [PATCH 11/63] More refactoring --- mesonbuild/mintro.py | 71 ++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index d37211235..9af9c6c00 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -245,6 +245,7 @@ def list_targets(coredata, builddata, installdata): tlist.append(t) return ('targets', tlist) +<<<<<<< HEAD def list_target_files(target_name, coredata, builddata): try: t = builddata.targets[target_name] @@ -405,6 +406,22 @@ def list_buildoptions_from_source(sourcedir, backend): # Reenable logging just in case mlog.enable() list_buildoptions(intr.coredata) +======= +def list_target_files(target_name, targets): + return ('error: TODO implement', []) + #try: + # t = builddata.targets[target_name] + # sources = t.sources + t.extra_files + #except KeyError: + # print("Unknown target %s." % target_name) + # sys.exit(1) + #out = [] + #for i in sources: + # if isinstance(i, mesonlib.File): + # i = os.path.join(i.subdir, i.fname) + # out.append(i) + #return ('target_files', out) +>>>>>>> More refactoring def list_buildoptions(coredata): optlist = [] @@ -581,8 +598,10 @@ def list_projinfo_from_source(sourcedir): def run(options): datadir = 'meson-private' + introfile = INTROSPECTION_OUTPUT_FILE if options.builddir is not None: datadir = os.path.join(options.builddir, datadir) + introfile = os.path.join(options.builddir, introfile) if options.builddir.endswith('/meson.build') or options.builddir.endswith('\\meson.build') or options.builddir == 'meson.build': sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11] if options.projectinfo: @@ -591,42 +610,42 @@ def run(options): if options.buildoptions: list_buildoptions_from_source(sourcedir, options.backend) return 0 - if not os.path.isdir(datadir): + if not os.path.isdir(datadir) or not os.path.isfile(introfile): print('Current directory is not a build dir. Please specify it or ' 'change the working directory to it.') return 1 - coredata = cdata.load(options.builddir) - builddata = build.load(options.builddir) - testdata = mtest.load_tests(options.builddir) - benchmarkdata = mtest.load_benchmarks(options.builddir) - - # Install data is only available with the Ninja backend - try: - installdata = ninjabackend.load(options.builddir) - except FileNotFoundError: - installdata = None + rawdata = {} + with open(introfile, 'r') as fp: + rawdata = json.load(fp) results = [] + toextract = [] - if options.all or options.list_targets: - results += [list_targets(coredata, builddata, installdata)] - if options.all or options.list_installed: - results += [list_installed(installdata)] - if options.target_files is not None: - results += [list_target_files(options.target_files, coredata, builddata)] - if options.all or options.buildsystem_files: - results += [list_buildsystem_files(builddata)] + if options.all or options.benchmarks: + toextract += ['benchmarks'] if options.all or options.buildoptions: + coredata = cdata.load(options.builddir) results += [list_buildoptions(coredata)] - if options.all or options.tests: - results += [list_tests(testdata)] - if options.all or options.benchmarks: - results += [list_benchmarks(benchmarkdata)] + if options.all or options.buildsystem_files: + toextract += ['buildsystem_files'] if options.all or options.dependencies: - results += [list_deps(coredata)] + toextract += ['dependencies'] + if options.all or options.list_installed: + toextract += ['installed'] if options.all or options.projectinfo: - results += [list_projinfo(builddata)] + toextract += ['projectinfo'] + if options.all or options.list_targets: + toextract += ['targets'] + if options.target_files is not None: + results += [list_target_files(options.target_files, rawdata['targets'])] + if options.all or options.tests: + toextract += ['tests'] + + for i in toextract: + if i not in rawdata: + raise RuntimeError('Key "{}" missing in introspection file. Please report this a bug.'.format(i)) + results += [(i, rawdata[i])] indent = options.indent if options.indent > 0 else None @@ -646,7 +665,7 @@ def run(options): def generate_introspection_file(coredata, builddata, testdata, benchmarkdata, installdata): intro_info = [ list_benchmarks(benchmarkdata), - list_buildoptions(coredata, builddata), + list_buildoptions(coredata), list_buildsystem_files(builddata), list_deps(coredata), list_installed(installdata), From 71d17b44e45c68b8d0b5189a8ebe8412ab99c622 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 28 Nov 2018 15:15:54 +0100 Subject: [PATCH 12/63] Fixed list_target_files and list_targets --- mesonbuild/compilers/cs.py | 2 +- mesonbuild/compilers/java.py | 2 +- mesonbuild/compilers/swift.py | 2 +- mesonbuild/mesonlib.py | 4 ++- mesonbuild/mintro.py | 68 ++++++++++++++++++----------------- 5 files changed, 41 insertions(+), 37 deletions(-) diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index a6c74d260..95b160393 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -82,7 +82,7 @@ class CsCompiler(Compiler): def get_std_exe_link_args(self): return [] - def get_include_args(self, path): + def get_include_args(self, path, is_system): return [] def get_pic_args(self): diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 978562ca1..686d2e051 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -63,7 +63,7 @@ class JavaCompiler(Compiler): def get_std_exe_link_args(self): return [] - def get_include_args(self, path): + def get_include_args(self, path, is_system): return [] def get_pic_args(self): diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 4d5dd0cfe..c156541d7 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -85,7 +85,7 @@ class SwiftCompiler(Compiler): def build_rpath_args(self, *args): return [] # FIXME - def get_include_args(self, dirname): + def get_include_args(self, dirname, is_system): return ['-I' + dirname] def get_compile_only_args(self): diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 98c2366d6..ef029405e 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -268,10 +268,12 @@ class File: def relative_name(self): return os.path.join(self.subdir, self.fname) -def get_compiler_for_source(compilers, src): +def get_compiler_for_source(compilers, src, canfail=False): for comp in compilers: if comp.can_compile(src): return comp + if canfail: + return None raise MesonException('No specified compiler can handle file {!s}'.format(src)) def classify_unity_sources(compilers, sources): diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 9af9c6c00..b6c00ec16 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -183,10 +183,12 @@ def list_targets(coredata, builddata, installdata): comp_list = target.compilers.values() source_list = target.sources + target.extra_files - source_list = list(map(lambda x: (mesonlib.get_compiler_for_source(comp_list, x), x), source_list)) + source_list = list(map(lambda x: (mesonlib.get_compiler_for_source(comp_list, x, True), x), source_list)) for comp, src in source_list: - if isinstance(comp, compilers.Compiler) and isinstance(src, mesonlib.File): + if isinstance(src, mesonlib.File): + src = os.path.join(src.subdir, src.fname) + if isinstance(comp, compilers.Compiler) and isinstance(src, str): lang = comp.get_language() if lang not in sources: parameters = [] @@ -208,7 +210,21 @@ def list_targets(coredata, builddata, installdata): 'source_files': [] } - sources[lang]['source_files'] += [os.path.join(src.subdir, src.fname)] + sources[lang]['source_files'] += [src] + elif comp is None and isinstance(src, str): + if 'unknown' not in sources: + sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': []} + sources['unknown']['source_files'] += [src] + elif isinstance(target, build.CustomTarget): + source_list_raw = target.sources + target.extra_files + source_list = [] + for i in source_list_raw: + if isinstance(i, mesonlib.File): + source_list += [os.path.join(i.subdir, i.fname)] + elif isinstance(i, str): + source_list += [i] + + sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': source_list} # Convert the dict to a list and add the language key. # This list approach will also work if the gurantee is removed that all @@ -245,21 +261,6 @@ def list_targets(coredata, builddata, installdata): tlist.append(t) return ('targets', tlist) -<<<<<<< HEAD -def list_target_files(target_name, coredata, builddata): - try: - t = builddata.targets[target_name] - sources = t.sources + t.extra_files - except KeyError: - print("Unknown target %s." % target_name) - sys.exit(1) - out = [] - for i in sources: - if isinstance(i, mesonlib.File): - i = os.path.join(i.subdir, i.fname) - out.append(i) - return ('target_files', out) - class BuildoptionsOptionHelper: # mimic an argparse namespace def __init__(self, cross_file): @@ -406,22 +407,23 @@ def list_buildoptions_from_source(sourcedir, backend): # Reenable logging just in case mlog.enable() list_buildoptions(intr.coredata) -======= + def list_target_files(target_name, targets): - return ('error: TODO implement', []) - #try: - # t = builddata.targets[target_name] - # sources = t.sources + t.extra_files - #except KeyError: - # print("Unknown target %s." % target_name) - # sys.exit(1) - #out = [] - #for i in sources: - # if isinstance(i, mesonlib.File): - # i = os.path.join(i.subdir, i.fname) - # out.append(i) - #return ('target_files', out) ->>>>>>> More refactoring + result = [] + tgt = None + + for i in targets: + if i['id'] == target_name: + tgt = i + break + + if tgt is None: + raise RuntimeError('Target with the ID "{}" could not be found'.format(target_name)) + + for i in tgt['sources']: + result += i['source_files'] + + return ('target_files', result) def list_buildoptions(coredata): optlist = [] From 74274e23ca42260e9be24c3cfaf7550c6c1505d2 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 28 Nov 2018 16:43:48 +0100 Subject: [PATCH 13/63] Moved the source and compiler generation to the backend --- mesonbuild/backend/backends.py | 146 +++++++++++++++++++++++++++++++- mesonbuild/mintro.py | 148 ++++----------------------------- mesonbuild/msetup.py | 2 +- 3 files changed, 161 insertions(+), 135 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8cc6f2f8a..d0c3e0a18 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -20,10 +20,11 @@ from .. import mesonlib from .. import mlog import json import subprocess +from ..dependencies import base from ..mesonlib import MesonException, OrderedSet from ..mesonlib import classify_unity_sources from ..mesonlib import File -from ..compilers import CompilerArgs, VisualStudioCCompiler +from ..compilers import CompilerArgs, VisualStudioCCompiler, compilers from collections import OrderedDict import shlex from functools import lru_cache @@ -1149,3 +1150,146 @@ class Backend: dst_dir = os.path.join(dst_dir, os.path.basename(src_dir)) d.install_subdirs.append([src_dir, dst_dir, sd.install_mode, sd.exclude]) + + def get_introspection_data(self, target_id, target): + ''' + Returns a list of source dicts with the following format for a given target: + [ + { + "language": "", + "compiler": ["result", "of", "comp.get_exelist()"], + "parameters": ["list", "of", "compiler", "parameters], + "source_files": ["list", "of", "all", "", "source", "files"] + } + ] + + This is a limited fallback / reference implementation. The backend should override this method. + ''' + def include_dirs_to_path(inc_dirs, src_root, build_root): + result = [] + if isinstance(inc_dirs, build.IncludeDirs): + for i in inc_dirs.get_incdirs(): + abs_src = os.path.join(src_root, inc_dirs.get_curdir(), i) + abs_build = os.path.join(build_root, inc_dirs.get_curdir(), i) + + if os.path.isdir(abs_src): + result += [abs_src] + + if os.path.isdir(abs_build): + result += [abs_build] + + return result + + def extract_dependency_infromation(dep): + inc_dirs = [] + args = [] + if isinstance(dep, base.Dependency): + reg = re.compile(r'-I(.*)') + for i in dep.get_compile_args(): + match = reg.match(i) + if match: + inc_dirs += [match.group(1)] + else: + args += [i] + + return inc_dirs, args + + def climb_stack(tgt, inc_dirs, extra_args, dep_args): + if isinstance(tgt, build.BuildTarget): + # The build directory is always in the include directories + absbase_src = os.path.join(src_root, tgt.subdir) + absbase_build = os.path.join(build_root, tgt.subdir) + inc_dirs += [absbase_src, absbase_build] + + for i in tgt.include_dirs: + inc_dirs += include_dirs_to_path(i, src_root, build_root) + + for i in tgt.external_deps: + dep_inc_dirs, args = extract_dependency_infromation(i) + inc_dirs += dep_inc_dirs + dep_args += args + + for i, comp in tgt.compilers.items(): + if isinstance(comp, compilers.Compiler): + if i not in extra_args: + extra_args[i] = [] + + extra_args[i] += tgt.get_extra_args(i) + extra_args[i] += self.build.get_global_args(comp, tgt.is_cross) + extra_args[i] += self.build.get_project_args(comp, tgt.subproject, tgt.is_cross) + + for i in tgt.link_targets: + climb_stack(i, inc_dirs, extra_args, dep_args) + + src_root = self.build.environment.get_source_dir() + build_root = self.build.environment.get_build_dir() + + inc_dirs = [] + extra_args = {} + dep_args = [] + sources = {} + + if isinstance(target, build.BuildTarget): + climb_stack(target, inc_dirs, extra_args, dep_args) + + # Add the dep_args, sort and remove duplicates + for i in extra_args: + extra_args[i] += dep_args + extra_args[i] = list(sorted(list(set(extra_args[i])))) + + # Remove duplicates, sort and make paths pretty + inc_dirs = list(sorted(list(set(inc_dirs)))) + inc_dirs = list(map(lambda x: os.path.realpath(x), inc_dirs)) + + comp_list = target.compilers.values() + source_list = target.sources + target.extra_files + source_list = list(map(lambda x: (mesonlib.get_compiler_for_source(comp_list, x, True), x), source_list)) + + for comp, src in source_list: + if isinstance(src, mesonlib.File): + src = os.path.join(src.subdir, src.fname) + if isinstance(comp, compilers.Compiler) and isinstance(src, str): + lang = comp.get_language() + if lang not in sources: + parameters = [] + + # Generate include directories + # Not all compilers have the get_include_args method + get_include_args = getattr(comp, 'get_include_args', None) + if callable(get_include_args): + for i in inc_dirs: + parameters += comp.get_include_args(i, False) + + # Extra args + if lang in extra_args: + parameters += extra_args[lang] + + sources[lang] = { + 'compiler': comp.get_exelist(), + 'parameters': parameters, + 'source_files': [] + } + + sources[lang]['source_files'] += [src] + elif comp is None and isinstance(src, str): + if 'unknown' not in sources: + sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': []} + sources['unknown']['source_files'] += [src] + elif isinstance(target, build.CustomTarget): + source_list_raw = target.sources + target.extra_files + source_list = [] + for i in source_list_raw: + if isinstance(i, mesonlib.File): + source_list += [os.path.join(i.subdir, i.fname)] + elif isinstance(i, str): + source_list += [i] + + sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': source_list} + + # Convert the dict to a list and add the language key. + # This list approach will also work if the gurantee is removed that all + # files in a target are compiled with the same parameters + # see https://github.com/mesonbuild/meson/pull/4547 + sources = list(map(lambda x: {'language': x[0], **x[1]}, sources.items())) + + return sources diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index b6c00ec16..449220ca9 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -20,7 +20,7 @@ Currently only works for the Ninja backend. Others use generated project files and don't need this info.""" import json -from . import build, mtest, coredata as cdata +from . import build, coredata as cdata from . import environment from . import mesonlib from . import astinterpreter @@ -34,7 +34,6 @@ from .dependencies import base from .compilers import compilers import sys, os import pathlib -import re INTROSPECTION_OUTPUT_FILE = 'meson-introspection.json' @@ -99,138 +98,16 @@ def list_installed(installdata): res[path] = os.path.join(installdata.prefix, installpath) return ('installed', res) -def include_dirs_to_path(inc_dirs, src_root, build_root): - result = [] - if isinstance(inc_dirs, build.IncludeDirs): - for i in inc_dirs.get_incdirs(): - abs_src = os.path.join(src_root, inc_dirs.get_curdir(), i) - abs_build = os.path.join(build_root, inc_dirs.get_curdir(), i) - - if os.path.isdir(abs_src): - result += [abs_src] - - if os.path.isdir(abs_build): - result += [abs_build] - - return result - -def extract_dependency_infromation(dep): - inc_dirs = [] - args = [] - if isinstance(dep, base.Dependency): - reg = re.compile(r'-I(.*)') - for i in dep.get_compile_args(): - match = reg.match(i) - if match: - inc_dirs += [match.group(1)] - else: - args += [i] - - return inc_dirs, args - -def list_targets(coredata, builddata, installdata): +def list_targets(builddata: build.Build, installdata, backend: backends.Backend): tlist = [] for (idname, target) in builddata.get_targets().items(): if not isinstance(target, build.Target): raise RuntimeError('Something weird happened. File a bug.') - src_root = builddata.environment.get_source_dir() - build_root = builddata.environment.get_build_dir() - - inc_dirs = [] - extra_args = {} - dep_args = [] - sources = {} - - def climb_stack(tgt, inc_dirs, extra_args, dep_args): - if isinstance(tgt, build.BuildTarget): - # The build directory is always in the include directories - absbase_src = os.path.join(src_root, tgt.subdir) - absbase_build = os.path.join(build_root, tgt.subdir) - inc_dirs += [absbase_src, absbase_build] - - for i in tgt.include_dirs: - inc_dirs += include_dirs_to_path(i, src_root, build_root) - - for i in tgt.external_deps: - dep_inc_dirs, args = extract_dependency_infromation(i) - inc_dirs += dep_inc_dirs - dep_args += args - - for i, comp in tgt.compilers.items(): - if isinstance(comp, compilers.Compiler): - if i not in extra_args: - extra_args[i] = [] - - extra_args[i] += tgt.get_extra_args(i) - extra_args[i] += builddata.get_global_args(comp, tgt.is_cross) - extra_args[i] += builddata.get_project_args(comp, tgt.subproject, tgt.is_cross) - - for i in tgt.link_targets: - climb_stack(i, inc_dirs, extra_args, dep_args) - - if isinstance(target, build.BuildTarget): - climb_stack(target, inc_dirs, extra_args, dep_args) - - # Add the dep_args, sort and remove duplicates - for i in extra_args: - extra_args[i] += dep_args - extra_args[i] = list(sorted(list(set(extra_args[i])))) - - # Remove duplicates, sort and make paths pretty - inc_dirs = list(sorted(list(set(inc_dirs)))) - inc_dirs = list(map(lambda x: os.path.realpath(x), inc_dirs)) - - comp_list = target.compilers.values() - source_list = target.sources + target.extra_files - source_list = list(map(lambda x: (mesonlib.get_compiler_for_source(comp_list, x, True), x), source_list)) - - for comp, src in source_list: - if isinstance(src, mesonlib.File): - src = os.path.join(src.subdir, src.fname) - if isinstance(comp, compilers.Compiler) and isinstance(src, str): - lang = comp.get_language() - if lang not in sources: - parameters = [] - - # Generate include directories - # Not all compilers have the get_include_args method - get_include_args = getattr(comp, 'get_include_args', None) - if callable(get_include_args): - for i in inc_dirs: - parameters += comp.get_include_args(i, False) - - # Extra args - if lang in extra_args: - parameters += extra_args[lang] - - sources[lang] = { - 'compiler': comp.get_exelist(), - 'parameters': parameters, - 'source_files': [] - } - - sources[lang]['source_files'] += [src] - elif comp is None and isinstance(src, str): - if 'unknown' not in sources: - sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': []} - sources['unknown']['source_files'] += [src] - elif isinstance(target, build.CustomTarget): - source_list_raw = target.sources + target.extra_files - source_list = [] - for i in source_list_raw: - if isinstance(i, mesonlib.File): - source_list += [os.path.join(i.subdir, i.fname)] - elif isinstance(i, str): - source_list += [i] - - sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': source_list} - - # Convert the dict to a list and add the language key. - # This list approach will also work if the gurantee is removed that all - # files in a target are compiled with the same parameters - # see https://github.com/mesonbuild/meson/pull/4547 - sources = list(map(lambda x: {'language': x[0], **x[1]}, sources.items())) + if isinstance(backend, backends.Backend): + sources = backend.get_introspection_data(idname, target) + else: + raise RuntimeError('Parameter backend has an invalid type. This is a bug.') t = {'name': target.get_basename(), 'id': idname, 'sources': sources} fname = target.get_filename() @@ -490,7 +367,7 @@ def find_buildsystem_files_list(src_dir): filelist.append(os.path.relpath(os.path.join(root, f), src_dir)) return filelist -def list_buildsystem_files(builddata): +def list_buildsystem_files(builddata: build.Build): src_dir = builddata.environment.get_source_dir() filelist = find_buildsystem_files_list(src_dir) return ('buildsystem_files', filelist) @@ -531,7 +408,7 @@ def list_tests(testdata): def list_benchmarks(benchdata): return ('benchmarks', get_test_list(benchdata)) -def list_projinfo(builddata): +def list_projinfo(builddata: build.Build): result = {'version': builddata.project_version, 'descriptive_name': builddata.project_name} subprojects = [] @@ -664,7 +541,12 @@ def run(options): print(json.dumps(out, indent=indent)) return 0 -def generate_introspection_file(coredata, builddata, testdata, benchmarkdata, installdata): +def generate_introspection_file(builddata: build.Build, backend: backends.Backend): + coredata = builddata.environment.get_coredata() + benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) + testdata = backend.create_test_serialisation(builddata.get_tests()) + installdata = backend.create_install_data() + intro_info = [ list_benchmarks(benchmarkdata), list_buildoptions(coredata), @@ -672,7 +554,7 @@ def generate_introspection_file(coredata, builddata, testdata, benchmarkdata, in list_deps(coredata), list_installed(installdata), list_projinfo(builddata), - list_targets(coredata, builddata, installdata), + list_targets(builddata, installdata, backend), list_tests(testdata) ] diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index d5709f1bd..88c92517d 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -221,7 +221,7 @@ class MesonApp: intro_tests = intr.backend.create_test_serialisation(b.get_tests()) intro_benchmarks = intr.backend.create_test_serialisation(b.get_benchmarks()) intro_install = intr.backend.create_install_data() - mintro.generate_introspection_file(b.environment.get_coredata(), b, intro_tests, intro_benchmarks, intro_install) + mintro.generate_introspection_file(b, intr.backend) except: if 'cdf' in locals(): old_cdf = cdf + '.prev' From a5be893b19daf9e1f08eacc5d7f01389f6e40956 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 28 Nov 2018 19:40:16 +0100 Subject: [PATCH 14/63] Some code cleanup --- mesonbuild/backend/backends.py | 95 +++++++++++++++++++++++++--------- mesonbuild/build.py | 12 +++++ mesonbuild/mintro.py | 31 ++++------- mesonbuild/msetup.py | 6 ++- 4 files changed, 98 insertions(+), 46 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d0c3e0a18..9a377d49b 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -159,6 +159,7 @@ class Backend: self.processed_targets = {} self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) + self.introspect_data_cache = None # Cache to speed up get_introspection_data def get_target_filename(self, t): if isinstance(t, build.CustomTarget): @@ -1165,20 +1166,10 @@ class Backend: This is a limited fallback / reference implementation. The backend should override this method. ''' - def include_dirs_to_path(inc_dirs, src_root, build_root): - result = [] - if isinstance(inc_dirs, build.IncludeDirs): - for i in inc_dirs.get_incdirs(): - abs_src = os.path.join(src_root, inc_dirs.get_curdir(), i) - abs_build = os.path.join(build_root, inc_dirs.get_curdir(), i) + if not self.introspect_data_cache: + self.introspect_data_cache = {'targets': {}, 'dependencies': {}} - if os.path.isdir(abs_src): - result += [abs_src] - - if os.path.isdir(abs_build): - result += [abs_build] - - return result + reg = re.compile(r'-I(.*)') def extract_dependency_infromation(dep): inc_dirs = [] @@ -1194,21 +1185,64 @@ class Backend: return inc_dirs, args - def climb_stack(tgt, inc_dirs, extra_args, dep_args): + def process_target(tgt, src_root, build_root): if isinstance(tgt, build.BuildTarget): + # First check if the target is cached + tgtid = tgt.get_id() + if tgtid in self.introspect_data_cache['targets']: + c_tgt = self.introspect_data_cache['targets'][tgtid] + return c_tgt['inc_dirs'], c_tgt['dep_args'], c_tgt['extra_args'] + # The build directory is always in the include directories absbase_src = os.path.join(src_root, tgt.subdir) absbase_build = os.path.join(build_root, tgt.subdir) - inc_dirs += [absbase_src, absbase_build] + inc_dirs = [absbase_src, absbase_build] + dep_args = [] + extra_args = {} + # Handle include directories for i in tgt.include_dirs: - inc_dirs += include_dirs_to_path(i, src_root, build_root) + if isinstance(i, build.IncludeDirs): + for j in i.get_incdirs(): + abs_src = os.path.join(absbase_src, i.get_curdir(), j) + abs_build = os.path.join(absbase_build, i.get_curdir(), j) + + if os.path.isdir(abs_src): + inc_dirs += [abs_src] + + if os.path.isdir(abs_build): + inc_dirs += [abs_build] + # Handle dependencies for i in tgt.external_deps: - dep_inc_dirs, args = extract_dependency_infromation(i) + if not isinstance(i, base.Dependency): + continue + + did = id(i) + if did in self.introspect_data_cache['dependencies']: + c_entry = self.introspect_data_cache['dependencies'][did] + inc_dirs += c_entry['dep_inc_dirs'] + dep_args += c_entry['dep_cur_args'] + continue + + dep_inc_dirs = [] + dep_cur_args = [] + + for i in i.get_compile_args(): + match = reg.match(i) + if match: + dep_inc_dirs += [match.group(1)] + else: + dep_cur_args += [i] + + self.introspect_data_cache['dependencies'][did] = { + 'dep_inc_dirs': dep_inc_dirs, + 'dep_cur_args': dep_cur_args + } inc_dirs += dep_inc_dirs - dep_args += args + dep_args += dep_cur_args + # Check for language specific extra args for i, comp in tgt.compilers.items(): if isinstance(comp, compilers.Compiler): if i not in extra_args: @@ -1218,19 +1252,32 @@ class Backend: extra_args[i] += self.build.get_global_args(comp, tgt.is_cross) extra_args[i] += self.build.get_project_args(comp, tgt.subproject, tgt.is_cross) + # Recursively check the other targets for i in tgt.link_targets: - climb_stack(i, inc_dirs, extra_args, dep_args) + t_inc_dirs, t_dep_args, t_extra_args = process_target(i, src_root, build_root) + inc_dirs += t_inc_dirs + dep_args += t_dep_args + for ind, arglist in t_extra_args.items(): + if ind in extra_args: + extra_args[ind] += arglist + else: + extra_args[ind] = arglist + + # Update the cache + self.introspect_data_cache['targets'][tgtid] = { + 'inc_dirs': inc_dirs, + 'dep_args': dep_args, + 'extra_args': extra_args + } + + return inc_dirs, dep_args, extra_args src_root = self.build.environment.get_source_dir() build_root = self.build.environment.get_build_dir() - - inc_dirs = [] - extra_args = {} - dep_args = [] sources = {} if isinstance(target, build.BuildTarget): - climb_stack(target, inc_dirs, extra_args, dep_args) + inc_dirs, dep_args, extra_args = process_target(target, src_root, build_root) # Add the dep_args, sort and remove duplicates for i in extra_args: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5d0fefa0b..1054451d9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -345,6 +345,7 @@ a hard error in the future.''' % name) self.install = False self.build_always_stale = False self.option_overrides = {} + self.typename = 'unknown target' def get_install_dir(self, environment): # Find the installation directory. @@ -366,6 +367,9 @@ a hard error in the future.''' % name) def get_subdir(self): return self.subdir + def get_typename(self): + return self.typename + @staticmethod def _get_id_hash(target_id): # We don't really need cryptographic security here. @@ -1364,6 +1368,7 @@ class Executable(BuildTarget): if 'pie' not in kwargs and 'b_pie' in environment.coredata.base_options: kwargs['pie'] = environment.coredata.base_options['b_pie'].value super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) + self.typename = 'executable' # Unless overridden, executables have no suffix or prefix. Except on # Windows and with C#/Mono executables where the suffix is 'exe' if not hasattr(self, 'prefix'): @@ -1453,6 +1458,7 @@ class StaticLibrary(BuildTarget): if 'pic' not in kwargs and 'b_staticpic' in environment.coredata.base_options: kwargs['pic'] = environment.coredata.base_options['b_staticpic'].value super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) + self.typename = 'static library' if 'cs' in self.compilers: raise InvalidArguments('Static libraries not supported for C#.') if 'rust' in self.compilers: @@ -1509,6 +1515,7 @@ class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): + self.typename = 'shared library' self.soversion = None self.ltversion = None # Max length 2, first element is compatibility_version, second is current_version @@ -1817,6 +1824,7 @@ class SharedModule(SharedLibrary): if 'soversion' in kwargs: raise MesonException('Shared modules must not specify the soversion kwarg.') super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) + self.typename = 'shared module' def get_default_install_dir(self, environment): return environment.get_shared_module_dir() @@ -1843,6 +1851,7 @@ class CustomTarget(Target): def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False): super().__init__(name, subdir, subproject, False) + self.typename = 'custom' self.dependencies = [] self.extra_depends = [] self.depend_files = [] # Files that this target depends on but are not on the command line. @@ -2084,6 +2093,7 @@ class CustomTarget(Target): class RunTarget(Target): def __init__(self, name, command, args, dependencies, subdir, subproject): super().__init__(name, subdir, subproject, False) + self.typename = 'run' self.command = command self.args = args self.dependencies = dependencies @@ -2124,6 +2134,7 @@ class Jar(BuildTarget): for t in self.link_targets: if not isinstance(t, Jar): raise InvalidArguments('Link target %s is not a jar target.' % t) + self.typename = 'jar' self.filename = self.name + '.jar' self.outputs = [self.filename] self.java_args = kwargs.get('java_args', []) @@ -2160,6 +2171,7 @@ class CustomTargetIndex: """ def __init__(self, target, output): + self.typename = 'custom' self.target = target self.output = output diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 449220ca9..6768070ed 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -104,37 +104,26 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) if not isinstance(target, build.Target): raise RuntimeError('Something weird happened. File a bug.') - if isinstance(backend, backends.Backend): - sources = backend.get_introspection_data(idname, target) - else: - raise RuntimeError('Parameter backend has an invalid type. This is a bug.') - - t = {'name': target.get_basename(), 'id': idname, 'sources': sources} fname = target.get_filename() if isinstance(fname, list): fname = [os.path.join(target.subdir, x) for x in fname] else: fname = os.path.join(target.subdir, fname) - t['filename'] = fname - if isinstance(target, build.Executable): - typename = 'executable' - elif isinstance(target, build.SharedLibrary): - typename = 'shared library' - elif isinstance(target, build.StaticLibrary): - typename = 'static library' - elif isinstance(target, build.CustomTarget): - typename = 'custom' - elif isinstance(target, build.RunTarget): - typename = 'run' - else: - typename = 'unknown' - t['type'] = typename + + t = { + 'name': target.get_basename(), + 'id': idname, + 'type': target.get_typename(), + 'filename': fname, + 'build_by_default': target.build_by_default, + 'sources': backend.get_introspection_data(idname, target) + } + if installdata and target.should_install(): t['installed'] = True t['install_filename'] = determine_installed_path(target, installdata) else: t['installed'] = False - t['build_by_default'] = target.build_by_default tlist.append(t) return ('targets', tlist) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 88c92517d..491ea2fc8 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -221,7 +221,11 @@ class MesonApp: intro_tests = intr.backend.create_test_serialisation(b.get_tests()) intro_benchmarks = intr.backend.create_test_serialisation(b.get_benchmarks()) intro_install = intr.backend.create_install_data() - mintro.generate_introspection_file(b, intr.backend) + if self.options.profile: + fname = os.path.join(self.build_dir, 'meson-private', 'profile-introspector.log') + profile.runctx('mintro.generate_introspection_file(b, intr.backend)', globals(), locals(), filename=fname) + else: + mintro.generate_introspection_file(b, intr.backend) except: if 'cdf' in locals(): old_cdf = cdf + '.prev' From 4addd176dbf3eca01f5b3058e48f502c69268cbd Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 28 Nov 2018 22:47:53 +0100 Subject: [PATCH 15/63] Removed fallback detection logic --- mesonbuild/backend/backends.py | 184 +++------------------------------ mesonbuild/mesonlib.py | 4 +- mesonbuild/mintro.py | 14 ++- 3 files changed, 29 insertions(+), 173 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 9a377d49b..b36b98c9d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -20,11 +20,10 @@ from .. import mesonlib from .. import mlog import json import subprocess -from ..dependencies import base from ..mesonlib import MesonException, OrderedSet from ..mesonlib import classify_unity_sources from ..mesonlib import File -from ..compilers import CompilerArgs, VisualStudioCCompiler, compilers +from ..compilers import CompilerArgs, VisualStudioCCompiler from collections import OrderedDict import shlex from functools import lru_cache @@ -159,7 +158,6 @@ class Backend: self.processed_targets = {} self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) - self.introspect_data_cache = None # Cache to speed up get_introspection_data def get_target_filename(self, t): if isinstance(t, build.CustomTarget): @@ -1166,163 +1164,7 @@ class Backend: This is a limited fallback / reference implementation. The backend should override this method. ''' - if not self.introspect_data_cache: - self.introspect_data_cache = {'targets': {}, 'dependencies': {}} - - reg = re.compile(r'-I(.*)') - - def extract_dependency_infromation(dep): - inc_dirs = [] - args = [] - if isinstance(dep, base.Dependency): - reg = re.compile(r'-I(.*)') - for i in dep.get_compile_args(): - match = reg.match(i) - if match: - inc_dirs += [match.group(1)] - else: - args += [i] - - return inc_dirs, args - - def process_target(tgt, src_root, build_root): - if isinstance(tgt, build.BuildTarget): - # First check if the target is cached - tgtid = tgt.get_id() - if tgtid in self.introspect_data_cache['targets']: - c_tgt = self.introspect_data_cache['targets'][tgtid] - return c_tgt['inc_dirs'], c_tgt['dep_args'], c_tgt['extra_args'] - - # The build directory is always in the include directories - absbase_src = os.path.join(src_root, tgt.subdir) - absbase_build = os.path.join(build_root, tgt.subdir) - inc_dirs = [absbase_src, absbase_build] - dep_args = [] - extra_args = {} - - # Handle include directories - for i in tgt.include_dirs: - if isinstance(i, build.IncludeDirs): - for j in i.get_incdirs(): - abs_src = os.path.join(absbase_src, i.get_curdir(), j) - abs_build = os.path.join(absbase_build, i.get_curdir(), j) - - if os.path.isdir(abs_src): - inc_dirs += [abs_src] - - if os.path.isdir(abs_build): - inc_dirs += [abs_build] - - # Handle dependencies - for i in tgt.external_deps: - if not isinstance(i, base.Dependency): - continue - - did = id(i) - if did in self.introspect_data_cache['dependencies']: - c_entry = self.introspect_data_cache['dependencies'][did] - inc_dirs += c_entry['dep_inc_dirs'] - dep_args += c_entry['dep_cur_args'] - continue - - dep_inc_dirs = [] - dep_cur_args = [] - - for i in i.get_compile_args(): - match = reg.match(i) - if match: - dep_inc_dirs += [match.group(1)] - else: - dep_cur_args += [i] - - self.introspect_data_cache['dependencies'][did] = { - 'dep_inc_dirs': dep_inc_dirs, - 'dep_cur_args': dep_cur_args - } - inc_dirs += dep_inc_dirs - dep_args += dep_cur_args - - # Check for language specific extra args - for i, comp in tgt.compilers.items(): - if isinstance(comp, compilers.Compiler): - if i not in extra_args: - extra_args[i] = [] - - extra_args[i] += tgt.get_extra_args(i) - extra_args[i] += self.build.get_global_args(comp, tgt.is_cross) - extra_args[i] += self.build.get_project_args(comp, tgt.subproject, tgt.is_cross) - - # Recursively check the other targets - for i in tgt.link_targets: - t_inc_dirs, t_dep_args, t_extra_args = process_target(i, src_root, build_root) - inc_dirs += t_inc_dirs - dep_args += t_dep_args - for ind, arglist in t_extra_args.items(): - if ind in extra_args: - extra_args[ind] += arglist - else: - extra_args[ind] = arglist - - # Update the cache - self.introspect_data_cache['targets'][tgtid] = { - 'inc_dirs': inc_dirs, - 'dep_args': dep_args, - 'extra_args': extra_args - } - - return inc_dirs, dep_args, extra_args - - src_root = self.build.environment.get_source_dir() - build_root = self.build.environment.get_build_dir() - sources = {} - - if isinstance(target, build.BuildTarget): - inc_dirs, dep_args, extra_args = process_target(target, src_root, build_root) - - # Add the dep_args, sort and remove duplicates - for i in extra_args: - extra_args[i] += dep_args - extra_args[i] = list(sorted(list(set(extra_args[i])))) - - # Remove duplicates, sort and make paths pretty - inc_dirs = list(sorted(list(set(inc_dirs)))) - inc_dirs = list(map(lambda x: os.path.realpath(x), inc_dirs)) - - comp_list = target.compilers.values() - source_list = target.sources + target.extra_files - source_list = list(map(lambda x: (mesonlib.get_compiler_for_source(comp_list, x, True), x), source_list)) - - for comp, src in source_list: - if isinstance(src, mesonlib.File): - src = os.path.join(src.subdir, src.fname) - if isinstance(comp, compilers.Compiler) and isinstance(src, str): - lang = comp.get_language() - if lang not in sources: - parameters = [] - - # Generate include directories - # Not all compilers have the get_include_args method - get_include_args = getattr(comp, 'get_include_args', None) - if callable(get_include_args): - for i in inc_dirs: - parameters += comp.get_include_args(i, False) - - # Extra args - if lang in extra_args: - parameters += extra_args[lang] - - sources[lang] = { - 'compiler': comp.get_exelist(), - 'parameters': parameters, - 'source_files': [] - } - - sources[lang]['source_files'] += [src] - elif comp is None and isinstance(src, str): - if 'unknown' not in sources: - sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': []} - sources['unknown']['source_files'] += [src] - elif isinstance(target, build.CustomTarget): + if isinstance(target, (build.CustomTarget, build.BuildTarget)): source_list_raw = target.sources + target.extra_files source_list = [] for i in source_list_raw: @@ -1331,12 +1173,16 @@ class Backend: elif isinstance(i, str): source_list += [i] - sources['unknown'] = {'compiler': [], 'parameters': [], 'source_files': source_list} - - # Convert the dict to a list and add the language key. - # This list approach will also work if the gurantee is removed that all - # files in a target are compiled with the same parameters - # see https://github.com/mesonbuild/meson/pull/4547 - sources = list(map(lambda x: {'language': x[0], **x[1]}, sources.items())) - - return sources + return [{ + 'language': 'unknown', + 'compiler': [], + 'parameters': [], + 'source_files': source_list + }] + + return [{ + 'language': 'unknown', + 'compiler': [], + 'parameters': [], + 'source_files': [] + }] diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index ef029405e..98c2366d6 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -268,12 +268,10 @@ class File: def relative_name(self): return os.path.join(self.subdir, self.fname) -def get_compiler_for_source(compilers, src, canfail=False): +def get_compiler_for_source(compilers, src): for comp in compilers: if comp.can_compile(src): return comp - if canfail: - return None raise MesonException('No specified compiler can handle file {!s}'.format(src)) def classify_unity_sources(compilers, sources): diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 6768070ed..97a7c9474 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -100,6 +100,13 @@ def list_installed(installdata): def list_targets(builddata: build.Build, installdata, backend: backends.Backend): tlist = [] + + # Fast lookup table for installation files + intall_lookuptable = {} + for i in installdata.targets: + outname = os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname)) + intall_lookuptable[os.path.basename(i.fname)] = str(pathlib.PurePath(outname)) + for (idname, target) in builddata.get_targets().items(): if not isinstance(target, build.Target): raise RuntimeError('Something weird happened. File a bug.') @@ -121,7 +128,12 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) if installdata and target.should_install(): t['installed'] = True - t['install_filename'] = determine_installed_path(target, installdata) + t['install_filename'] = [] + + for i in target.outputs: + fname = intall_lookuptable.get(i) + if i is not None: + t['install_filename'] += [fname] else: t['installed'] = False tlist.append(t) From 24d668bea06b3b953855fa12ef2f5b417d4dd163 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 28 Nov 2018 23:05:40 +0100 Subject: [PATCH 16/63] Removed determine_installed_path since it is no longer needed --- mesonbuild/mintro.py | 20 +------------------- run_unittests.py | 2 +- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 97a7c9474..326cd6ca1 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -66,24 +66,6 @@ def add_arguments(parser): help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') -def determine_installed_path(target, installdata): - install_targets = [] - for i in target.outputs: - for j in installdata.targets: - if os.path.basename(j.fname) == i: # FIXME, might clash due to subprojects. - install_targets += [j] - break - if len(install_targets) == 0: - raise RuntimeError('Something weird happened. File a bug.') - - # Normalize the path by using os.path.sep consistently, etc. - # Does not change the effective path. - install_targets = list(map(lambda x: os.path.join(installdata.prefix, x.outdir, os.path.basename(x.fname)), install_targets)) - install_targets = list(map(lambda x: str(pathlib.PurePath(x)), install_targets)) - - return install_targets - - def list_installed(installdata): res = {} if installdata is not None: @@ -132,7 +114,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) for i in target.outputs: fname = intall_lookuptable.get(i) - if i is not None: + if fname is not None: t['install_filename'] += [fname] else: t['installed'] = False diff --git a/run_unittests.py b/run_unittests.py index 9088a868e..9120a43a7 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1221,7 +1221,7 @@ class BasePlatformTests(unittest.TestCase): self.assertEqual(PurePath(path1), PurePath(path2)) def assertPathListEqual(self, pathlist1, pathlist2): - self.assertEquals(len(pathlist1), len(pathlist2)) + self.assertEqual(len(pathlist1), len(pathlist2)) worklist = list(zip(pathlist1, pathlist2)) for i in worklist: self.assertPathEqual(i[0], i[1]) From c4eb5c79fe89ca49dd9d9f5bd72be99d919dd2a0 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 12:56:52 +0100 Subject: [PATCH 17/63] Added unit test --- mesonbuild/build.py | 2 +- mesonbuild/compilers/cs.py | 2 +- mesonbuild/compilers/java.py | 2 +- mesonbuild/compilers/swift.py | 2 +- mesonbuild/msetup.py | 3 - run_unittests.py | 145 ++++++++++++++++-- test cases/unit/49 introspection/meson.build | 14 ++ .../49 introspection/sharedlib/meson.build | 2 + .../49 introspection/sharedlib/shared.cpp | 9 ++ .../49 introspection/sharedlib/shared.hpp | 10 ++ .../49 introspection/staticlib/meson.build | 2 + .../unit/49 introspection/staticlib/static.c | 5 + .../unit/49 introspection/staticlib/static.h | 3 + test cases/unit/49 introspection/t1.cpp | 13 ++ test cases/unit/49 introspection/t2.cpp | 8 + test cases/unit/49 introspection/t3.cpp | 16 ++ 16 files changed, 216 insertions(+), 22 deletions(-) create mode 100644 test cases/unit/49 introspection/meson.build create mode 100644 test cases/unit/49 introspection/sharedlib/meson.build create mode 100644 test cases/unit/49 introspection/sharedlib/shared.cpp create mode 100644 test cases/unit/49 introspection/sharedlib/shared.hpp create mode 100644 test cases/unit/49 introspection/staticlib/meson.build create mode 100644 test cases/unit/49 introspection/staticlib/static.c create mode 100644 test cases/unit/49 introspection/staticlib/static.h create mode 100644 test cases/unit/49 introspection/t1.cpp create mode 100644 test cases/unit/49 introspection/t2.cpp create mode 100644 test cases/unit/49 introspection/t3.cpp diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1054451d9..4fa6bde63 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1515,7 +1515,6 @@ class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): - self.typename = 'shared library' self.soversion = None self.ltversion = None # Max length 2, first element is compatibility_version, second is current_version @@ -1528,6 +1527,7 @@ class SharedLibrary(BuildTarget): # The import library that GCC would generate (and prefer) self.gcc_import_filename = None super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) + self.typename = 'shared library' if 'rust' in self.compilers: # If no crate type is specified, or it's the generic lib type, use dylib if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib': diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index 95b160393..a6c74d260 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -82,7 +82,7 @@ class CsCompiler(Compiler): def get_std_exe_link_args(self): return [] - def get_include_args(self, path, is_system): + def get_include_args(self, path): return [] def get_pic_args(self): diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 686d2e051..978562ca1 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -63,7 +63,7 @@ class JavaCompiler(Compiler): def get_std_exe_link_args(self): return [] - def get_include_args(self, path, is_system): + def get_include_args(self, path): return [] def get_pic_args(self): diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index c156541d7..4d5dd0cfe 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -85,7 +85,7 @@ class SwiftCompiler(Compiler): def build_rpath_args(self, *args): return [] # FIXME - def get_include_args(self, dirname, is_system): + def get_include_args(self, dirname): return ['-I' + dirname] def get_compile_only_args(self): diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 491ea2fc8..402f756e9 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -218,9 +218,6 @@ class MesonApp: coredata.update_cmd_line_file(self.build_dir, self.options) # Generate an IDE introspection file with the same syntax as the already existing API - intro_tests = intr.backend.create_test_serialisation(b.get_tests()) - intro_benchmarks = intr.backend.create_test_serialisation(b.get_benchmarks()) - intro_install = intr.backend.create_install_data() if self.options.profile: fname = os.path.join(self.build_dir, 'meson-private', 'profile-introspector.log') profile.runctx('mintro.generate_introspection_file(b, intr.backend)', globals(), locals(), filename=fname) diff --git a/run_unittests.py b/run_unittests.py index 9120a43a7..811df6bd3 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3110,26 +3110,141 @@ recommended as it is not supported on some platforms''') self.maxDiff = None self.assertListEqual(res_nb, res_wb) - def test_introspect_all_command(self): - testdir = os.path.join(self.common_test_dir, '6 linkshared') + def test_introspect_json_dump(self): + testdir = os.path.join(self.unit_test_dir, '49 introspection') self.init(testdir) - res = self.introspect('--all') - keylist = [ - 'benchmarks', - 'buildoptions', - 'buildsystem_files', - 'dependencies', - 'installed', - 'projectinfo', - 'targets', - 'tests' + introfile = os.path.join(self.builddir, 'meson-introspection.json') + self.assertPathExists(introfile) + with open(introfile, 'r') as fp: + res = json.load(fp) + + def assertKeyTypes(key_type_list, obj): + for i in key_type_list: + self.assertIn(i[0], obj) + self.assertIsInstance(obj[i[0]], i[1]) + + root_keylist = [ + ('benchmarks', list), + ('buildoptions', list), + ('buildsystem_files', list), + ('dependencies', list), + ('installed', dict), + ('projectinfo', dict), + ('targets', list), + ('tests', list), + ] + + test_keylist = [ + ('cmd', list), + ('env', dict), + ('name', str), + ('timeout', int), + ('suite', list), + ('is_parallel', bool), + ] + + buildoptions_keylist = [ + ('name', str), + ('section', str), + ('type', str), + ('description', str), + ] + + buildoptions_typelist = [ + ('combo', str, [('choices', list)]), + ('string', str, []), + ('boolean', bool, []), + ('integer', int, []), + ('array', list, []), + ] + + dependencies_typelist = [ + ('name', str), + ('compile_args', list), + ('link_args', list), + ] + + targets_typelist = [ + ('name', str), + ('id', str), + ('type', str), + ('filename', str), + ('build_by_default', bool), + ('sources', list), + ('installed', bool), + ] + + targets_sources_typelist = [ + ('language', str), + ('compiler', list), + ('parameters', list), + ('source_files', list), ] - for i in keylist: - self.assertIn(i, res) + assertKeyTypes(root_keylist, res) + + # Check Tests and benchmarks + tests_to_find = ['test case 1', 'test case 2', 'benchmark 1'] + for i in res['benchmarks'] + res['tests']: + assertKeyTypes(test_keylist, i) + if i['name'] in tests_to_find: + tests_to_find.remove(i['name']) + self.assertListEqual(tests_to_find, []) + + # Check buildoptions + buildopts_to_find = {'cpp_std': 'c++11'} + for i in res['buildoptions']: + assertKeyTypes(buildoptions_keylist, i) + valid_type = False + for j in buildoptions_typelist: + if i['type'] == j[0]: + self.assertIsInstance(i['value'], j[1]) + assertKeyTypes(j[2], i) + valid_type = True + break + + self.assertTrue(valid_type) + if i['name'] in buildopts_to_find: + self.assertEqual(i['value'], buildopts_to_find[i['name']]) + buildopts_to_find.pop(i['name'], None) + self.assertDictEqual(buildopts_to_find, {}) + + # Check buildsystem_files + self.assertListEqual(res['buildsystem_files'], ['meson.build', 'sharedlib/meson.build', 'staticlib/meson.build']) + + # Check dependencies + dependencies_to_find = ['zlib'] + for i in res['dependencies']: + assertKeyTypes(dependencies_typelist, i) + if i['name'] in dependencies_to_find: + dependencies_to_find.remove(i['name']) + self.assertListEqual(dependencies_to_find, []) + + # Check projectinfo + self.assertDictEqual(res['projectinfo'], {'version': '1.2.3', 'descriptive_name': 'introspection', 'subprojects': []}) + + # Check targets + targets_to_find = { + 'sharedTestLib': ('shared library', True, False), + 'staticTestLib': ('static library', True, False), + 'test1': ('executable', True, True), + 'test2': ('executable', True, False), + 'test3': ('executable', True, False), + } + for i in res['targets']: + assertKeyTypes(targets_typelist, i) + if i['name'] in targets_to_find: + tgt = targets_to_find[i['name']] + self.assertEqual(i['type'], tgt[0]) + self.assertEqual(i['build_by_default'], tgt[1]) + self.assertEqual(i['installed'], tgt[2]) + targets_to_find.pop(i['name'], None) + for j in i['sources']: + assertKeyTypes(targets_sources_typelist, j) + self.assertDictEqual(targets_to_find, {}) def test_introspect_file_dump_eauals_all(self): - testdir = os.path.join(self.common_test_dir, '6 linkshared') + testdir = os.path.join(self.unit_test_dir, '49 introspection') self.init(testdir) res_all = self.introspect('--all') res_file = {} diff --git a/test cases/unit/49 introspection/meson.build b/test cases/unit/49 introspection/meson.build new file mode 100644 index 000000000..bd5d51a89 --- /dev/null +++ b/test cases/unit/49 introspection/meson.build @@ -0,0 +1,14 @@ +project('introspection', ['c', 'cpp'], version: '1.2.3', default_options: ['cpp_std=c++11']) + +dep1 = dependency('zlib') + +subdir('sharedlib') +subdir('staticlib') + +t1 = executable('test1', 't1.cpp', link_with: [sharedlib], install: true) +t2 = executable('test2', 't2.cpp', link_with: [staticlib]) +t3 = executable('test3', 't3.cpp', link_with: [sharedlib, staticlib], dependencies: [dep1]) + +test('test case 1', t1) +test('test case 2', t2) +benchmark('benchmark 1', t3) diff --git a/test cases/unit/49 introspection/sharedlib/meson.build b/test cases/unit/49 introspection/sharedlib/meson.build new file mode 100644 index 000000000..3de3493db --- /dev/null +++ b/test cases/unit/49 introspection/sharedlib/meson.build @@ -0,0 +1,2 @@ +SRC_shared = ['shared.cpp'] +sharedlib = shared_library('sharedTestLib', SRC_shared) diff --git a/test cases/unit/49 introspection/sharedlib/shared.cpp b/test cases/unit/49 introspection/sharedlib/shared.cpp new file mode 100644 index 000000000..5030ab75e --- /dev/null +++ b/test cases/unit/49 introspection/sharedlib/shared.cpp @@ -0,0 +1,9 @@ +#include "shared.hpp" + +void SharedClass::doStuff() { + number++; +} + +int SharedClass::getNumber() const { + return number; +} diff --git a/test cases/unit/49 introspection/sharedlib/shared.hpp b/test cases/unit/49 introspection/sharedlib/shared.hpp new file mode 100644 index 000000000..dc9b2da28 --- /dev/null +++ b/test cases/unit/49 introspection/sharedlib/shared.hpp @@ -0,0 +1,10 @@ +#pragma once + +class SharedClass { + private: + int number = 42; + public: + SharedClass() = default; + void doStuff(); + int getNumber() const; +}; \ No newline at end of file diff --git a/test cases/unit/49 introspection/staticlib/meson.build b/test cases/unit/49 introspection/staticlib/meson.build new file mode 100644 index 000000000..b1b9afe02 --- /dev/null +++ b/test cases/unit/49 introspection/staticlib/meson.build @@ -0,0 +1,2 @@ +SRC_static = ['static.c'] +staticlib = static_library('staticTestLib', SRC_static) diff --git a/test cases/unit/49 introspection/staticlib/static.c b/test cases/unit/49 introspection/staticlib/static.c new file mode 100644 index 000000000..37ebc0d80 --- /dev/null +++ b/test cases/unit/49 introspection/staticlib/static.c @@ -0,0 +1,5 @@ +#include "static.h" + +int add_numbers(int a, int b) { + return a + b; +} \ No newline at end of file diff --git a/test cases/unit/49 introspection/staticlib/static.h b/test cases/unit/49 introspection/staticlib/static.h new file mode 100644 index 000000000..506784e43 --- /dev/null +++ b/test cases/unit/49 introspection/staticlib/static.h @@ -0,0 +1,3 @@ +#pragma once + +int add_numbers(int a, int b); \ No newline at end of file diff --git a/test cases/unit/49 introspection/t1.cpp b/test cases/unit/49 introspection/t1.cpp new file mode 100644 index 000000000..2bcaab87c --- /dev/null +++ b/test cases/unit/49 introspection/t1.cpp @@ -0,0 +1,13 @@ +#include "sharedlib/shared.hpp" + +int main() { + SharedClass cl1; + if(cl1.getNumber() != 42) { + return 1; + } + cl1.doStuff(); + if(cl1.getNumber() != 43) { + return 2; + } + return 0; +} diff --git a/test cases/unit/49 introspection/t2.cpp b/test cases/unit/49 introspection/t2.cpp new file mode 100644 index 000000000..fee5097c3 --- /dev/null +++ b/test cases/unit/49 introspection/t2.cpp @@ -0,0 +1,8 @@ +#include "staticlib/static.h" + +int main() { + if(add_numbers(1, 2) != 3) { + return 1; + } + return 0; +} diff --git a/test cases/unit/49 introspection/t3.cpp b/test cases/unit/49 introspection/t3.cpp new file mode 100644 index 000000000..8a906e079 --- /dev/null +++ b/test cases/unit/49 introspection/t3.cpp @@ -0,0 +1,16 @@ +#include "sharedlib/shared.hpp" +#include "staticlib/static.h" + +int main() { + for(int i = 0; i < 1000; add_numbers(i, 1)) { + SharedClass cl1; + if(cl1.getNumber() != 42) { + return 1; + } + cl1.doStuff(); + if(cl1.getNumber() != 43) { + return 2; + } + } + return 0; +} From b91c5aad854bff3a13c27aa1a6ade85ded216207 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 14:21:07 +0100 Subject: [PATCH 18/63] Update intro dump on meson configure --- docs/markdown/snippets/introspect_multiple.md | 11 ++++---- mesonbuild/mconf.py | 2 ++ mesonbuild/mintro.py | 20 ++++++++++++-- run_unittests.py | 26 +++++++++++++++++++ test cases/unit/49 introspection/meson.build | 2 +- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index b7266f805..d05eae69a 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -1,12 +1,13 @@ ## Added option to introspect multiple parameters at once -Meson introspect can now print the results of multiple parameters -in a single call. The results are then printed as a single JSON +Meson introspect can now print the results of multiple introspection +commands in a single call. The results are then printed as a single JSON object. The format for a single command was not changed to keep backward compatibility. -Furthermore the option `-a,--all` and `-i,--indent` was added to -print all introspection information in one go and format the -JSON output (the default is still compact JSON). \ No newline at end of file +Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-new` +were added to print all introspection information in one go, format the +JSON output (the default is still compact JSON) and foce use the new +output format, even if only one introspection command was given. \ No newline at end of file diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 28589dab0..eca32bf6b 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -14,6 +14,7 @@ import os from . import (coredata, mesonlib, build) +from . import mintro def add_arguments(parser): coredata.register_builtin_arguments(parser) @@ -162,6 +163,7 @@ def run(options): c.print_conf() if save: c.save() + mintro.update_build_options(c.coredata, builddir) except ConfException as e: print('Meson configurator encountered an error:') raise e diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 326cd6ca1..04850c63e 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -487,8 +487,7 @@ def run(options): if options.all or options.benchmarks: toextract += ['benchmarks'] if options.all or options.buildoptions: - coredata = cdata.load(options.builddir) - results += [list_buildoptions(coredata)] + toextract += ['buildoptions'] if options.all or options.buildsystem_files: toextract += ['buildsystem_files'] if options.all or options.dependencies: @@ -550,3 +549,20 @@ def generate_introspection_file(builddata: build.Build, backend: backends.Backen with open(outfile, 'w') as fp: json.dump(outdict, fp) + +def update_build_options(coredata, builddir): + outfile = os.path.join(builddir, INTROSPECTION_OUTPUT_FILE) + outfile = os.path.abspath(outfile) + + with open(outfile, 'r') as fp: + outdict = json.load(fp) + + intro_info = [ + list_buildoptions(coredata) + ] + + for i in intro_info: + outdict[i[0]] = i[1] + + with open(outfile, 'w') as fp: + json.dump(outdict, fp) diff --git a/run_unittests.py b/run_unittests.py index 811df6bd3..492a22c1a 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3256,6 +3256,32 @@ recommended as it is not supported on some platforms''') self.assertEqual(res_all, res_file) + def test_introspect_config_update(self): + testdir = os.path.join(self.unit_test_dir, '49 introspection') + introfile = os.path.join(self.builddir, 'meson-introspection.json') + self.init(testdir) + self.assertPathExists(introfile) + with open(introfile, 'r') as fp: + res1 = json.load(fp) + + self.setconf('-Dcpp_std=c++14') + self.setconf('-Dbuildtype=release') + + for idx, i in enumerate(res1['buildoptions']): + if i['name'] == 'cpp_std': + res1['buildoptions'][idx]['value'] = 'c++14' + if i['name'] == 'buildtype': + res1['buildoptions'][idx]['value'] = 'release' + if i['name'] == 'optimization': + res1['buildoptions'][idx]['value'] = '3' + if i['name'] == 'debug': + res1['buildoptions'][idx]['value'] = False + + with open(introfile, 'r') as fp: + res2 = json.load(fp) + + self.assertDictEqual(res1, res2) + class FailureTests(BasePlatformTests): ''' Tests that test failure conditions. Build files here should be dynamically diff --git a/test cases/unit/49 introspection/meson.build b/test cases/unit/49 introspection/meson.build index bd5d51a89..09b1d4f8d 100644 --- a/test cases/unit/49 introspection/meson.build +++ b/test cases/unit/49 introspection/meson.build @@ -1,4 +1,4 @@ -project('introspection', ['c', 'cpp'], version: '1.2.3', default_options: ['cpp_std=c++11']) +project('introspection', ['c', 'cpp'], version: '1.2.3', default_options: ['cpp_std=c++11', 'buildtype=debug']) dep1 = dependency('zlib') From b034f52656c19f378fc144abd9087e7526b1e27f Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 14:53:28 +0100 Subject: [PATCH 19/63] Filenames are now lists --- docs/markdown/snippets/introspect_multiple.md | 7 ++++++- mesonbuild/build.py | 3 +++ mesonbuild/mintro.py | 6 +----- run_unittests.py | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index d05eae69a..17d0a3f97 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -10,4 +10,9 @@ compatibility. Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-new` were added to print all introspection information in one go, format the JSON output (the default is still compact JSON) and foce use the new -output format, even if only one introspection command was given. \ No newline at end of file +output format, even if only one introspection command was given. + +Additionlly the format of target was changed: + - `filename` is now a list of output filenames + - `install_filename` is now also a list of installed files + - New: the `sources` key. It stores the source files of a target and there compiler parameters diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4fa6bde63..642c2d5f0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2120,6 +2120,9 @@ class RunTarget(Target): def get_filename(self): return self.name + def get_outputs(self): + return [self.name] + def type_suffix(self): return "@run" diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 04850c63e..cf1aeffde 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -93,11 +93,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) if not isinstance(target, build.Target): raise RuntimeError('Something weird happened. File a bug.') - fname = target.get_filename() - if isinstance(fname, list): - fname = [os.path.join(target.subdir, x) for x in fname] - else: - fname = os.path.join(target.subdir, fname) + fname = [os.path.join(target.subdir, x) for x in target.get_outputs()] t = { 'name': target.get_basename(), diff --git a/run_unittests.py b/run_unittests.py index 492a22c1a..1977fe0c1 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1436,7 +1436,7 @@ class AllPlatformTests(BasePlatformTests): # Get name of static library targets = self.introspect('--targets') self.assertEqual(len(targets), 1) - libname = targets[0]['filename'] + libname = targets[0]['filename'][0] # Build and get contents of static library self.build() before = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split() @@ -3168,7 +3168,7 @@ recommended as it is not supported on some platforms''') ('name', str), ('id', str), ('type', str), - ('filename', str), + ('filename', list), ('build_by_default', bool), ('sources', list), ('installed', bool), @@ -4368,7 +4368,7 @@ class LinuxlikeTests(BasePlatformTests): break self.assertIsInstance(docbook_target, dict) ifile = self.introspect(['--target-files', 'generated-gdbus-docbook@cus'])[0] - self.assertEqual(t['filename'], 'gdbus/generated-gdbus-doc-' + os.path.basename(ifile)) + self.assertListEqual(t['filename'], ['gdbus/generated-gdbus-doc-' + os.path.basename(ifile)]) def test_build_rpath(self): if is_cygwin(): From b11df88395a6543ab1ea9354050f0b885959854a Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 16:19:01 +0100 Subject: [PATCH 20/63] Documentation and unit test update --- docs/markdown/IDE-integration.md | 103 ++++++++++++++---- .../snippets/introspect_target_new.md | 7 -- mesonbuild/mintro.py | 7 +- run_unittests.py | 11 +- test cases/unit/49 introspection/meson.build | 2 +- 5 files changed, 93 insertions(+), 37 deletions(-) delete mode 100644 docs/markdown/snippets/introspect_target_new.md diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index c75392cae..a6c89bb7e 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -4,39 +4,86 @@ short-description: Meson's API to integrate Meson support into an IDE # IDE integration -Meson has exporters for Visual Studio and XCode, but writing a custom backend for every IDE out there is not a scalable approach. To solve this problem, Meson provides an API that makes it easy for any IDE or build tool to integrate Meson builds and provide an experience comparable to a solution native to the IDE. +Meson has exporters for Visual Studio and XCode, but writing a custom backend for every IDE out there is not a scalable approach. To solve this problem, Meson provides an API that makes it easy for any IDE or build tools to integrate Meson builds and provide an experience comparable to a solution native to the IDE. -The basic tool for this is `meson introspect`. +The basic resource for this is the `meson-introspection.json` file in the build directory. -The first thing to do when setting up a Meson project in an IDE is to select the source and build directories. For this example we assume that the source resides in an Eclipse-like directory called `workspace/project` and the build tree is nested inside it as `workspace/project/build`. First we initialise Meson by running the following command in the source directory. +The first thing to do when setting up a Meson project in an IDE is to select the source and build directories. For this example we assume that the source resides in an Eclipse-like directory called `workspace/project` and the build tree is nested inside it as `workspace/project/build`. First, we initialize Meson by running the following command in the source directory. meson builddir -For the remainder of the document we assume that all commands are executed inside the build directory unless otherwise specified. +The `meson-introspection.json` can then be found in the root of this build directory. It will be automatically updated when meson is (re)configured, or the build options change. As a result, an IDE can watch for changes in this file to know when something changed. -The first thing you probably want is to get a list of top level targets. For that we use the introspection tool. It comes with extensive command line help so we recommend using that in case problems appear. +The basic JSON format structure defined as follows: - meson introspect --targets +```json +{ + "benchmarks": [], + "buildoptions": [], + "buildsystem_files": ["just", "a", "list", "of", "meson", "files"], + "dependencies": [], + "installed": {}, + "projectinfo": { + "version": "1.2.3", + "descriptive_name": "Project Name", + "subprojects": [] + }, + "targets": [], + "tests": [] +} +``` -The JSON formats will not be specified in this document. The easiest way of learning them is to look at sample output from the tool. +The content of each JSON entry in this format is further specified in the remainder of this document. -Once you have a list of targets, you probably need the list of source files that comprise the target. To get this list for a target, say `exampletarget`, issue the following command. +## The `targets` section - meson introspect --target-files exampletarget +The most important entry for an IDE is probably the `targets` section. Here each target with its sources and compiler parameters is specified. The JSON format for one target is defined as follows: -In order to make code completion work, you need the compiler flags for each compilation step. Meson does not provide this itself, but the Ninja tool Meson uses to build does provide it. To find out the compile steps necessary to build target foo, issue the following command. +```json +{ + "name": "Name of the target", + "id": "The internal ID meson uses", + "type": "", + "filename": ["list", "of", "generate", "files"], + "build_by_default": true / false, + "sources": [], + "installed": true / false, +} +``` - ninja -t commands foo +If the key `installed` is set to `true`, the key `install_filename` will also be present. It stores the installation location for each file in `filename`. If one file in `filename` is not installed, its corresponding install location is set to `null`. -Note that if the target has dependencies (such as generated sources), then the commands for those show up in this list as well, so you need to do some filtering. Alternatively you can grab every command invocation in the [Clang tools db](https://clang.llvm.org/docs/JSONCompilationDatabase.html) format that is written to a file called `compile_commands.json` in the build directory. +A target usually generates only one file. However, it is possible for custom targets to have multiple outputs. -## Build Options +### Target sources + +The `sources` entry stores a list of all source objects of the target. With this information, an IDE can provide code completion for all source files. + +```json +{ + "language": "language ID", + "compiler": ["The", "compiler", "command"], + "parameters": ["list", "of", "compiler", "parameters"], + "source_files": ["list", "of", "all", "source", "files", "for", "this", "language"] +} +``` -The next thing to display is the list of options that can be set. These include build type and so on. Here's how to extract them. +### Possible values for `type` - meson introspect --buildoptions +The following table shows all valid types for a target. -This command returns a list of all supported buildoptions with the format: + value of `type` | Description + ---------------- | ------------------------------------------------------------------------------------------------- + `executable` | This target will generate an executable file + `static library` | Target for a static library + `shared library` | Target for a shared library + `shared module` | A shared library that is meant to be used with dlopen rather than linking into something else + `custom` | A custom target + `unknown target` | The current target format is unknown. This is probably a bug + +## Build Options + +The list of all build options (build type, warning level, etc.) is stored in the `buildoptions` list. Here is the JSON format for each option. ```json { @@ -85,11 +132,29 @@ Because of this options for the subprojects can differ. Compilation and unit tests are done as usual by running the `ninja` and `ninja test` commands. A JSON formatted result log can be found in `workspace/project/builddir/meson-logs/testlog.json`. -When these tests fail, the user probably wants to run the failing test in a debugger. To make this as integrated as possible, extract the test test setups with this command. +When these tests fail, the user probably wants to run the failing test in a debugger. To make this as integrated as possible, extract the tests from the `tests` and `benchmarks` entries. +This provides you with all the information needed to run the test: what command to execute, command line arguments and environment variable settings. - meson introspect --tests +```json +{ + "name": "name of the test", + "workdir": "the working directory (can be null)", + "timeout": "the test timeout", + "suite": ["list", "of", "test", "suites"], + "is_parallel": true / false, + "cmd": ["command", "to", "run"], + "env": { + "VARIABLE1": "value 1", + "VARIABLE2": "value 2" + } +} +``` -This provides you with all the information needed to run the test: what command to execute, command line arguments and environment variable settings. +# Programmatic interface + +Meson also provides the `meson introspect` for project introspection via the command line. Use `meson introspect -h` to see all available options. + +This API can also work without a build directory for the `--projectinfo` command. # Existing integrations diff --git a/docs/markdown/snippets/introspect_target_new.md b/docs/markdown/snippets/introspect_target_new.md deleted file mode 100644 index cf0fd6a52..000000000 --- a/docs/markdown/snippets/introspect_target_new.md +++ /dev/null @@ -1,7 +0,0 @@ -## New `include_directories` and `extra_args` keys for the target introspection - -Meson now also prints the include directories and extra compiler arguments for -the target introspection (`meson introspect --targets`). - -The `include_directories` key stores a list of absolute paths and the `extra_args` -key holds a dict of compiler arguments for each language. diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index cf1aeffde..5643b1a8c 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -106,12 +106,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) if installdata and target.should_install(): t['installed'] = True - t['install_filename'] = [] - - for i in target.outputs: - fname = intall_lookuptable.get(i) - if fname is not None: - t['install_filename'] += [fname] + t['install_filename'] = [intall_lookuptable.get(x, None) for x in target.get_outputs()] else: t['installed'] = False tlist.append(t) diff --git a/run_unittests.py b/run_unittests.py index 1977fe0c1..db88c9b05 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1224,7 +1224,10 @@ class BasePlatformTests(unittest.TestCase): self.assertEqual(len(pathlist1), len(pathlist2)) worklist = list(zip(pathlist1, pathlist2)) for i in worklist: - self.assertPathEqual(i[0], i[1]) + if i[0] == None: + self.assertEqual(i[0], i[1]) + else: + self.assertPathEqual(i[0], i[1]) def assertPathBasenameEqual(self, path, basename): msg = '{!r} does not end with {!r}'.format(path, basename) @@ -1510,8 +1513,8 @@ class AllPlatformTests(BasePlatformTests): intro = intro[::-1] self.assertPathListEqual(intro[0]['install_filename'], ['/usr/include/diff.h', '/usr/bin/diff.sh']) self.assertPathListEqual(intro[1]['install_filename'], ['/opt/same.h', '/opt/same.sh']) - self.assertPathListEqual(intro[2]['install_filename'], ['/usr/include/first.h']) - self.assertPathListEqual(intro[3]['install_filename'], ['/usr/bin/second.sh']) + self.assertPathListEqual(intro[2]['install_filename'], ['/usr/include/first.h', None]) + self.assertPathListEqual(intro[3]['install_filename'], [None, '/usr/bin/second.sh']) def test_uninstall(self): exename = os.path.join(self.installdir, 'usr/bin/prog' + exe_suffix) @@ -3213,7 +3216,7 @@ recommended as it is not supported on some platforms''') self.assertListEqual(res['buildsystem_files'], ['meson.build', 'sharedlib/meson.build', 'staticlib/meson.build']) # Check dependencies - dependencies_to_find = ['zlib'] + dependencies_to_find = ['threads'] for i in res['dependencies']: assertKeyTypes(dependencies_typelist, i) if i['name'] in dependencies_to_find: diff --git a/test cases/unit/49 introspection/meson.build b/test cases/unit/49 introspection/meson.build index 09b1d4f8d..14d880b06 100644 --- a/test cases/unit/49 introspection/meson.build +++ b/test cases/unit/49 introspection/meson.build @@ -1,6 +1,6 @@ project('introspection', ['c', 'cpp'], version: '1.2.3', default_options: ['cpp_std=c++11', 'buildtype=debug']) -dep1 = dependency('zlib') +dep1 = dependency('threads') subdir('sharedlib') subdir('staticlib') From c326ae2e75d655798b4fc19e97084175e6e3d5ad Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 16:21:56 +0100 Subject: [PATCH 21/63] Fixed flake8 --- run_unittests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_unittests.py b/run_unittests.py index db88c9b05..30a8e8f99 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1224,7 +1224,7 @@ class BasePlatformTests(unittest.TestCase): self.assertEqual(len(pathlist1), len(pathlist2)) worklist = list(zip(pathlist1, pathlist2)) for i in worklist: - if i[0] == None: + if i[0] is None: self.assertEqual(i[0], i[1]) else: self.assertPathEqual(i[0], i[1]) From a9ade5470755a646a6153bc2a86cb442d6b0776c Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 17:00:23 +0100 Subject: [PATCH 22/63] Should fix azure --- run_unittests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_unittests.py b/run_unittests.py index 30a8e8f99..0cbfc86df 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3213,7 +3213,7 @@ recommended as it is not supported on some platforms''') self.assertDictEqual(buildopts_to_find, {}) # Check buildsystem_files - self.assertListEqual(res['buildsystem_files'], ['meson.build', 'sharedlib/meson.build', 'staticlib/meson.build']) + self.assertPathListEqual(res['buildsystem_files'], ['meson.build', 'sharedlib/meson.build', 'staticlib/meson.build']) # Check dependencies dependencies_to_find = ['threads'] From e4e0e89ea81afc6b8d67f3822c2c4f26633a4e69 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 29 Nov 2018 17:38:24 +0100 Subject: [PATCH 23/63] Fixed mac build --- run_unittests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_unittests.py b/run_unittests.py index 0cbfc86df..7bcc66113 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3722,7 +3722,7 @@ class DarwinTests(BasePlatformTests): self.build() targets = {} for t in self.introspect('--targets'): - targets[t['name']] = t['filename'] + targets[t['name']] = t['filename'][0] self.assertEqual(self._get_darwin_versions(targets['some']), ('7.0.0', '7.0.0')) self.assertEqual(self._get_darwin_versions(targets['noversion']), ('0.0.0', '0.0.0')) self.assertEqual(self._get_darwin_versions(targets['onlyversion']), ('1.0.0', '1.0.0')) From d4ac832bf695f3c6b00c976d3706159b3616ec2f Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 2 Dec 2018 18:14:44 +0100 Subject: [PATCH 24/63] Split the monolithic introspection file into chunks --- mesonbuild/environment.py | 15 ++++++++-- mesonbuild/mconf.py | 2 +- mesonbuild/mintro.py | 58 ++++++++++++++++----------------------- run_unittests.py | 50 +++++++++++++++++++++++---------- 4 files changed, 73 insertions(+), 52 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index b10f826ee..67b58bc9a 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -308,6 +308,7 @@ def search_version(text): class Environment: private_dir = 'meson-private' log_dir = 'meson-logs' + info_dir = 'meson-info' def __init__(self, source_dir, build_dir, options): self.source_dir = source_dir @@ -318,8 +319,10 @@ class Environment: if build_dir is not None: self.scratch_dir = os.path.join(build_dir, Environment.private_dir) self.log_dir = os.path.join(build_dir, Environment.log_dir) + self.info_dir = os.path.join(build_dir, Environment.info_dir) os.makedirs(self.scratch_dir, exist_ok=True) os.makedirs(self.log_dir, exist_ok=True) + os.makedirs(self.info_dir, exist_ok=True) try: self.coredata = coredata.load(self.get_build_dir()) self.first_invocation = False @@ -333,8 +336,16 @@ class Environment: mlog.log('Reason:', mlog.red(str(e))) coredata.read_cmd_line_file(self.build_dir, options) self.create_new_coredata(options) - else: - raise e + except MesonException as e: + # If we stored previous command line options, we can recover from + # a broken/outdated coredata. + if os.path.isfile(coredata.get_cmd_line_file(self.build_dir)): + mlog.warning('Regenerating configuration from scratch.') + mlog.log('Reason:', mlog.red(str(e))) + coredata.read_cmd_line_file(self.build_dir, options) + self.create_new_coredata(options) + else: + raise e else: # Just create a fresh coredata in this case self.create_new_coredata(options) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index eca32bf6b..a7120a8d5 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -163,7 +163,7 @@ def run(options): c.print_conf() if save: c.save() - mintro.update_build_options(c.coredata, builddir) + mintro.update_build_options(c.coredata, c.build.environment.info_dir) except ConfException as e: print('Meson configurator encountered an error:') raise e diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 5643b1a8c..e10b95bf0 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -276,7 +276,7 @@ def list_target_files(target_name, targets): return ('target_files', result) -def list_buildoptions(coredata): +def list_buildoptions(coredata: cdata.CoreData): optlist = [] dir_option_names = ['bindir', @@ -346,7 +346,7 @@ def list_buildsystem_files(builddata: build.Build): filelist = find_buildsystem_files_list(src_dir) return ('buildsystem_files', filelist) -def list_deps(coredata): +def list_deps(coredata: cdata.CoreData): result = [] for d in coredata.deps.values(): if d.found(): @@ -451,10 +451,10 @@ def list_projinfo_from_source(sourcedir): def run(options): datadir = 'meson-private' - introfile = INTROSPECTION_OUTPUT_FILE + infodir = 'meson-info' if options.builddir is not None: datadir = os.path.join(options.builddir, datadir) - introfile = os.path.join(options.builddir, introfile) + infodir = os.path.join(options.builddir, infodir) if options.builddir.endswith('/meson.build') or options.builddir.endswith('\\meson.build') or options.builddir == 'meson.build': sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11] if options.projectinfo: @@ -463,15 +463,11 @@ def run(options): if options.buildoptions: list_buildoptions_from_source(sourcedir, options.backend) return 0 - if not os.path.isdir(datadir) or not os.path.isfile(introfile): + if not os.path.isdir(datadir) or not os.path.isdir(infodir): print('Current directory is not a build dir. Please specify it or ' 'change the working directory to it.') return 1 - rawdata = {} - with open(introfile, 'r') as fp: - rawdata = json.load(fp) - results = [] toextract = [] @@ -490,14 +486,20 @@ def run(options): if options.all or options.list_targets: toextract += ['targets'] if options.target_files is not None: - results += [list_target_files(options.target_files, rawdata['targets'])] + targets_file = os.path.join(infodir, 'intro-targets.json') + with open(targets_file, 'r') as fp: + targets = json.load(fp) + results += [list_target_files(options.target_files, targets)] if options.all or options.tests: toextract += ['tests'] for i in toextract: - if i not in rawdata: - raise RuntimeError('Key "{}" missing in introspection file. Please report this a bug.'.format(i)) - results += [(i, rawdata[i])] + curr = os.path.join(infodir, 'intro-{}.json'.format(i)) + if not os.path.isfile(curr): + print('Introspection file {} does not exist.'.format(curr)) + return 1 + with open(curr, 'r') as fp: + results += [(i, json.load(fp))] indent = options.indent if options.indent > 0 else None @@ -514,6 +516,12 @@ def run(options): print(json.dumps(out, indent=indent)) return 0 +def write_intro_info(intro_info, info_dir): + for i in intro_info: + out_file = os.path.join(info_dir, 'intro-{}.json'.format(i[0])) + with open(out_file, 'w') as fp: + json.dump(i[1], fp) + def generate_introspection_file(builddata: build.Build, backend: backends.Backend): coredata = builddata.environment.get_coredata() benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) @@ -531,29 +539,11 @@ def generate_introspection_file(builddata: build.Build, backend: backends.Backen list_tests(testdata) ] - outdict = {} - for i in intro_info: - outdict[i[0]] = i[1] - - outfile = os.path.join(builddata.environment.get_build_dir(), INTROSPECTION_OUTPUT_FILE) - outfile = os.path.abspath(outfile) - - with open(outfile, 'w') as fp: - json.dump(outdict, fp) - -def update_build_options(coredata, builddir): - outfile = os.path.join(builddir, INTROSPECTION_OUTPUT_FILE) - outfile = os.path.abspath(outfile) - - with open(outfile, 'r') as fp: - outdict = json.load(fp) + write_intro_info(intro_info, builddata.environment.info_dir) +def update_build_options(coredata: cdata.CoreData, info_dir): intro_info = [ list_buildoptions(coredata) ] - for i in intro_info: - outdict[i[0]] = i[1] - - with open(outfile, 'w') as fp: - json.dump(outdict, fp) + write_intro_info(intro_info, info_dir) diff --git a/run_unittests.py b/run_unittests.py index 7bcc66113..0c313d26c 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3116,10 +3116,8 @@ recommended as it is not supported on some platforms''') def test_introspect_json_dump(self): testdir = os.path.join(self.unit_test_dir, '49 introspection') self.init(testdir) - introfile = os.path.join(self.builddir, 'meson-introspection.json') - self.assertPathExists(introfile) - with open(introfile, 'r') as fp: - res = json.load(fp) + infodir = os.path.join(self.builddir, 'meson-info') + self.assertPathExists(infodir) def assertKeyTypes(key_type_list, obj): for i in key_type_list: @@ -3184,6 +3182,14 @@ recommended as it is not supported on some platforms''') ('source_files', list), ] + # First load all files + res = {} + for i in root_keylist: + curr = os.path.join(infodir, 'intro-{}.json'.format(i[0])) + self.assertPathExists(curr) + with open(curr, 'r') as fp: + res[i[0]] = json.load(fp) + assertKeyTypes(root_keylist, res) # Check Tests and benchmarks @@ -3252,16 +3258,30 @@ recommended as it is not supported on some platforms''') res_all = self.introspect('--all') res_file = {} - introfile = os.path.join(self.builddir, 'meson-introspection.json') - self.assertPathExists(introfile) - with open(introfile, 'r') as fp: - res_file = json.load(fp) + root_keylist = [ + 'benchmarks', + 'buildoptions', + 'buildsystem_files', + 'dependencies', + 'installed', + 'projectinfo', + 'targets', + 'tests', + ] + + infodir = os.path.join(self.builddir, 'meson-info') + self.assertPathExists(infodir) + for i in root_keylist: + curr = os.path.join(infodir, 'intro-{}.json'.format(i)) + self.assertPathExists(curr) + with open(curr, 'r') as fp: + res_file[i] = json.load(fp) self.assertEqual(res_all, res_file) def test_introspect_config_update(self): testdir = os.path.join(self.unit_test_dir, '49 introspection') - introfile = os.path.join(self.builddir, 'meson-introspection.json') + introfile = os.path.join(self.builddir, 'meson-info', 'intro-buildoptions.json') self.init(testdir) self.assertPathExists(introfile) with open(introfile, 'r') as fp: @@ -3270,20 +3290,20 @@ recommended as it is not supported on some platforms''') self.setconf('-Dcpp_std=c++14') self.setconf('-Dbuildtype=release') - for idx, i in enumerate(res1['buildoptions']): + for idx, i in enumerate(res1): if i['name'] == 'cpp_std': - res1['buildoptions'][idx]['value'] = 'c++14' + res1[idx]['value'] = 'c++14' if i['name'] == 'buildtype': - res1['buildoptions'][idx]['value'] = 'release' + res1[idx]['value'] = 'release' if i['name'] == 'optimization': - res1['buildoptions'][idx]['value'] = '3' + res1[idx]['value'] = '3' if i['name'] == 'debug': - res1['buildoptions'][idx]['value'] = False + res1[idx]['value'] = False with open(introfile, 'r') as fp: res2 = json.load(fp) - self.assertDictEqual(res1, res2) + self.assertListEqual(res1, res2) class FailureTests(BasePlatformTests): ''' From b9c4913cf032144eed0cb6308aaff4e77a825f08 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 2 Dec 2018 19:53:34 +0100 Subject: [PATCH 25/63] Updated documentation --- docs/markdown/IDE-integration.md | 40 ++++++++----------- docs/markdown/snippets/introspect_multiple.md | 3 ++ 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index a6c89bb7e..bd5dff26e 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -6,38 +6,32 @@ short-description: Meson's API to integrate Meson support into an IDE Meson has exporters for Visual Studio and XCode, but writing a custom backend for every IDE out there is not a scalable approach. To solve this problem, Meson provides an API that makes it easy for any IDE or build tools to integrate Meson builds and provide an experience comparable to a solution native to the IDE. -The basic resource for this is the `meson-introspection.json` file in the build directory. +All the resources required for such a IDE integration can be found in the `meson-info` directory in the build directory. The first thing to do when setting up a Meson project in an IDE is to select the source and build directories. For this example we assume that the source resides in an Eclipse-like directory called `workspace/project` and the build tree is nested inside it as `workspace/project/build`. First, we initialize Meson by running the following command in the source directory. meson builddir -The `meson-introspection.json` can then be found in the root of this build directory. It will be automatically updated when meson is (re)configured, or the build options change. As a result, an IDE can watch for changes in this file to know when something changed. +With this command meson will configure the project and also generate introspection information that is stored in `intro-*.json` files in the `meson-info` directory. All files will be automatically updated when meson is (re)configured, or the build options change. Thus, an IDE can watch for changes in this directory to know when something changed. -The basic JSON format structure defined as follows: +The `meson-info` directory should contain the following files: -```json -{ - "benchmarks": [], - "buildoptions": [], - "buildsystem_files": ["just", "a", "list", "of", "meson", "files"], - "dependencies": [], - "installed": {}, - "projectinfo": { - "version": "1.2.3", - "descriptive_name": "Project Name", - "subprojects": [] - }, - "targets": [], - "tests": [] -} -``` + File | Description + ------------------------------- | --------------------------------------------------------------------- + `intro-benchmarks.json` | Lists all benchmarks + `intro-buildoptions.json` | Contains a full list of meson configuration options for the project + `intro-buildsystem_files.json` | Full list of all meson build files + `intro-dependencies.json` | Lists all dependencies used in the project + `intro-installed.json` | Contains mapping of files to their installed location + `intro-projectinfo.json` | Stores basic information about the project (name, version, etc.) + `intro-targets.json` | Full list of all build targets + `intro-tests.json` | Lists all tests with instructions how to run them -The content of each JSON entry in this format is further specified in the remainder of this document. +The content of the JSON files is further specified in the remainder of this document. ## The `targets` section -The most important entry for an IDE is probably the `targets` section. Here each target with its sources and compiler parameters is specified. The JSON format for one target is defined as follows: +The most important file for an IDE is probably `intro-targets.json`. Here each target with its sources and compiler parameters is specified. The JSON format for one target is defined as follows: ```json { @@ -57,7 +51,7 @@ A target usually generates only one file. However, it is possible for custom tar ### Target sources -The `sources` entry stores a list of all source objects of the target. With this information, an IDE can provide code completion for all source files. +The `intro-sources.json` file stores a list of all source objects of the target. With this information, an IDE can provide code completion for all source files. ```json { @@ -83,7 +77,7 @@ The following table shows all valid types for a target. ## Build Options -The list of all build options (build type, warning level, etc.) is stored in the `buildoptions` list. Here is the JSON format for each option. +The list of all build options (build type, warning level, etc.) is stored in the `intro-buildoptions.json` file. Here is the JSON format for each option. ```json { diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 17d0a3f97..151421950 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -12,6 +12,9 @@ were added to print all introspection information in one go, format the JSON output (the default is still compact JSON) and foce use the new output format, even if only one introspection command was given. +A complete introspection dump is also stored in the `meson-info` +directory. This dump will alwys be (re)generated on every meson run. + Additionlly the format of target was changed: - `filename` is now a list of output filenames - `install_filename` is now also a list of installed files From 7691b0460c53478cfc2e11fca156557add6579bf Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 11 Dec 2018 17:50:38 +0100 Subject: [PATCH 26/63] Ninja backend target introspection --- mesonbuild/backend/backends.py | 14 ++-- mesonbuild/backend/ninjabackend.py | 127 ++++++++++++++++++++++++++--- mesonbuild/mintro.py | 2 +- run_unittests.py | 7 +- 4 files changed, 130 insertions(+), 20 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b36b98c9d..b3b6df93c 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1158,7 +1158,8 @@ class Backend: "language": "", "compiler": ["result", "of", "comp.get_exelist()"], "parameters": ["list", "of", "compiler", "parameters], - "source_files": ["list", "of", "all", "", "source", "files"] + "sources": ["list", "of", "all", "", "source", "files"], + "generated_sources": ["list", "of", "generated", "source", "files"] } ] @@ -1169,20 +1170,23 @@ class Backend: source_list = [] for i in source_list_raw: if isinstance(i, mesonlib.File): - source_list += [os.path.join(i.subdir, i.fname)] + source_list += [i.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())] elif isinstance(i, str): - source_list += [i] + source_list += [os.path.join(self.environment.get_source_dir(), i)] + source_list = list(map(lambda x: os.path.normpath(x), source_list)) return [{ 'language': 'unknown', 'compiler': [], 'parameters': [], - 'source_files': source_list + 'sources': source_list, + 'generated_sources': [] }] return [{ 'language': 'unknown', 'compiler': [], 'parameters': [], - 'source_files': [] + 'sources': [], + 'generated_sources': [] }] diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 44bdaab55..06b450678 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -150,6 +150,8 @@ class NinjaBackend(backends.Backend): self.ninja_filename = 'build.ninja' self.fortran_deps = {} self.all_outputs = {} + self.introspection_data = {} + self._intro_last_index = 0 def create_target_alias(self, to_target, outfile): # We need to use aliases for targets that might be used as directory @@ -321,6 +323,73 @@ int dummy; return False return True + def create_target_source_introspection(self, target, comp, parameters, sources, generated_sources): + ''' + Adds the source file introspection information for a language of a target + + Internal introspection storage formart: + self.introspection_data = { + '': [ + { + 'language: 'lang', + 'compiler': ['comp', 'exe', 'list'], + 'parameters': ['UNIQUE', 'parameter', 'list'], + 'sources': [], + 'generated_sources': [], + 'id_hash': 634523234445 # Internal unique hash to identify a compiler / language / paramerter combo + } + ] + } + ''' + build_dir = self.environment.get_build_dir() + id = target.get_id() + lang = comp.get_language() + tgt = self.introspection_data[id] + id_hash = hash((lang, CompilerArgs)) + src_block = None + # Find an existing entry or create a new one + # ... first check the last used index + if self._intro_last_index < len(tgt): + tmp = tgt[self._intro_last_index] + if tmp['id_hash'] == id_hash: + src_block = tmp + # ... check all entries + if src_block is None: + for idx, i in enumerate(tgt): + if i['id_hash'] == id_hash: + src_block = i + self._intro_last_index = idx + break + # ... create a new one + if src_block is None: + # Convert parameters + if isinstance(parameters, CompilerArgs): + parameters = parameters.to_native(copy=True) + for idx, i in enumerate(parameters): + if i[:2] == '-I' or i[:2] == '/I' or i[:2] == '-L': + parameters[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + if target.is_cross: + parameters += comp.get_cross_extra_flags(self.environment, False) + # The new entry + src_block = { + 'language': lang, + 'compiler': comp.get_exelist(), + 'parameters': parameters, + 'sources': [], + 'generated_sources': [], + 'id_hash': id_hash + } + self._intro_last_index = len(tgt) + tgt.append(src_block) + # Make source files absolute + sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in sources] + sources = [os.path.normpath(os.path.join(build_dir, x)) for x in sources] + generated_sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in generated_sources] + generated_sources = [os.path.normpath(os.path.join(build_dir, x)) for x in generated_sources] + # Add the source files + src_block['sources'] += sources + src_block['generated_sources'] += generated_sources + def generate_target(self, target, outfile): if isinstance(target, build.CustomTarget): self.generate_custom_target(target, outfile) @@ -330,6 +399,8 @@ int dummy; if name in self.processed_targets: return self.processed_targets[name] = True + # Initialize an empty introspection source list + self.introspection_data[name] = [] # Generate rules for all dependency targets self.process_target_dependencies(target, outfile) # If target uses a language that cannot link to C objects, @@ -770,14 +841,16 @@ int dummy; # Add possible java generated files to src list generated_sources = self.get_target_generated_sources(target) + gen_src_list = [] for rel_src, gensrc in generated_sources.items(): dirpart, fnamepart = os.path.split(rel_src) raw_src = File(True, dirpart, fnamepart) if rel_src.endswith('.java'): - src_list.append(raw_src) + gen_src_list.append(raw_src) - for src in src_list: - plain_class_path = self.generate_single_java_compile(src, target, compiler, outfile) + compile_args = self.determine_single_java_compile_args(target, compiler) + for src in src_list + gen_src_list: + plain_class_path = self.generate_single_java_compile(src, target, compiler, compile_args, outfile) class_list.append(plain_class_path) class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list] manifest_path = os.path.join(self.get_target_private_dir(target), 'META-INF', 'MANIFEST.MF') @@ -803,6 +876,8 @@ int dummy; elem.add_dep(class_dep_list) elem.add_item('ARGS', commands) elem.write(outfile) + # Create introspection information + self.create_target_source_introspection(target, compiler, compile_args, src_list, gen_src_list) def generate_cs_resource_tasks(self, target, outfile): args = [] @@ -856,10 +931,11 @@ int dummy; else: outputs = [outname_rel] generated_sources = self.get_target_generated_sources(target) + generated_rel_srcs = [] for rel_src in generated_sources.keys(): dirpart, fnamepart = os.path.split(rel_src) if rel_src.lower().endswith('.cs'): - rel_srcs.append(os.path.normpath(rel_src)) + generated_rel_srcs.append(os.path.normpath(rel_src)) deps.append(os.path.normpath(rel_src)) for dep in target.get_external_deps(): @@ -867,19 +943,15 @@ int dummy; commands += self.build.get_project_args(compiler, target.subproject, target.is_cross) commands += self.build.get_global_args(compiler, target.is_cross) - elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs) + elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs + generated_rel_srcs) elem.add_dep(deps) elem.add_item('ARGS', commands) elem.write(outfile) self.generate_generator_list_rules(target, outfile) + self.create_target_source_introspection(target, compiler, commands, rel_srcs, generated_rel_srcs) - def generate_single_java_compile(self, src, target, compiler, outfile): - deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] - generated_sources = self.get_target_generated_sources(target) - for rel_src, gensrc in generated_sources.items(): - if rel_src.endswith('.java'): - deps.append(rel_src) + def determine_single_java_compile_args(self, target, compiler): args = [] args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) args += self.build.get_global_args(compiler, target.is_cross) @@ -894,6 +966,14 @@ int dummy; for idir in i.get_incdirs(): sourcepath += os.path.join(self.build_to_src, i.curdir, idir) + os.pathsep args += ['-sourcepath', sourcepath] + return args + + def generate_single_java_compile(self, src, target, compiler, args, outfile): + deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] + generated_sources = self.get_target_generated_sources(target) + for rel_src, gensrc in generated_sources.items(): + if rel_src.endswith('.java'): + deps.append(rel_src) rel_src = src.rel_to_builddir(self.build_to_src) plain_class_path = src.fname[:-4] + 'class' rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path) @@ -1102,6 +1182,7 @@ int dummy; element.add_item('ARGS', args) element.add_dep(extra_dep_files) element.write(outfile) + self.create_target_source_introspection(target, valac, args, all_files, []) return other_src[0], other_src[1], vala_c_src def generate_rust_target(self, target, outfile): @@ -1193,6 +1274,7 @@ int dummy; element.write(outfile) if isinstance(target, build.SharedLibrary): self.generate_shsym(outfile, target) + self.create_target_source_introspection(target, rustc, args, [main_rust_file], []) def swift_module_file_name(self, target): return os.path.join(self.get_target_private_dir(target), @@ -1241,12 +1323,14 @@ int dummy; module_name = self.target_swift_modulename(target) swiftc = target.compilers['swift'] abssrc = [] + relsrc = [] abs_headers = [] header_imports = [] for i in target.get_sources(): if swiftc.can_compile(i): - relsrc = i.rel_to_builddir(self.build_to_src) - abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), relsrc)) + rels = i.rel_to_builddir(self.build_to_src) + abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), rels)) + relsrc.append(rels) abssrc.append(abss) elif self.environment.is_header(i): relh = i.rel_to_builddir(self.build_to_src) @@ -1330,6 +1414,8 @@ int dummy; elem.write(outfile) else: raise MesonException('Swift supports only executable and static library targets.') + # Introspection information + self.create_target_source_introspection(target, swiftc, compile_args + header_imports + module_includes, relsrc, rel_generated) def generate_static_link_rules(self, is_cross, outfile): num_pools = self.environment.coredata.backend_options['backend_max_links'].value @@ -2049,6 +2135,12 @@ rule FORTRAN_DEP_HACK%s commands = self._generate_single_compile(target, compiler, is_generated) commands = CompilerArgs(commands.compiler, commands) + # Create introspection information + if is_generated is False: + self.create_target_source_introspection(target, compiler, commands, [src], []) + else: + self.create_target_source_introspection(target, compiler, commands, [], [src]) + build_dir = self.environment.get_build_dir() if isinstance(src, File): rel_src = src.rel_to_builddir(self.build_to_src) @@ -2663,6 +2755,15 @@ rule FORTRAN_DEP_HACK%s elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '') elem.write(outfile) + def get_introspection_data(self, target_id, target): + if target_id not in self.introspection_data or len(self.introspection_data[target_id]) == 0: + return super().get_introspection_data(target_id, target) + + result = self.introspection_data[target_id].copy() + for i in result: + i.pop('id_hash') + return result + def load(build_dir): filename = os.path.join(build_dir, 'meson-private', 'install.dat') with open(filename, 'rb') as f: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index e10b95bf0..9372ed897 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -272,7 +272,7 @@ def list_target_files(target_name, targets): raise RuntimeError('Target with the ID "{}" could not be found'.format(target_name)) for i in tgt['sources']: - result += i['source_files'] + result += i['sources'] + i['generated_sources'] return ('target_files', result) diff --git a/run_unittests.py b/run_unittests.py index 0c313d26c..f3f3fa9c8 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -2562,6 +2562,7 @@ int main(int argc, char **argv) { for t in t_intro: id = t['id'] tf_intro = self.introspect(['--target-files', id]) + tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) self.assertEqual(tf_intro, expected[id]) self.wipe() @@ -2576,6 +2577,9 @@ int main(int argc, char **argv) { for t in t_intro: id = t['id'] tf_intro = self.introspect(['--target-files', id]) + print(tf_intro) + tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) + print(tf_intro) self.assertEqual(tf_intro, expected[id]) self.wipe() @@ -3179,7 +3183,8 @@ recommended as it is not supported on some platforms''') ('language', str), ('compiler', list), ('parameters', list), - ('source_files', list), + ('sources', list), + ('generated_sources', list), ] # First load all files From fde10eaee9886f5834a4ab92deadfd39b2d05424 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 11 Dec 2018 17:59:35 +0100 Subject: [PATCH 27/63] Updated the docs --- docs/markdown/IDE-integration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index bd5dff26e..6fb232161 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -58,7 +58,8 @@ The `intro-sources.json` file stores a list of all source objects of the target. "language": "language ID", "compiler": ["The", "compiler", "command"], "parameters": ["list", "of", "compiler", "parameters"], - "source_files": ["list", "of", "all", "source", "files", "for", "this", "language"] + "sources": ["list", "of", "all", "source", "files", "for", "this", "language"], + "generated_sources": ["list", "of", "all", "soruce", "files", "that", "where", "generated", "somewhere", "else"] } ``` From f75e21929758f841d5cd39901a7b90c871f865d3 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 11 Dec 2018 20:40:25 +0100 Subject: [PATCH 28/63] Simplified code and improved preformance --- mesonbuild/backend/ninjabackend.py | 36 +++++++++--------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 06b450678..56e2ac5ae 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -151,7 +151,6 @@ class NinjaBackend(backends.Backend): self.fortran_deps = {} self.all_outputs = {} self.introspection_data = {} - self._intro_last_index = 0 def create_target_alias(self, to_target, outfile): # We need to use aliases for targets that might be used as directory @@ -329,38 +328,24 @@ int dummy; Internal introspection storage formart: self.introspection_data = { - '': [ - { + '': { + : { 'language: 'lang', 'compiler': ['comp', 'exe', 'list'], 'parameters': ['UNIQUE', 'parameter', 'list'], 'sources': [], 'generated_sources': [], - 'id_hash': 634523234445 # Internal unique hash to identify a compiler / language / paramerter combo } - ] + } } ''' build_dir = self.environment.get_build_dir() id = target.get_id() lang = comp.get_language() tgt = self.introspection_data[id] - id_hash = hash((lang, CompilerArgs)) - src_block = None # Find an existing entry or create a new one - # ... first check the last used index - if self._intro_last_index < len(tgt): - tmp = tgt[self._intro_last_index] - if tmp['id_hash'] == id_hash: - src_block = tmp - # ... check all entries - if src_block is None: - for idx, i in enumerate(tgt): - if i['id_hash'] == id_hash: - src_block = i - self._intro_last_index = idx - break - # ... create a new one + id_hash = (lang, CompilerArgs) + src_block = tgt.get(id_hash, None) if src_block is None: # Convert parameters if isinstance(parameters, CompilerArgs): @@ -377,10 +362,9 @@ int dummy; 'parameters': parameters, 'sources': [], 'generated_sources': [], - 'id_hash': id_hash } self._intro_last_index = len(tgt) - tgt.append(src_block) + tgt[id_hash] = src_block # Make source files absolute sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in sources] sources = [os.path.normpath(os.path.join(build_dir, x)) for x in sources] @@ -400,7 +384,7 @@ int dummy; return self.processed_targets[name] = True # Initialize an empty introspection source list - self.introspection_data[name] = [] + self.introspection_data[name] = {} # Generate rules for all dependency targets self.process_target_dependencies(target, outfile) # If target uses a language that cannot link to C objects, @@ -2759,9 +2743,9 @@ rule FORTRAN_DEP_HACK%s if target_id not in self.introspection_data or len(self.introspection_data[target_id]) == 0: return super().get_introspection_data(target_id, target) - result = self.introspection_data[target_id].copy() - for i in result: - i.pop('id_hash') + result = [] + for _, i in self.introspection_data[target_id].items(): + result += [i] return result def load(build_dir): From eb2cc9eccd240fd76c290b77bd28c8285b313b5c Mon Sep 17 00:00:00 2001 From: textshell Date: Fri, 28 Dec 2018 21:18:01 +0100 Subject: [PATCH 29/63] Update docs/markdown/snippets/introspect_multiple.md Co-Authored-By: mensinda --- docs/markdown/snippets/introspect_multiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 151421950..38bd90cc2 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -13,7 +13,7 @@ JSON output (the default is still compact JSON) and foce use the new output format, even if only one introspection command was given. A complete introspection dump is also stored in the `meson-info` -directory. This dump will alwys be (re)generated on every meson run. +directory. This dump will be (re)generated each time meson updates the configuration of the build directory. Additionlly the format of target was changed: - `filename` is now a list of output filenames From c1838d9e4ce6539b6d6694ea0a7935ad07521006 Mon Sep 17 00:00:00 2001 From: textshell Date: Fri, 28 Dec 2018 21:18:39 +0100 Subject: [PATCH 30/63] Update docs/markdown/snippets/introspect_multiple.md Co-Authored-By: mensinda --- docs/markdown/snippets/introspect_multiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 38bd90cc2..75580d379 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -15,7 +15,7 @@ output format, even if only one introspection command was given. A complete introspection dump is also stored in the `meson-info` directory. This dump will be (re)generated each time meson updates the configuration of the build directory. -Additionlly the format of target was changed: +Additionlly the format of `meson introspect target` was changed: - `filename` is now a list of output filenames - `install_filename` is now also a list of installed files - New: the `sources` key. It stores the source files of a target and there compiler parameters From 7eb8621ea6d7a1a50005b80460c30722e3680007 Mon Sep 17 00:00:00 2001 From: textshell Date: Fri, 28 Dec 2018 21:19:28 +0100 Subject: [PATCH 31/63] Update run_unittests.py Co-Authored-By: mensinda --- run_unittests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_unittests.py b/run_unittests.py index f3f3fa9c8..821ab0969 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3257,7 +3257,7 @@ recommended as it is not supported on some platforms''') assertKeyTypes(targets_sources_typelist, j) self.assertDictEqual(targets_to_find, {}) - def test_introspect_file_dump_eauals_all(self): + def test_introspect_file_dump_equals_all(self): testdir = os.path.join(self.unit_test_dir, '49 introspection') self.init(testdir) res_all = self.introspect('--all') From 7cf0e307074f11028c383a85616ff7c5a3a1b529 Mon Sep 17 00:00:00 2001 From: textshell Date: Fri, 28 Dec 2018 21:20:15 +0100 Subject: [PATCH 32/63] Update docs/markdown/IDE-integration.md Co-Authored-By: mensinda --- docs/markdown/IDE-integration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 6fb232161..765c2c412 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -59,7 +59,7 @@ The `intro-sources.json` file stores a list of all source objects of the target. "compiler": ["The", "compiler", "command"], "parameters": ["list", "of", "compiler", "parameters"], "sources": ["list", "of", "all", "source", "files", "for", "this", "language"], - "generated_sources": ["list", "of", "all", "soruce", "files", "that", "where", "generated", "somewhere", "else"] + "generated_sources": ["list", "of", "all", "source", "files", "that", "where", "generated", "somewhere", "else"] } ``` @@ -155,4 +155,4 @@ This API can also work without a build directory for the `--projectinfo` command - [Gnome Builder](https://wiki.gnome.org/Apps/Builder) - [Eclipse CDT](https://www.eclipse.org/cdt/) (experimental) -- [Meson Cmake Wrapper](https://github.com/prozum/meson-cmake-wrapper) (for cmake IDEs) \ No newline at end of file +- [Meson Cmake Wrapper](https://github.com/prozum/meson-cmake-wrapper) (for cmake IDEs) From 2e81631d0c892d4842412c5244d9374b390f3787 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 10:59:58 +0100 Subject: [PATCH 33/63] Keep 'filename' and 'install_filename' as strings --- docs/markdown/snippets/introspect_multiple.md | 3 +-- mesonbuild/mintro.py | 5 ++-- run_unittests.py | 25 ++++++++++++------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 75580d379..53278e6af 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -16,6 +16,5 @@ A complete introspection dump is also stored in the `meson-info` directory. This dump will be (re)generated each time meson updates the configuration of the build directory. Additionlly the format of `meson introspect target` was changed: - - `filename` is now a list of output filenames - - `install_filename` is now also a list of installed files + - New: the `sources` key. It stores the source files of a target and there compiler parameters diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 9372ed897..4de16ca3f 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -99,14 +99,15 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) 'name': target.get_basename(), 'id': idname, 'type': target.get_typename(), - 'filename': fname, + 'filename': fname[0], # TODO Change this to the full list in a seperate PR 'build_by_default': target.build_by_default, 'sources': backend.get_introspection_data(idname, target) } if installdata and target.should_install(): t['installed'] = True - t['install_filename'] = [intall_lookuptable.get(x, None) for x in target.get_outputs()] + # TODO Change this to the full list in a seperate PR + t['install_filename'] = [intall_lookuptable.get(x, None) for x in target.get_outputs()][0] else: t['installed'] = False tlist.append(t) diff --git a/run_unittests.py b/run_unittests.py index 821ab0969..69fac132e 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1439,7 +1439,7 @@ class AllPlatformTests(BasePlatformTests): # Get name of static library targets = self.introspect('--targets') self.assertEqual(len(targets), 1) - libname = targets[0]['filename'][0] + libname = targets[0]['filename'] # TODO Change filename back to a list again # Build and get contents of static library self.build() before = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split() @@ -1496,13 +1496,16 @@ class AllPlatformTests(BasePlatformTests): intro = self.introspect('--targets') if intro[0]['type'] == 'executable': intro = intro[::-1] - self.assertPathListEqual(intro[0]['install_filename'], ['/usr/lib/libstat.a']) - self.assertPathListEqual(intro[1]['install_filename'], ['/usr/bin/prog' + exe_suffix]) + self.assertPathListEqual([intro[0]['install_filename']], ['/usr/lib/libstat.a']) + self.assertPathListEqual([intro[1]['install_filename']], ['/usr/bin/prog' + exe_suffix]) def test_install_introspection_multiple_outputs(self): ''' Tests that the Meson introspection API exposes multiple install filenames correctly without crashing https://github.com/mesonbuild/meson/pull/4555 + + Reverted to the first file only because of https://github.com/mesonbuild/meson/pull/4547#discussion_r244173438 + TODO Change the format to a list officialy in a followup PR ''' if self.backend is not Backend.ninja: raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name)) @@ -1511,10 +1514,14 @@ class AllPlatformTests(BasePlatformTests): intro = self.introspect('--targets') if intro[0]['type'] == 'executable': intro = intro[::-1] - self.assertPathListEqual(intro[0]['install_filename'], ['/usr/include/diff.h', '/usr/bin/diff.sh']) - self.assertPathListEqual(intro[1]['install_filename'], ['/opt/same.h', '/opt/same.sh']) - self.assertPathListEqual(intro[2]['install_filename'], ['/usr/include/first.h', None]) - self.assertPathListEqual(intro[3]['install_filename'], [None, '/usr/bin/second.sh']) + #self.assertPathListEqual(intro[0]['install_filename'], ['/usr/include/diff.h', '/usr/bin/diff.sh']) + #self.assertPathListEqual(intro[1]['install_filename'], ['/opt/same.h', '/opt/same.sh']) + #self.assertPathListEqual(intro[2]['install_filename'], ['/usr/include/first.h', None]) + #self.assertPathListEqual(intro[3]['install_filename'], [None, '/usr/bin/second.sh']) + self.assertPathListEqual([intro[0]['install_filename']], ['/usr/include/diff.h']) + self.assertPathListEqual([intro[1]['install_filename']], ['/opt/same.h']) + self.assertPathListEqual([intro[2]['install_filename']], ['/usr/include/first.h']) + self.assertPathListEqual([intro[3]['install_filename']], [None]) def test_uninstall(self): exename = os.path.join(self.installdir, 'usr/bin/prog' + exe_suffix) @@ -3173,7 +3180,7 @@ recommended as it is not supported on some platforms''') ('name', str), ('id', str), ('type', str), - ('filename', list), + ('filename', str), ('build_by_default', bool), ('sources', list), ('installed', bool), @@ -4396,7 +4403,7 @@ class LinuxlikeTests(BasePlatformTests): break self.assertIsInstance(docbook_target, dict) ifile = self.introspect(['--target-files', 'generated-gdbus-docbook@cus'])[0] - self.assertListEqual(t['filename'], ['gdbus/generated-gdbus-doc-' + os.path.basename(ifile)]) + self.assertListEqual([t['filename']], ['gdbus/generated-gdbus-doc-' + os.path.basename(ifile)]) def test_build_rpath(self): if is_cygwin(): From 1268597df590413539099f0f3797fae7bcb5c09a Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 11:09:08 +0100 Subject: [PATCH 34/63] Slight modification of the wording --- docs/markdown/IDE-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 765c2c412..4b5c3605a 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -12,7 +12,7 @@ The first thing to do when setting up a Meson project in an IDE is to select the meson builddir -With this command meson will configure the project and also generate introspection information that is stored in `intro-*.json` files in the `meson-info` directory. All files will be automatically updated when meson is (re)configured, or the build options change. Thus, an IDE can watch for changes in this directory to know when something changed. +With this command meson will configure the project and also generate introspection information that is stored in `intro-*.json` files in the `meson-info` directory. The introspection dump will be automatically updated when meson is (re)configured, or the build options change. Thus, an IDE can watch for changes in this directory to know when something changed. The `meson-info` directory should contain the following files: From 5c39dd0668ffe301aee66453b86591bbdb29a954 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 11:31:03 +0100 Subject: [PATCH 35/63] Doc updates and throw if no target type is set --- docs/markdown/IDE-integration.md | 3 ++- mesonbuild/backend/backends.py | 8 +------- mesonbuild/build.py | 15 ++++++++------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 4b5c3605a..639f4a02e 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -74,7 +74,8 @@ The following table shows all valid types for a target. `shared library` | Target for a shared library `shared module` | A shared library that is meant to be used with dlopen rather than linking into something else `custom` | A custom target - `unknown target` | The current target format is unknown. This is probably a bug + `run` | A Meson run target + `jar` | A Java JAR target ## Build Options diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b3b6df93c..0291e7e98 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1183,10 +1183,4 @@ class Backend: 'generated_sources': [] }] - return [{ - 'language': 'unknown', - 'compiler': [], - 'parameters': [], - 'sources': [], - 'generated_sources': [] - }] + return [] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 642c2d5f0..5a243a446 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -345,7 +345,8 @@ a hard error in the future.''' % name) self.install = False self.build_always_stale = False self.option_overrides = {} - self.typename = 'unknown target' + if not hasattr(self, 'typename'): + raise RuntimeError('Target type is not set for target class "{}". This is a bug'.format(type(self).__name__)) def get_install_dir(self, environment): # Find the installation directory. @@ -1365,10 +1366,10 @@ class Executable(BuildTarget): known_kwargs = known_exe_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): + self.typename = 'executable' if 'pie' not in kwargs and 'b_pie' in environment.coredata.base_options: kwargs['pie'] = environment.coredata.base_options['b_pie'].value super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) - self.typename = 'executable' # Unless overridden, executables have no suffix or prefix. Except on # Windows and with C#/Mono executables where the suffix is 'exe' if not hasattr(self, 'prefix'): @@ -1455,10 +1456,10 @@ class StaticLibrary(BuildTarget): known_kwargs = known_stlib_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): + self.typename = 'static library' if 'pic' not in kwargs and 'b_staticpic' in environment.coredata.base_options: kwargs['pic'] = environment.coredata.base_options['b_staticpic'].value super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) - self.typename = 'static library' if 'cs' in self.compilers: raise InvalidArguments('Static libraries not supported for C#.') if 'rust' in self.compilers: @@ -1515,6 +1516,7 @@ class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): + self.typename = 'shared library' self.soversion = None self.ltversion = None # Max length 2, first element is compatibility_version, second is current_version @@ -1527,7 +1529,6 @@ class SharedLibrary(BuildTarget): # The import library that GCC would generate (and prefer) self.gcc_import_filename = None super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) - self.typename = 'shared library' if 'rust' in self.compilers: # If no crate type is specified, or it's the generic lib type, use dylib if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib': @@ -1850,8 +1851,8 @@ class CustomTarget(Target): ]) def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False): - super().__init__(name, subdir, subproject, False) self.typename = 'custom' + super().__init__(name, subdir, subproject, False) self.dependencies = [] self.extra_depends = [] self.depend_files = [] # Files that this target depends on but are not on the command line. @@ -2092,8 +2093,8 @@ class CustomTarget(Target): class RunTarget(Target): def __init__(self, name, command, args, dependencies, subdir, subproject): - super().__init__(name, subdir, subproject, False) self.typename = 'run' + super().__init__(name, subdir, subproject, False) self.command = command self.args = args self.dependencies = dependencies @@ -2130,6 +2131,7 @@ class Jar(BuildTarget): known_kwargs = known_jar_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): + self.typename = 'jar' super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) for s in self.sources: if not s.endswith('.java'): @@ -2137,7 +2139,6 @@ class Jar(BuildTarget): for t in self.link_targets: if not isinstance(t, Jar): raise InvalidArguments('Link target %s is not a jar target.' % t) - self.typename = 'jar' self.filename = self.name + '.jar' self.outputs = [self.filename] self.java_args = kwargs.get('java_args', []) From 84948ea6cd61c54404d6e0df82594a56e19fe01f Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 11:37:50 +0100 Subject: [PATCH 36/63] Renamed `--force-new` to `--force-dict-output` --- docs/markdown/snippets/introspect_multiple.md | 2 +- mesonbuild/mintro.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 53278e6af..97fdf8c81 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -7,7 +7,7 @@ object. The format for a single command was not changed to keep backward compatibility. -Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-new` +Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-dict-output` were added to print all introspection information in one go, format the JSON output (the default is still compact JSON) and foce use the new output format, even if only one introspection command was given. diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 4de16ca3f..4a20e6bfa 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -62,7 +62,7 @@ def add_arguments(parser): help='Print all available information.') parser.add_argument('-i', '--indent', dest='indent', type=int, default=0, help='Number of spaces used for indentation.') - parser.add_argument('-f', '--force-new', action='store_true', dest='force_new', default=False, + parser.add_argument('-f', '--force-dict-output', action='store_true', dest='force_dict', default=False, help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') @@ -504,10 +504,10 @@ def run(options): indent = options.indent if options.indent > 0 else None - if len(results) == 0 and not options.force_new: + if len(results) == 0 and not options.force_dict: print('No command specified') return 1 - elif len(results) == 1 and not options.force_new: + 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: From 35887861388eb7d84998bc27b8b3531ae124c648 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 11:51:32 +0100 Subject: [PATCH 37/63] Some small fixes --- mesonbuild/build.py | 7 ++++++- mesonbuild/mintro.py | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5a243a446..91edbb8b0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2122,7 +2122,12 @@ class RunTarget(Target): return self.name def get_outputs(self): - return [self.name] + if isinstance(self.name, str): + return [self.name] + elif isinstance(self.name, list): + return self.name + else: + raise RuntimeError('RunTarget: self.name is neither a list nor a string. This is a bug') def type_suffix(self): return "@run" diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 4a20e6bfa..9444b964d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -84,14 +84,14 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) tlist = [] # Fast lookup table for installation files - intall_lookuptable = {} + install_lookuptable = {} for i in installdata.targets: outname = os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname)) - intall_lookuptable[os.path.basename(i.fname)] = str(pathlib.PurePath(outname)) + install_lookuptable[os.path.basename(i.fname)] = str(pathlib.PurePath(outname)) for (idname, target) in builddata.get_targets().items(): if not isinstance(target, build.Target): - raise RuntimeError('Something weird happened. File a bug.') + raise RuntimeError('The target object in `builddata.get_targets()` is not of type `build.Target`. Please file a bug with this error message.') fname = [os.path.join(target.subdir, x) for x in target.get_outputs()] @@ -107,7 +107,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) if installdata and target.should_install(): t['installed'] = True # TODO Change this to the full list in a seperate PR - t['install_filename'] = [intall_lookuptable.get(x, None) for x in target.get_outputs()][0] + t['install_filename'] = [install_lookuptable.get(x, None) for x in target.get_outputs()][0] else: t['installed'] = False tlist.append(t) From c33df1fd7377ed6f2116c61b19cac1825ae68f86 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 12:25:37 +0100 Subject: [PATCH 38/63] Revert to old behavior --- mesonbuild/mintro.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 9444b964d..fa92a1ae1 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -93,13 +93,16 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) if not isinstance(target, build.Target): raise RuntimeError('The target object in `builddata.get_targets()` is not of type `build.Target`. Please file a bug with this error message.') + # TODO Change this to the full list in a seperate PR fname = [os.path.join(target.subdir, x) for x in target.get_outputs()] + if len(fname) == 1: + fname = fname[0] t = { 'name': target.get_basename(), 'id': idname, 'type': target.get_typename(), - 'filename': fname[0], # TODO Change this to the full list in a seperate PR + 'filename': fname, 'build_by_default': target.build_by_default, 'sources': backend.get_introspection_data(idname, target) } @@ -260,7 +263,7 @@ def list_buildoptions_from_source(sourcedir, backend): mlog.enable() list_buildoptions(intr.coredata) -def list_target_files(target_name, targets): +def list_target_files(target_name, targets, builddata: build.Build): result = [] tgt = None @@ -270,11 +273,15 @@ def list_target_files(target_name, targets): break if tgt is None: - raise RuntimeError('Target with the ID "{}" could not be found'.format(target_name)) + print('Target with the ID "{}" could not be found'.format(target_name)) + sys.exit(1) for i in tgt['sources']: result += i['sources'] + i['generated_sources'] + # TODO Remove this line in a future PR with other breaking changes + result = list(map(lambda x: os.path.relpath(x, builddata.environment.get_source_dir()), result)) + return ('target_files', result) def list_buildoptions(coredata: cdata.CoreData): @@ -490,7 +497,8 @@ def run(options): targets_file = os.path.join(infodir, 'intro-targets.json') with open(targets_file, 'r') as fp: targets = json.load(fp) - results += [list_target_files(options.target_files, targets)] + builddata = build.load(options.builddir) # TODO remove this in a breaking changes PR + results += [list_target_files(options.target_files, targets, builddata)] if options.all or options.tests: toextract += ['tests'] From 02734cc5c34faabe8ec0685139451f8349469993 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 12:38:55 +0100 Subject: [PATCH 39/63] Better documentation --- docs/markdown/snippets/introspect_multiple.md | 1 + mesonbuild/mintro.py | 6 ++++-- run_unittests.py | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 97fdf8c81..2a885e41f 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -18,3 +18,4 @@ directory. This dump will be (re)generated each time meson updates the configura Additionlly the format of `meson introspect target` was changed: - New: the `sources` key. It stores the source files of a target and there compiler parameters + - Added new target types (`jar`, `shared module`) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index fa92a1ae1..ce9d81e8d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -472,8 +472,10 @@ def run(options): list_buildoptions_from_source(sourcedir, options.backend) return 0 if not os.path.isdir(datadir) or not os.path.isdir(infodir): - print('Current directory is not a build dir. Please specify it or ' - 'change the working directory to it.') + print('Current directory is not a meson build directory.' + 'Please specify a valid build dir or change the working directory to it.' + 'It is also possible that the build directory was generated with an old' + 'meson version. Please regenerate it in this case.') return 1 results = [] diff --git a/run_unittests.py b/run_unittests.py index 69fac132e..349375e3e 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -2569,7 +2569,7 @@ int main(int argc, char **argv) { for t in t_intro: id = t['id'] tf_intro = self.introspect(['--target-files', id]) - tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) + #tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) TODO make paths absolute in future PR self.assertEqual(tf_intro, expected[id]) self.wipe() @@ -2585,7 +2585,7 @@ int main(int argc, char **argv) { id = t['id'] tf_intro = self.introspect(['--target-files', id]) print(tf_intro) - tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) + #tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) TODO make paths absolute in future PR print(tf_intro) self.assertEqual(tf_intro, expected[id]) self.wipe() From 2487e0a1fbf8ac33b95467f7428afd78a39537ed Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 12:54:25 +0100 Subject: [PATCH 40/63] Simplefied absolute path generation --- mesonbuild/backend/backends.py | 2 ++ mesonbuild/backend/ninjabackend.py | 11 +++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 0291e7e98..5e81d6b48 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -156,6 +156,8 @@ class Backend: self.build = build self.environment = build.environment self.processed_targets = {} + self.build_dir = self.environment.get_build_dir() + self.source_dir = self.environment.get_source_dir() self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 56e2ac5ae..818c9793a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -339,7 +339,6 @@ int dummy; } } ''' - build_dir = self.environment.get_build_dir() id = target.get_id() lang = comp.get_language() tgt = self.introspection_data[id] @@ -352,7 +351,7 @@ int dummy; parameters = parameters.to_native(copy=True) for idx, i in enumerate(parameters): if i[:2] == '-I' or i[:2] == '/I' or i[:2] == '-L': - parameters[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + parameters[idx] = i[:2] + os.path.normpath(os.path.join(self.build_dir, i[2:])) if target.is_cross: parameters += comp.get_cross_extra_flags(self.environment, False) # The new entry @@ -366,10 +365,10 @@ int dummy; self._intro_last_index = len(tgt) tgt[id_hash] = src_block # Make source files absolute - sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in sources] - sources = [os.path.normpath(os.path.join(build_dir, x)) for x in sources] - generated_sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in generated_sources] - generated_sources = [os.path.normpath(os.path.join(build_dir, x)) for x in generated_sources] + sources = [x.absolute_path(self.source_dir, self.build_dir) if isinstance(x, File) else os.path.normpath(os.path.join(self.build_dir, x)) + for x in sources] + generated_sources = [x.absolute_path(self.source_dir, self.build_dir) if isinstance(x, File) else os.path.normpath(os.path.join(self.build_dir, x)) + for x in generated_sources] # Add the source files src_block['sources'] += sources src_block['generated_sources'] += generated_sources From 97429279031d9b9f0ad9e2e66511c67d2dd0c6a0 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 13:37:41 +0100 Subject: [PATCH 41/63] Absolute path generation refactoring --- mesonbuild/backend/ninjabackend.py | 8 +++----- mesonbuild/compilers/__init__.py | 2 ++ mesonbuild/compilers/c.py | 9 +++++++++ mesonbuild/compilers/compilers.py | 31 ++++++++++++++++++++++++++++++ mesonbuild/compilers/cs.py | 3 +++ mesonbuild/compilers/d.py | 14 ++++++++++++++ mesonbuild/compilers/fortran.py | 7 +++++++ mesonbuild/compilers/java.py | 3 +++ mesonbuild/compilers/rust.py | 3 +++ mesonbuild/compilers/swift.py | 7 +++++++ mesonbuild/compilers/vala.py | 3 +++ 11 files changed, 85 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 818c9793a..1b8c8ec91 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -29,7 +29,7 @@ from .. import build from .. import mlog from .. import dependencies from .. import compilers -from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler +from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler, Compiler from ..linkers import ArLinker from ..mesonlib import File, MesonException, OrderedSet from ..mesonlib import get_compiler_for_source, has_path_sep @@ -322,7 +322,7 @@ int dummy; return False return True - def create_target_source_introspection(self, target, comp, parameters, sources, generated_sources): + def create_target_source_introspection(self, target: build.Target, comp: compilers.Compiler, parameters, sources, generated_sources): ''' Adds the source file introspection information for a language of a target @@ -349,9 +349,7 @@ int dummy; # Convert parameters if isinstance(parameters, CompilerArgs): parameters = parameters.to_native(copy=True) - for idx, i in enumerate(parameters): - if i[:2] == '-I' or i[:2] == '/I' or i[:2] == '-L': - parameters[idx] = i[:2] + os.path.normpath(os.path.join(self.build_dir, i[2:])) + parameters = comp.compute_parameters_with_absolute_paths(parameters, self.build_dir) if target.is_cross: parameters += comp.get_cross_extra_flags(self.environment, False) # The new entry diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 31b7b897b..b5b2475dd 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -15,6 +15,7 @@ # Public symbols for compilers sub-package when using 'from . import compilers' __all__ = [ 'CompilerType', + 'Compiler', 'all_languages', 'base_options', @@ -91,6 +92,7 @@ __all__ = [ # Bring symbols from each module into compilers sub-package namespace from .compilers import ( CompilerType, + Compiler, all_languages, base_options, clib_langs, diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 92a9fa6d7..6350eee9a 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -1475,6 +1475,15 @@ class VisualStudioCCompiler(CCompiler): # msvc does not have a concept of system header dirs. return ['-I' + path] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I' or i[:2] == '/I': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + elif i[:9] == '/LIBPATH:': + parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:])) + + return parameter_list + # Visual Studio is special. It ignores some arguments it does not # understand and you can't tell it to error out on those. # http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 2be6ef124..3ef4ffcc1 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -884,6 +884,9 @@ class Compiler: def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): raise EnvironmentException('%s does not support compute_int ' % self.get_id()) + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + raise EnvironmentException('%s does not support compute_parameters_with_absolute_paths ' % self.get_id()) + def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None): raise EnvironmentException('%s does not support has_member(s) ' % self.get_id()) @@ -1547,6 +1550,13 @@ class GnuLikeCompiler(abc.ABC): return ['-mwindows'] return [] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I' or i[:2] == '-L': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + class GnuCompiler(GnuLikeCompiler): """ GnuCompiler represents an actual GCC in its many incarnations. @@ -1776,6 +1786,13 @@ class ArmclangCompiler: """ return ['--symdefs=' + implibname] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I' or i[:2] == '-L': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + # Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1, 19.0.0 class IntelCompiler(GnuLikeCompiler): @@ -1910,6 +1927,13 @@ class ArmCompiler: def get_debug_args(self, is_debug): return clike_debug_args[is_debug] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I' or i[:2] == '-L': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + class CcrxCompiler: def __init__(self, compiler_type): if not self.is_cross: @@ -2003,3 +2027,10 @@ class CcrxCompiler: continue result.append(i) return result + + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:9] == '-include=': + parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:])) + + return parameter_list diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index a6c74d260..a504f80fe 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -88,6 +88,9 @@ class CsCompiler(Compiler): def get_pic_args(self): return [] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + return parameter_list + def name_string(self): return ' '.join(self.exelist) diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 2cf0fbd92..a3e336983 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -111,6 +111,13 @@ class DCompiler(Compiler): def get_include_args(self, path, is_system): return ['-I=' + path] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:3] == '-I=': + parameter_list[idx] = i[:3] + os.path.normpath(os.path.join(build_dir, i[3:])) + + return parameter_list + def get_warn_args(self, level): return ['-wi'] @@ -511,6 +518,13 @@ class GnuDCompiler(DCompiler): def get_buildtype_args(self, buildtype): return d_gdc_buildtype_args[buildtype] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 75db26d5b..456e63e34 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -171,6 +171,13 @@ end program prog def get_module_outdir_args(self, path): return ['-module', path] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + def module_name_to_filename(self, module_name): return module_name.lower() + '.mod' diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 978562ca1..89d4c9c2c 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -81,6 +81,9 @@ class JavaCompiler(Compiler): def get_buildtype_args(self, buildtype): return java_buildtype_args[buildtype] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + return parameter_list + def sanity_check(self, work_dir, environment): src = 'SanityCheck.java' obj = 'SanityCheck' diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 93c291786..b5695662b 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -82,3 +82,6 @@ class RustCompiler(Compiler): def get_optimization_args(self, optimization_level): return rust_optimization_args[optimization_level] + + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + return parameter_list diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 4d5dd0cfe..1d12cbcdc 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -91,6 +91,13 @@ class SwiftCompiler(Compiler): def get_compile_only_args(self): return ['-c'] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-I': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + def sanity_check(self, work_dir, environment): src = 'swifttest.swift' source_name = os.path.join(work_dir, src) diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 46bb210e9..bfe48f403 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -66,6 +66,9 @@ class ValaCompiler(Compiler): return ['--color=' + colortype] return [] + def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + return parameter_list + def sanity_check(self, work_dir, environment): code = 'class MesonSanityCheck : Object { }' args = self.get_cross_extra_flags(environment, link=False) From e1719c5d37c82233c27ae733ab9b1ecbb781a27f Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 14:31:04 +0100 Subject: [PATCH 42/63] Load coredata --- mesonbuild/mintro.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index ce9d81e8d..caa69824d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -478,6 +478,10 @@ def run(options): 'meson version. Please regenerate it in this case.') return 1 + # Load build data to make sure that the version matches + # TODO Find a better solution for this + _ = cdata.load(options.builddir) + results = [] toextract = [] From 63e16fbcc31ed616b713bf83b4b1079ee1b7f617 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 14:34:53 +0100 Subject: [PATCH 43/63] Fixed flake8 --- mesonbuild/backend/ninjabackend.py | 2 +- mesonbuild/mintro.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 1b8c8ec91..23da39e8e 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -29,7 +29,7 @@ from .. import build from .. import mlog from .. import dependencies from .. import compilers -from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler, Compiler +from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler from ..linkers import ArLinker from ..mesonlib import File, MesonException, OrderedSet from ..mesonlib import get_compiler_for_source, has_path_sep diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index caa69824d..63cf7fb9b 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -480,7 +480,7 @@ def run(options): # Load build data to make sure that the version matches # TODO Find a better solution for this - _ = cdata.load(options.builddir) + cdata.load(options.builddir) results = [] toextract = [] From 8ac5b36c70ba0cee69c791576160641807aec032 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 15:04:24 +0100 Subject: [PATCH 44/63] Fixed mac unit test --- run_unittests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_unittests.py b/run_unittests.py index 349375e3e..4b4d86b03 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3754,7 +3754,7 @@ class DarwinTests(BasePlatformTests): self.build() targets = {} for t in self.introspect('--targets'): - targets[t['name']] = t['filename'][0] + targets[t['name']] = t['filename'][0] if isinstance(t['filename'], list) else t['filename'] self.assertEqual(self._get_darwin_versions(targets['some']), ('7.0.0', '7.0.0')) self.assertEqual(self._get_darwin_versions(targets['noversion']), ('0.0.0', '0.0.0')) self.assertEqual(self._get_darwin_versions(targets['onlyversion']), ('1.0.0', '1.0.0')) From 1e374feb1ce09e866f9625a3ea378b58fddaf768 Mon Sep 17 00:00:00 2001 From: textshell Date: Sun, 30 Dec 2018 22:34:57 +0100 Subject: [PATCH 45/63] Update docs/markdown/IDE-integration.md Co-Authored-By: mensinda --- docs/markdown/IDE-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 639f4a02e..1b79c7480 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -38,7 +38,7 @@ The most important file for an IDE is probably `intro-targets.json`. Here each t "name": "Name of the target", "id": "The internal ID meson uses", "type": "", - "filename": ["list", "of", "generate", "files"], + "filename": ["list", "of", "generated", "files"], "build_by_default": true / false, "sources": [], "installed": true / false, From fb4bdd3330b5857cc0187dec1c75f307fe8889f8 Mon Sep 17 00:00:00 2001 From: textshell Date: Sun, 30 Dec 2018 22:35:42 +0100 Subject: [PATCH 46/63] Update docs/markdown/IDE-integration.md Co-Authored-By: mensinda --- docs/markdown/IDE-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 1b79c7480..81fd43f20 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -128,7 +128,7 @@ Because of this options for the subprojects can differ. Compilation and unit tests are done as usual by running the `ninja` and `ninja test` commands. A JSON formatted result log can be found in `workspace/project/builddir/meson-logs/testlog.json`. -When these tests fail, the user probably wants to run the failing test in a debugger. To make this as integrated as possible, extract the tests from the `tests` and `benchmarks` entries. +When these tests fail, the user probably wants to run the failing test in a debugger. To make this as integrated as possible, extract the tests from the `intro-tests.json` and `intro-benchmarks.json` files. This provides you with all the information needed to run the test: what command to execute, command line arguments and environment variable settings. ```json From 248adbab9ad0775e513b920e9047b2e93664ae2e Mon Sep 17 00:00:00 2001 From: textshell Date: Sun, 30 Dec 2018 22:35:56 +0100 Subject: [PATCH 47/63] Update docs/markdown/snippets/introspect_multiple.md Co-Authored-By: mensinda --- docs/markdown/snippets/introspect_multiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 2a885e41f..3bbe6ccc2 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -9,7 +9,7 @@ compatibility. Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-dict-output` were added to print all introspection information in one go, format the -JSON output (the default is still compact JSON) and foce use the new +JSON output (the default is still compact JSON) and force use the new output format, even if only one introspection command was given. A complete introspection dump is also stored in the `meson-info` From bd8bad46c3fad6e53e259f73408a73eeca920dc7 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 23:04:59 +0100 Subject: [PATCH 48/63] Code cleanup and renamed variables --- docs/markdown/IDE-integration.md | 4 ++-- docs/markdown/snippets/introspect_multiple.md | 2 +- mesonbuild/backend/ninjabackend.py | 5 ++--- mesonbuild/mintro.py | 8 +++----- run_unittests.py | 4 ++-- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 81fd43f20..d4554aea4 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -40,7 +40,7 @@ The most important file for an IDE is probably `intro-targets.json`. Here each t "type": "", "filename": ["list", "of", "generated", "files"], "build_by_default": true / false, - "sources": [], + "target_sources": [], "installed": true / false, } ``` @@ -51,7 +51,7 @@ A target usually generates only one file. However, it is possible for custom tar ### Target sources -The `intro-sources.json` file stores a list of all source objects of the target. With this information, an IDE can provide code completion for all source files. +The `intro-targets.json` file also stores a list of all source objects of the target in the `target_sources`. With this information, an IDE can provide code completion for all source files. ```json { diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 3bbe6ccc2..0d53d4806 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -7,7 +7,7 @@ object. The format for a single command was not changed to keep backward compatibility. -Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-dict-output` +Furthermore the option `-a,--all`, `-i,--indent` and `-f,--force-object-output` were added to print all introspection information in one go, format the JSON output (the default is still compact JSON) and force use the new output format, even if only one introspection command was given. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 23da39e8e..b7df33a01 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -343,7 +343,7 @@ int dummy; lang = comp.get_language() tgt = self.introspection_data[id] # Find an existing entry or create a new one - id_hash = (lang, CompilerArgs) + id_hash = (lang, parameters) src_block = tgt.get(id_hash, None) if src_block is None: # Convert parameters @@ -351,7 +351,7 @@ int dummy; parameters = parameters.to_native(copy=True) parameters = comp.compute_parameters_with_absolute_paths(parameters, self.build_dir) if target.is_cross: - parameters += comp.get_cross_extra_flags(self.environment, False) + parameters.insert(0, comp.get_cross_extra_flags(self.environment, False)) # The new entry src_block = { 'language': lang, @@ -360,7 +360,6 @@ int dummy; 'sources': [], 'generated_sources': [], } - self._intro_last_index = len(tgt) tgt[id_hash] = src_block # Make source files absolute sources = [x.absolute_path(self.source_dir, self.build_dir) if isinstance(x, File) else os.path.normpath(os.path.join(self.build_dir, x)) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 63cf7fb9b..cba10f34d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -35,8 +35,6 @@ from .compilers import compilers import sys, os import pathlib -INTROSPECTION_OUTPUT_FILE = 'meson-introspection.json' - def add_arguments(parser): parser.add_argument('--targets', action='store_true', dest='list_targets', default=False, help='List top level targets.') @@ -62,7 +60,7 @@ def add_arguments(parser): help='Print all available information.') parser.add_argument('-i', '--indent', dest='indent', type=int, default=0, help='Number of spaces used for indentation.') - parser.add_argument('-f', '--force-dict-output', action='store_true', dest='force_dict', default=False, + parser.add_argument('-f', '--force-object-output', action='store_true', dest='force_dict', default=False, help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') @@ -104,7 +102,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) 'type': target.get_typename(), 'filename': fname, 'build_by_default': target.build_by_default, - 'sources': backend.get_introspection_data(idname, target) + 'target_sources': backend.get_introspection_data(idname, target) } if installdata and target.should_install(): @@ -276,7 +274,7 @@ def list_target_files(target_name, targets, builddata: build.Build): print('Target with the ID "{}" could not be found'.format(target_name)) sys.exit(1) - for i in tgt['sources']: + for i in tgt['target_sources']: result += i['sources'] + i['generated_sources'] # TODO Remove this line in a future PR with other breaking changes diff --git a/run_unittests.py b/run_unittests.py index 4b4d86b03..f7737ab57 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3182,7 +3182,7 @@ recommended as it is not supported on some platforms''') ('type', str), ('filename', str), ('build_by_default', bool), - ('sources', list), + ('target_sources', list), ('installed', bool), ] @@ -3260,7 +3260,7 @@ recommended as it is not supported on some platforms''') self.assertEqual(i['build_by_default'], tgt[1]) self.assertEqual(i['installed'], tgt[2]) targets_to_find.pop(i['name'], None) - for j in i['sources']: + for j in i['target_sources']: assertKeyTypes(targets_sources_typelist, j) self.assertDictEqual(targets_to_find, {}) From 9eca2e46a05870065e3cfa88ae42362d81aa3157 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 30 Dec 2018 23:28:28 +0100 Subject: [PATCH 49/63] Added more compiler option support --- mesonbuild/compilers/cs.py | 6 ++++++ mesonbuild/compilers/d.py | 8 +++++++- mesonbuild/compilers/fortran.py | 2 +- mesonbuild/compilers/java.py | 6 ++++++ mesonbuild/compilers/rust.py | 8 ++++++++ mesonbuild/compilers/swift.py | 2 +- mesonbuild/compilers/vala.py | 10 ++++++++++ 7 files changed, 39 insertions(+), 3 deletions(-) diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index a504f80fe..cbfcd9c9d 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -89,6 +89,12 @@ class CsCompiler(Compiler): return [] def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-L': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + if i[:5] == '-lib:': + parameter_list[idx] = i[:5] + os.path.normpath(os.path.join(build_dir, i[5:])) + return parameter_list def name_string(self): diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index a3e336983..3065ac74d 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -115,6 +115,12 @@ class DCompiler(Compiler): for idx, i in enumerate(parameter_list): if i[:3] == '-I=': parameter_list[idx] = i[:3] + os.path.normpath(os.path.join(build_dir, i[3:])) + if i[:4] == '-L-L': + parameter_list[idx] = i[:4] + os.path.normpath(os.path.join(build_dir, i[4:])) + if i[:5] == '-L=-L': + parameter_list[idx] = i[:5] + os.path.normpath(os.path.join(build_dir, i[5:])) + if i[:6] == '-Wl,-L': + parameter_list[idx] = i[:6] + os.path.normpath(os.path.join(build_dir, i[6:])) return parameter_list @@ -520,7 +526,7 @@ class GnuDCompiler(DCompiler): def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): for idx, i in enumerate(parameter_list): - if i[:2] == '-I': + if i[:2] == '-I' or i[:2] == '-L': parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 456e63e34..8056969aa 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -173,7 +173,7 @@ end program prog def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): for idx, i in enumerate(parameter_list): - if i[:2] == '-I': + if i[:2] == '-I' or i[:2] == '-L': parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 89d4c9c2c..03cc1b078 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -82,6 +82,12 @@ class JavaCompiler(Compiler): return java_buildtype_args[buildtype] def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:4] == '-cp:' or i[:4] == '-cp;': + parameter_list[idx] = i[:4] + os.path.normpath(os.path.join(build_dir, i[4:])) + if i[:11] == '-classpath:' or i[:11] == '-classpath;': + parameter_list[idx] = i[:11] + os.path.normpath(os.path.join(build_dir, i[11:])) + return parameter_list def sanity_check(self, work_dir, environment): diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index b5695662b..68da82361 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -84,4 +84,12 @@ class RustCompiler(Compiler): return rust_optimization_args[optimization_level] def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:2] == '-L': + for j in ['dependency', 'crate', 'native', 'framework', 'all']: + combined_len = len(j) + 3 + if i[:combined_len] == '-L{}='.format(j): + parameter_list[idx] = i[:combined_len] + os.path.normpath(os.path.join(build_dir, i[combined_len:])) + break + return parameter_list diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 1d12cbcdc..eb58d11b3 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -93,7 +93,7 @@ class SwiftCompiler(Compiler): def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): for idx, i in enumerate(parameter_list): - if i[:2] == '-I': + if i[:2] == '-I' or i[:2] == '-L': parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index bfe48f403..e64d57f36 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -67,6 +67,16 @@ class ValaCompiler(Compiler): return [] def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + for idx, i in enumerate(parameter_list): + if i[:9] == '--girdir=': + parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:])) + if i[:10] == '--vapidir=': + parameter_list[idx] = i[:10] + os.path.normpath(os.path.join(build_dir, i[10:])) + if i[:13] == '--includedir=': + parameter_list[idx] = i[:13] + os.path.normpath(os.path.join(build_dir, i[13:])) + if i[:14] == '--metadatadir=': + parameter_list[idx] = i[:14] + os.path.normpath(os.path.join(build_dir, i[14:])) + return parameter_list def sanity_check(self, work_dir, environment): From b97046ea0fd3c121fa1de6790df00893b9eead19 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 00:09:13 +0100 Subject: [PATCH 50/63] Fixed hash --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b7df33a01..2369995c8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -343,7 +343,7 @@ int dummy; lang = comp.get_language() tgt = self.introspection_data[id] # Find an existing entry or create a new one - id_hash = (lang, parameters) + id_hash = (lang, tuple(parameters)) src_block = tgt.get(id_hash, None) if src_block is None: # Convert parameters From 681532452cf3863fe133108c54f99e9fe33c859c Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 00:15:50 +0100 Subject: [PATCH 51/63] Handle java correct --- mesonbuild/compilers/java.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 03cc1b078..dbd1f3fda 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -83,10 +83,10 @@ class JavaCompiler(Compiler): def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): for idx, i in enumerate(parameter_list): - if i[:4] == '-cp:' or i[:4] == '-cp;': - parameter_list[idx] = i[:4] + os.path.normpath(os.path.join(build_dir, i[4:])) - if i[:11] == '-classpath:' or i[:11] == '-classpath;': - parameter_list[idx] = i[:11] + os.path.normpath(os.path.join(build_dir, i[11:])) + if i in ['-cp', '-classpath', '-sourcepath'] and idx + 1 < len(parameter_list): + path_list = parameter_list[idx + 1].replace(';', ':').split(':') + path_list = [os.path.normpath(os.path.join(build_dir, x)) for x in path_list] + parameter_list[idx + 1] = ':'.join(path_list) return parameter_list From 477b69b505c35ffe9fea758df74b1d821f47bf0b Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 00:23:44 +0100 Subject: [PATCH 52/63] Use os.pathsep --- mesonbuild/compilers/java.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index dbd1f3fda..16495116a 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -84,7 +84,7 @@ class JavaCompiler(Compiler): def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): for idx, i in enumerate(parameter_list): if i in ['-cp', '-classpath', '-sourcepath'] and idx + 1 < len(parameter_list): - path_list = parameter_list[idx + 1].replace(';', ':').split(':') + path_list = parameter_list[idx + 1].split(os.pathsep) path_list = [os.path.normpath(os.path.join(build_dir, x)) for x in path_list] parameter_list[idx + 1] = ':'.join(path_list) From ca808a070273ad3dfa579a6f0849b33c561b459a Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 00:28:25 +0100 Subject: [PATCH 53/63] More os.pathsep --- mesonbuild/compilers/java.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 16495116a..03ee38241 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -86,7 +86,7 @@ class JavaCompiler(Compiler): if i in ['-cp', '-classpath', '-sourcepath'] and idx + 1 < len(parameter_list): path_list = parameter_list[idx + 1].split(os.pathsep) path_list = [os.path.normpath(os.path.join(build_dir, x)) for x in path_list] - parameter_list[idx + 1] = ':'.join(path_list) + parameter_list[idx + 1] = os.pathsep.join(path_list) return parameter_list From 9d025f13742827818e8366cc706ec9c865ce1f38 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 00:37:34 +0100 Subject: [PATCH 54/63] Make custom command sources slightly more useful --- mesonbuild/backend/backends.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 5e81d6b48..efd94d738 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1177,9 +1177,15 @@ class Backend: source_list += [os.path.join(self.environment.get_source_dir(), i)] source_list = list(map(lambda x: os.path.normpath(x), source_list)) + compiler = [] + if isinstance(target, build.CustomTarget): + compiler = target.command + if isinstance(compiler, str): + compiler = [compiler] + return [{ 'language': 'unknown', - 'compiler': [], + 'compiler': compiler, 'parameters': [], 'sources': source_list, 'generated_sources': [] From df1434d5a5ec099b6d54b70906210494e85f2c21 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 13:54:08 +0100 Subject: [PATCH 55/63] Fixed CI issues --- mesonbuild/backend/backends.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index efd94d738..39aa36534 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1170,18 +1170,27 @@ class Backend: if isinstance(target, (build.CustomTarget, build.BuildTarget)): source_list_raw = target.sources + target.extra_files source_list = [] - for i in source_list_raw: - if isinstance(i, mesonlib.File): - source_list += [i.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())] - elif isinstance(i, str): - source_list += [os.path.join(self.environment.get_source_dir(), i)] + for j in source_list_raw: + if isinstance(j, mesonlib.File): + source_list += [j.absolute_path(self.source_dir, self.build_dir)] + elif isinstance(j, str): + source_list += [os.path.join(self.source_dir, j)] source_list = list(map(lambda x: os.path.normpath(x), source_list)) compiler = [] if isinstance(target, build.CustomTarget): - compiler = target.command - if isinstance(compiler, str): - compiler = [compiler] + tmp_compiler = target.command + if not isinstance(compiler, list): + tmp_compiler = [compiler] + for j in tmp_compiler: + if isinstance(j, mesonlib.File): + compiler += [j.absolute_path(self.source_dir, self.build_dir)] + elif isinstance(j, str): + compiler += [j] + elif isinstance(j, (build.BuildTarget, build.CustomTarget)): + compiler += j.get_outputs() + else: + raise RuntimeError('Type "{}" is not supported in get_introspection_data. This is a bug'.format(type(j).__name__)) return [{ 'language': 'unknown', From 25618c6a4dd8e2c05d0e19ee19f4e6a5e735ef0c Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 14:27:20 +0100 Subject: [PATCH 56/63] Atomic write --- mesonbuild/mintro.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index cba10f34d..e6782cb0d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -532,8 +532,11 @@ def run(options): def write_intro_info(intro_info, info_dir): for i in intro_info: out_file = os.path.join(info_dir, 'intro-{}.json'.format(i[0])) - with open(out_file, 'w') as fp: + tmp_file = os.path.join(info_dir, 'tmp_dump.json') + with open(tmp_file, 'w') as fp: json.dump(i[1], fp) + fp.flush() # Not sure if this is needed + os.replace(tmp_file, out_file) def generate_introspection_file(builddata: build.Build, backend: backends.Backend): coredata = builddata.environment.get_coredata() From 4b17bbc753a76924e60bde50e6cb91a240fff989 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 31 Dec 2018 16:28:23 +0100 Subject: [PATCH 57/63] Better list prepending --- mesonbuild/backend/ninjabackend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2369995c8..3688f294d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -351,7 +351,10 @@ int dummy; parameters = parameters.to_native(copy=True) parameters = comp.compute_parameters_with_absolute_paths(parameters, self.build_dir) if target.is_cross: - parameters.insert(0, comp.get_cross_extra_flags(self.environment, False)) + extra_parameters = comp.get_cross_extra_flags(self.environment, False) + if isinstance(parameters, CompilerArgs): + extra_parameters = extra_parameters.to_native(copy=True) + parameters = extra_parameters + parameters # The new entry src_block = { 'language': lang, From 9c214f4a70cc82340257b39b9bbfe45b2f4c0c8f Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 1 Jan 2019 18:57:07 +0100 Subject: [PATCH 58/63] Fixed buildoptions form source --- mesonbuild/environment.py | 12 ++---------- mesonbuild/mintro.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 67b58bc9a..e99174ce1 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -336,16 +336,8 @@ class Environment: mlog.log('Reason:', mlog.red(str(e))) coredata.read_cmd_line_file(self.build_dir, options) self.create_new_coredata(options) - except MesonException as e: - # If we stored previous command line options, we can recover from - # a broken/outdated coredata. - if os.path.isfile(coredata.get_cmd_line_file(self.build_dir)): - mlog.warning('Regenerating configuration from scratch.') - mlog.log('Reason:', mlog.red(str(e))) - coredata.read_cmd_line_file(self.build_dir, options) - self.create_new_coredata(options) - else: - raise e + else: + raise e else: # Just create a fresh coredata in this case self.create_new_coredata(options) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index e6782cb0d..6f2cdf634 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -251,7 +251,7 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter): self.parse_project() self.run() -def list_buildoptions_from_source(sourcedir, backend): +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) @@ -259,7 +259,8 @@ def list_buildoptions_from_source(sourcedir, backend): intr.analyze() # Reenable logging just in case mlog.enable() - list_buildoptions(intr.coredata) + buildoptions = list_buildoptions(intr.coredata)[1] + print(json.dumps(buildoptions, indent=indent)) def list_target_files(target_name, targets, builddata: build.Build): result = [] @@ -424,7 +425,7 @@ class ProjectInfoInterperter(astinterpreter.AstInterpreter): self.parse_project() self.run() -def list_projinfo_from_source(sourcedir): +def list_projinfo_from_source(sourcedir, indent): files = find_buildsystem_files_list(sourcedir) result = {'buildsystem_files': []} @@ -453,21 +454,22 @@ def list_projinfo_from_source(sourcedir): subprojects = [obj for name, obj in subprojects.items()] result['subprojects'] = subprojects - print(json.dumps(result)) + print(json.dumps(result, indent=indent)) def run(options): datadir = 'meson-private' infodir = 'meson-info' + indent = options.indent if options.indent > 0 else None if options.builddir is not None: datadir = os.path.join(options.builddir, datadir) infodir = os.path.join(options.builddir, infodir) if options.builddir.endswith('/meson.build') or options.builddir.endswith('\\meson.build') or options.builddir == 'meson.build': sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11] if options.projectinfo: - list_projinfo_from_source(sourcedir) + list_projinfo_from_source(sourcedir, indent) return 0 if options.buildoptions: - list_buildoptions_from_source(sourcedir, options.backend) + list_buildoptions_from_source(sourcedir, options.backend, indent) return 0 if not os.path.isdir(datadir) or not os.path.isdir(infodir): print('Current directory is not a meson build directory.' @@ -514,8 +516,6 @@ def run(options): with open(curr, 'r') as fp: results += [(i, json.load(fp))] - indent = options.indent if options.indent > 0 else None - if len(results) == 0 and not options.force_dict: print('No command specified') return 1 From aa6e3dc470010ac2586786610f3061aae70547ec Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 1 Jan 2019 19:16:23 +0100 Subject: [PATCH 59/63] Fixed flake8 issues --- mesonbuild/mintro.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 6f2cdf634..c0cefdbe9 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -29,9 +29,7 @@ from . import mlog from . import compilers from . import optinterpreter from .interpreterbase import InvalidArguments -from .backend import ninjabackend, backends -from .dependencies import base -from .compilers import compilers +from .backend import backends import sys, os import pathlib From 428f85e8605034e30f835542f2d26600e967f4f0 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 1 Jan 2019 22:57:16 +0100 Subject: [PATCH 60/63] Updated the docs [skip ci] --- docs/markdown/IDE-integration.md | 73 +++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index d4554aea4..25d262a5d 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -4,15 +4,28 @@ short-description: Meson's API to integrate Meson support into an IDE # IDE integration -Meson has exporters for Visual Studio and XCode, but writing a custom backend for every IDE out there is not a scalable approach. To solve this problem, Meson provides an API that makes it easy for any IDE or build tools to integrate Meson builds and provide an experience comparable to a solution native to the IDE. +Meson has exporters for Visual Studio and XCode, but writing a custom backend +for every IDE out there is not a scalable approach. To solve this problem, +Meson provides an API that makes it easy for any IDE or build tools to +integrate Meson builds and provide an experience comparable to a solution +native to the IDE. -All the resources required for such a IDE integration can be found in the `meson-info` directory in the build directory. +All the resources required for such a IDE integration can be found in +the `meson-info` directory in the build directory. -The first thing to do when setting up a Meson project in an IDE is to select the source and build directories. For this example we assume that the source resides in an Eclipse-like directory called `workspace/project` and the build tree is nested inside it as `workspace/project/build`. First, we initialize Meson by running the following command in the source directory. +The first thing to do when setting up a Meson project in an IDE is to select +the source and build directories. For this example we assume that the source +resides in an Eclipse-like directory called `workspace/project` and the build +tree is nested inside it as `workspace/project/build`. First, we initialize +Meson by running the following command in the source directory. meson builddir -With this command meson will configure the project and also generate introspection information that is stored in `intro-*.json` files in the `meson-info` directory. The introspection dump will be automatically updated when meson is (re)configured, or the build options change. Thus, an IDE can watch for changes in this directory to know when something changed. +With this command meson will configure the project and also generate +introspection information that is stored in `intro-*.json` files in the +`meson-info` directory. The introspection dump will be automatically updated +when meson is (re)configured, or the build options change. Thus, an IDE can +watch for changes in this directory to know when something changed. The `meson-info` directory should contain the following files: @@ -31,7 +44,9 @@ The content of the JSON files is further specified in the remainder of this docu ## The `targets` section -The most important file for an IDE is probably `intro-targets.json`. Here each target with its sources and compiler parameters is specified. The JSON format for one target is defined as follows: +The most important file for an IDE is probably `intro-targets.json`. Here each +target with its sources and compiler parameters is specified. The JSON format +for one target is defined as follows: ```json { @@ -45,13 +60,19 @@ The most important file for an IDE is probably `intro-targets.json`. Here each t } ``` -If the key `installed` is set to `true`, the key `install_filename` will also be present. It stores the installation location for each file in `filename`. If one file in `filename` is not installed, its corresponding install location is set to `null`. +If the key `installed` is set to `true`, the key `install_filename` will also +be present. It stores the installation location for each file in `filename`. +If one file in `filename` is not installed, its corresponding install location +is set to `null`. -A target usually generates only one file. However, it is possible for custom targets to have multiple outputs. +A target usually generates only one file. However, it is possible for custom +targets to have multiple outputs. ### Target sources -The `intro-targets.json` file also stores a list of all source objects of the target in the `target_sources`. With this information, an IDE can provide code completion for all source files. +The `intro-targets.json` file also stores a list of all source objects of the +target in the `target_sources`. With this information, an IDE can provide code +completion for all source files. ```json { @@ -63,6 +84,12 @@ The `intro-targets.json` file also stores a list of all source objects of the ta } ``` +It should be noted that the compiler parameters stored in the `parameters` +differ from the actual parameters used to compile the file. This is because +the parameters are optimized for the usage in an IDE to provide autocompletion +support, etc. It is thus not recommended to use this introspection information +for actual compilation. + ### Possible values for `type` The following table shows all valid types for a target. @@ -79,7 +106,8 @@ The following table shows all valid types for a target. ## Build Options -The list of all build options (build type, warning level, etc.) is stored in the `intro-buildoptions.json` file. Here is the JSON format for each option. +The list of all build options (build type, warning level, etc.) is stored in +the `intro-buildoptions.json` file. Here is the JSON format for each option. ```json { @@ -99,7 +127,8 @@ The supported types are: - integer - array -For the type `combo` the key `choices` is also present. Here all valid values for the option are stored. +For the type `combo` the key `choices` is also present. Here all valid values +for the option are stored. The possible values for `section` are: @@ -117,19 +146,24 @@ Since Meson 0.50.0 it is also possible to get the default buildoptions without a build directory by providing the root `meson.build` instead of a build directory to `meson introspect --buildoptions`. -Running `--buildoptions` without a build directory produces the same output as running -it with a freshly configured build directory. +Running `--buildoptions` without a build directory produces the same output as +running it with a freshly configured build directory. -However, this behavior is not guaranteed if subprojects are present. Due to internal -limitations all subprojects are processed even if they are never used in a real meson run. -Because of this options for the subprojects can differ. +However, this behavior is not guaranteed if subprojects are present. Due to +internal limitations all subprojects are processed even if they are never used +in a real meson run. Because of this options for the subprojects can differ. ## Tests -Compilation and unit tests are done as usual by running the `ninja` and `ninja test` commands. A JSON formatted result log can be found in `workspace/project/builddir/meson-logs/testlog.json`. +Compilation and unit tests are done as usual by running the `ninja` and +`ninja test` commands. A JSON formatted result log can be found in +`workspace/project/builddir/meson-logs/testlog.json`. -When these tests fail, the user probably wants to run the failing test in a debugger. To make this as integrated as possible, extract the tests from the `intro-tests.json` and `intro-benchmarks.json` files. -This provides you with all the information needed to run the test: what command to execute, command line arguments and environment variable settings. +When these tests fail, the user probably wants to run the failing test in a +debugger. To make this as integrated as possible, extract the tests from the +`intro-tests.json` and `intro-benchmarks.json` files. This provides you with +all the information needed to run the test: what command to execute, command +line arguments and environment variable settings. ```json { @@ -148,7 +182,8 @@ This provides you with all the information needed to run the test: what command # Programmatic interface -Meson also provides the `meson introspect` for project introspection via the command line. Use `meson introspect -h` to see all available options. +Meson also provides the `meson introspect` for project introspection via the +command line. Use `meson introspect -h` to see all available options. This API can also work without a build directory for the `--projectinfo` command. From bcb8146280ed89d1fabc3136e2e65a6173b31214 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Wed, 2 Jan 2019 21:58:04 +0100 Subject: [PATCH 61/63] Indent flag only toggles --- docs/markdown/snippets/introspect_multiple.md | 3 ++- mesonbuild/mintro.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 0d53d4806..131bf58ee 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -13,7 +13,8 @@ JSON output (the default is still compact JSON) and force use the new output format, even if only one introspection command was given. A complete introspection dump is also stored in the `meson-info` -directory. This dump will be (re)generated each time meson updates the configuration of the build directory. +directory. This dump will be (re)generated each time meson updates the +configuration of the build directory. Additionlly the format of `meson introspect target` was changed: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index c0cefdbe9..3382e0da0 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -56,8 +56,8 @@ def add_arguments(parser): help='The backend to use for the --buildoptions introspection.') parser.add_argument('-a', '--all', action='store_true', dest='all', default=False, help='Print all available information.') - parser.add_argument('-i', '--indent', dest='indent', type=int, default=0, - help='Number of spaces used for indentation.') + parser.add_argument('-i', '--indent', action='store_true', dest='indent', default=False, + help='Enable pretty printed JSON.') parser.add_argument('-f', '--force-object-output', action='store_true', dest='force_dict', default=False, help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') @@ -457,7 +457,7 @@ def list_projinfo_from_source(sourcedir, indent): def run(options): datadir = 'meson-private' infodir = 'meson-info' - indent = options.indent if options.indent > 0 else None + 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) From 0b0ec5895c6db491cb1fefaadbdc091c1e77189a Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 5 Jan 2019 18:04:01 +0100 Subject: [PATCH 62/63] Fixed typo [skip ci] --- docs/markdown/snippets/introspect_multiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 131bf58ee..06df733ef 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -18,5 +18,5 @@ configuration of the build directory. Additionlly the format of `meson introspect target` was changed: - - New: the `sources` key. It stores the source files of a target and there compiler parameters + - New: the `sources` key. It stores the source files of a target and their compiler parameters - Added new target types (`jar`, `shared module`) From 52071c6d4ee15c750f8a8620e038b78627db890e Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 5 Jan 2019 18:04:44 +0100 Subject: [PATCH 63/63] Fixed missing dots [skip ci] --- docs/markdown/snippets/introspect_multiple.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/markdown/snippets/introspect_multiple.md b/docs/markdown/snippets/introspect_multiple.md index 06df733ef..67f517a2a 100644 --- a/docs/markdown/snippets/introspect_multiple.md +++ b/docs/markdown/snippets/introspect_multiple.md @@ -18,5 +18,5 @@ configuration of the build directory. Additionlly the format of `meson introspect target` was changed: - - New: the `sources` key. It stores the source files of a target and their compiler parameters - - Added new target types (`jar`, `shared module`) + - New: the `sources` key. It stores the source files of a target and their compiler parameters. + - Added new target types (`jar`, `shared module`).