aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Rewriter.md236
-rw-r--r--docs/markdown/snippets/rewriter.md18
-rw-r--r--docs/sitemap.txt1
-rw-r--r--mesonbuild/ast/interpreter.py5
-rw-r--r--mesonbuild/ast/introspection.py7
-rw-r--r--mesonbuild/mesonmain.py19
-rw-r--r--mesonbuild/mlog.py21
-rw-r--r--mesonbuild/rewriter.py328
-rwxr-xr-xrun_unittests.py80
-rw-r--r--test cases/rewrite/1 basic/addTgt.json2
-rw-r--r--test cases/rewrite/1 basic/rmTgt.json6
-rw-r--r--test cases/rewrite/2 subdirs/addTgt.json2
-rw-r--r--test cases/rewrite/2 subdirs/rmTgt.json2
-rw-r--r--test cases/rewrite/3 kwargs/add.json6
-rw-r--r--test cases/rewrite/3 kwargs/defopts_delete.json2
-rw-r--r--test cases/rewrite/3 kwargs/info.json2
-rw-r--r--test cases/rewrite/3 kwargs/remove.json6
-rw-r--r--test cases/rewrite/3 kwargs/remove_regex.json4
-rw-r--r--test cases/rewrite/3 kwargs/set.json2
-rw-r--r--test cases/rewrite/4 same name targets/addSrc.json8
-rw-r--r--test cases/rewrite/4 same name targets/info.json12
-rw-r--r--test cases/rewrite/4 same name targets/meson.build6
-rw-r--r--test cases/rewrite/4 same name targets/sub1/meson.build3
23 files changed, 606 insertions, 172 deletions
diff --git a/docs/markdown/Rewriter.md b/docs/markdown/Rewriter.md
new file mode 100644
index 0000000..b6301d6
--- /dev/null
+++ b/docs/markdown/Rewriter.md
@@ -0,0 +1,236 @@
+---
+short-description: Automatic modification of the build system files
+...
+
+# Meson file rewriter
+
+Since version 0.50.0, meson has the functionality to perform some basic
+modification on the `meson.build` files from the command line. The currently
+supported operations are:
+
+- For build targets:
+ - Add/Remove source files
+ - Add/Remove targets
+ - Modify a select set of kwargs
+ - Print some JSON information
+- For dependencies:
+ - Modify a select set of kwargs
+- For the project function:
+ - Modify a select set of kwargs
+ - Modify the default options list
+
+The rewriter has both, a normal command line interface and a "script mode". The
+normal CLI is mostly designed for everyday use. The "script mode", on the
+other hand, is meant to be used by external programs (IDEs, graphical
+frontends, etc.)
+
+The rewriter itself is considered stable, however the user interface and the
+"script mode" API might change in the future. These changes may also break
+backwards comaptibility to older releases.
+
+We are also open to suggestions for API improvements.
+
+## Using the rewriter
+
+All rewriter functions are accessed via `meson rewrite`. The meson rewriter
+assumes that it is run inside the project root directory. If this isn't the
+case, use `--sourcedir` to specify the actual project source directory.
+
+### Adding and removing sources
+
+The most common operations will probably be the adding and removing of source
+files to a build target. This can be easily done with:
+
+```bash
+meson rewrite target <target name/id> {add/rm} [list of sources]
+```
+
+For instance, given the following example
+
+```meson
+src = ['main.cpp', 'fileA.cpp']
+
+exe1 = executable('testExe', src)
+```
+
+the source `fileB.cpp` can be added with:
+
+```bash
+meson rewrite target testExe add fileB.cpp
+```
+
+After executing this command, the new `meson.build` will look like this:
+
+```meson
+src = ['main.cpp', 'fileA.cpp', 'fileB.cpp']
+
+exe1 = executable('testExe', src)
+```
+
+In this case, `exe1` could also have been used for the target name. This is
+possible because the rewriter also searches for assignments and unique meson
+IDs, which can be acquired with introspection. If there are multiple targets
+with the same name, meson will do nothing and print an error message.
+
+For more information see the help output of the rewriter target command.
+
+### Setting the project version
+
+It is also possible to set kwargs of specific functions with the rewriter. The
+general command for setting or removing kwargs is:
+
+```bash
+meson rewriter kwargs {set/delete} <function type> <function ID> <key1> <value1> <key2> <value2> ...
+```
+
+For instance, setting the project version can be achieved with this command:
+
+```bash
+meson rewriter kwargs set project / version 1.0.0
+```
+
+Currently, only the following function types are supported:
+
+- dependency
+- target (any build target, the function ID is the target name/ID)
+- project (the function ID must be `/` since project() can only be called once)
+
+For more information see the help output of the rewriter kwargs command.
+
+### Setting the project default options
+
+For setting and deleting default options, use the following command:
+
+```bash
+meson rewrite default-options {set/delete} <opt1> <value1> <opt2> <value2> ...
+```
+
+## Limitations
+
+Rewriting a meson file is not guranteed to keep the indentation of the modified
+functions. Additionally, comments inside a modified statement will be removed.
+Furthermore, all source files will be sorted alphabetically.
+
+For instance adding `e.c` to srcs in the following code
+
+```meson
+# Important comment
+
+srcs = [
+'a.c', 'c.c', 'f.c',
+# something important about b
+ 'b.c', 'd.c', 'g.c'
+]
+
+# COMMENT
+```
+
+would result in the following code:
+
+```meson
+# Important comment
+
+srcs = [
+ 'a.c',
+ 'b.c',
+ 'c.c',
+ 'd.c',
+ 'e.c',
+ 'f.c',
+ 'g.c'
+]
+
+# COMMENT
+```
+
+## Using the "script mode"
+
+The "script mode" should be the preferred API for third party programs, since
+it offers more flexibility and higher API stability. The "scripts" are stored
+in JSON format and executed with `meson rewrite command <JSON file or string>`.
+
+The JSON format is defined as follows:
+
+```json
+[
+ {
+ "type": "function to execute",
+ ...
+ }, {
+ "type": "other function",
+ ...
+ },
+ ...
+]
+```
+
+Each object in the main array must have a `type` entry which specifies which
+function should be executed.
+
+Currently, the following functions are supported:
+
+- target
+- kwargs
+- default_options
+
+### Target modification format
+
+The format for the type `target` is defined as follows:
+
+```json
+{
+ "type": "target",
+ "target": "target ID/name/assignment variable",
+ "operation": "one of ['src_add', 'src_rm', 'target_rm', 'target_add', 'info']",
+ "sources": ["list", "of", "source", "files", "to", "add, remove"],
+ "subdir": "subdir where the new target should be added (only has an effect for operation 'tgt_add')",
+ "target_type": "function name of the new target -- same as in the CLI (only has an effect for operation 'tgt_add')"
+}
+```
+
+The keys `sources`, `subdir` and `target_type` are optional.
+
+### kwargs modification format
+
+The format for the type `target` is defined as follows:
+
+```json
+{
+ "type": "kwargs",
+ "function": "one of ['dependency', 'target', 'project']",
+ "id": "function ID",
+ "operation": "one of ['set', 'delete', 'add', 'remove', 'remove_regex', 'info']",
+ "kwargs": {
+ "key1": "value1",
+ "key2": "value2",
+ ...
+ }
+}
+```
+
+### Default options modification format
+
+The format for the type `default_options` is defined as follows:
+
+```json
+{
+ "type": "default_options",
+ "operation": "one of ['set', 'delete']",
+ "options": {
+ "opt1": "value1",
+ "opt2": "value2",
+ ...
+ }
+}
+```
+
+For operation `delete`, the values of the `options` can be anything (including `null`)
+
+## Extracting information
+
+The rewriter also offers operation `info` for the types `target` and `kwargs`.
+When this operation is used, meson will print a JSON dump to stderr, containing
+all available information to the rewriter about the build target / function
+kwargs in question.
+
+The output format is currently experimental and may change in the future.
diff --git a/docs/markdown/snippets/rewriter.md b/docs/markdown/snippets/rewriter.md
new file mode 100644
index 0000000..7a4621d
--- /dev/null
+++ b/docs/markdown/snippets/rewriter.md
@@ -0,0 +1,18 @@
+## Meson file rewriter
+
+This release adds the functionality to perform some basic modification
+on the `meson.build` files from the command line. The currently
+supported operations are:
+
+- For build targets:
+ - Add/Remove source files
+ - Add/Remove targets
+ - Modify a select set of kwargs
+ - Print some JSON information
+- For dependencies:
+ - Modify a select set of kwargs
+- For the project function:
+ - Modify a select set of kwargs
+ - Modify the default options list
+
+For more information see the rewriter documentation.
diff --git a/docs/sitemap.txt b/docs/sitemap.txt
index 6987641..bea2a31 100644
--- a/docs/sitemap.txt
+++ b/docs/sitemap.txt
@@ -60,6 +60,7 @@ index.md
Reference-manual.md
Reference-tables.md
Style-guide.md
+ Rewriter.md
FAQ.md
Reproducible-builds.md
howtox.md
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index b2cd3f5..01277f0 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -51,6 +51,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
self.visitors = visitors
self.visited_subdirs = {}
self.assignments = {}
+ self.assign_vals = {}
self.reverse_assignment = {}
self.funcs.update({'project': self.func_do_nothing,
'test': self.func_do_nothing,
@@ -161,7 +162,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
self.assignments[node.var_name] += [node.value] # Save a reference to the value node
if hasattr(node.value, 'ast_id'):
self.reverse_assignment[node.value.ast_id] = node
- self.evaluate_statement(node.value) # Evaluate the value just in case
+ self.assign_vals[node.var_name] += [self.evaluate_statement(node.value)]
def evaluate_indexing(self, node):
return 0
@@ -200,7 +201,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
self.assignments[node.var_name] = [node.value] # Save a reference to the value node
if hasattr(node.value, 'ast_id'):
self.reverse_assignment[node.value.ast_id] = node
- self.evaluate_statement(node.value) # Evaluate the value just in case
+ self.assign_vals[node.var_name] = [self.evaluate_statement(node.value)] # Evaluate the value just in case
def flatten_args(self, args, include_unknown_args: bool = False):
# Resolve mparser.ArrayNode if needed
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index 12cb379..5745d29 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -194,7 +194,7 @@ class IntrospectionInterpreter(AstInterpreter):
empty_sources = [] # Passing the unresolved sources list causes errors
target = targetclass(name, self.subdir, self.subproject, is_cross, empty_sources, objects, self.environment, kwargs_reduced)
- self.targets += [{
+ new_target = {
'name': target.get_basename(),
'id': target.get_id(),
'type': target.get_typename(),
@@ -206,9 +206,10 @@ class IntrospectionInterpreter(AstInterpreter):
'sources': source_nodes,
'kwargs': kwargs,
'node': node,
- }]
+ }
- return
+ self.targets += [new_target]
+ return new_target
def build_library(self, node, args, kwargs):
default_library = self.coredata.get_builtin_option('default_library')
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 516c411..822a943 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -18,6 +18,7 @@ import importlib
import traceback
import argparse
import codecs
+import shutil
from . import mesonlib
from . import mlog
@@ -29,9 +30,12 @@ from .wrap import wraptool
class CommandLineParser:
def __init__(self):
+ self.term_width = shutil.get_terminal_size().columns
+ self.formater = lambda prog: argparse.HelpFormatter(prog, max_help_position=int(self.term_width / 2), width=self.term_width)
+
self.commands = {}
self.hidden_commands = []
- self.parser = argparse.ArgumentParser(prog='meson')
+ self.parser = argparse.ArgumentParser(prog='meson', formatter_class=self.formater)
self.subparsers = self.parser.add_subparsers(title='Commands',
description='If no command is specified it defaults to setup command.')
self.add_command('setup', msetup.add_arguments, msetup.run,
@@ -52,26 +56,27 @@ class CommandLineParser:
help='Manage subprojects')
self.add_command('help', self.add_help_arguments, self.run_help_command,
help='Print help of a subcommand')
+ self.add_command('rewrite', lambda parser: rewriter.add_arguments(parser, self.formater), rewriter.run,
+ help='Modify the project definition')
# Hidden commands
- self.add_command('rewrite', rewriter.add_arguments, rewriter.run,
- help=argparse.SUPPRESS)
self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command,
help=argparse.SUPPRESS)
self.add_command('unstable-coredata', munstable_coredata.add_arguments, munstable_coredata.run,
help=argparse.SUPPRESS)
- def add_command(self, name, add_arguments_func, run_func, help):
+ def add_command(self, name, add_arguments_func, run_func, help, aliases=[]):
# FIXME: Cannot have hidden subparser:
# https://bugs.python.org/issue22848
if help == argparse.SUPPRESS:
- p = argparse.ArgumentParser(prog='meson ' + name)
+ p = argparse.ArgumentParser(prog='meson ' + name, formatter_class=self.formater)
self.hidden_commands.append(name)
else:
- p = self.subparsers.add_parser(name, help=help)
+ p = self.subparsers.add_parser(name, help=help, aliases=aliases, formatter_class=self.formater)
add_arguments_func(p)
p.set_defaults(run_func=run_func)
- self.commands[name] = p
+ for i in [name] + aliases:
+ self.commands[i] = p
def add_runpython_arguments(self, parser):
parser.add_argument('script_file')
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index a8b146f..0434274 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -48,6 +48,7 @@ log_depth = 0
log_timestamp_start = None
log_fatal_warnings = False
log_disable_stdout = False
+log_errors_only = False
def disable():
global log_disable_stdout
@@ -57,6 +58,14 @@ def enable():
global log_disable_stdout
log_disable_stdout = False
+def set_quiet():
+ global log_errors_only
+ log_errors_only = True
+
+def set_verbose():
+ global log_errors_only
+ log_errors_only = False
+
def initialize(logdir, fatal_warnings=False):
global log_dir, log_file, log_fatal_warnings
log_dir = logdir
@@ -152,14 +161,16 @@ def debug(*args, **kwargs):
print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
log_file.flush()
-def log(*args, **kwargs):
+def log(*args, is_error=False, **kwargs):
+ global log_errors_only
arr = process_markup(args, False)
if log_file is not None:
print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
log_file.flush()
if colorize_console:
arr = process_markup(args, True)
- force_print(*arr, **kwargs)
+ if not log_errors_only or is_error:
+ force_print(*arr, **kwargs)
def _log_error(severity, *args, **kwargs):
from .mesonlib import get_error_location_string
@@ -187,13 +198,13 @@ def _log_error(severity, *args, **kwargs):
raise MesonException("Fatal warnings enabled, aborting")
def error(*args, **kwargs):
- return _log_error('error', *args, **kwargs)
+ return _log_error('error', *args, **kwargs, is_error=True)
def warning(*args, **kwargs):
- return _log_error('warning', *args, **kwargs)
+ return _log_error('warning', *args, **kwargs, is_error=True)
def deprecation(*args, **kwargs):
- return _log_error('deprecation', *args, **kwargs)
+ return _log_error('deprecation', *args, **kwargs, is_error=True)
def exception(e, prefix=red('ERROR:')):
log()
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index 2619aae..975655c 100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
@@ -25,21 +25,49 @@
from .ast import IntrospectionInterpreter, build_target_functions, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstPrinter
from mesonbuild.mesonlib import MesonException
-from . import mlog, mparser, environment
+from . import mlog, environment
from functools import wraps
-from pprint import pprint
-from .mparser import Token, ArrayNode, ArgumentNode, AssignmentNode, IdNode, FunctionNode, StringNode
-import json, os, re
+from typing import List, Dict, Optional
+from .mparser import Token, ArrayNode, ArgumentNode, AssignmentNode, BaseNode, BooleanNode, ElementaryNode, IdNode, FunctionNode, StringNode
+import json, os, re, sys
class RewriterException(MesonException):
pass
-def add_arguments(parser):
- parser.add_argument('--sourcedir', default='.',
- help='Path to source directory.')
- parser.add_argument('-p', '--print', action='store_true', default=False, dest='print',
- help='Print the parsed AST.')
- parser.add_argument('command', type=str)
+def add_arguments(parser, formater=None):
+ parser.add_argument('-s', '--sourcedir', type=str, default='.', metavar='SRCDIR', help='Path to source directory.')
+ parser.add_argument('-V', '--verbose', action='store_true', default=False, help='Enable verbose output')
+ parser.add_argument('-S', '--skip-errors', dest='skip', action='store_true', default=False, help='Skip errors instead of aborting')
+ subparsers = parser.add_subparsers(dest='type', title='Rewriter commands', description='Rewrite command to execute')
+
+ # Target
+ tgt_parser = subparsers.add_parser('target', help='Modify a target', formatter_class=formater)
+ tgt_parser.add_argument('-s', '--subdir', default='', dest='subdir', help='Subdirectory of the new target (only for the "add_target" action)')
+ tgt_parser.add_argument('--type', dest='tgt_type', choices=rewriter_keys['target']['target_type'][2], default='executable',
+ help='Type of the target to add (only for the "add_target" action)')
+ tgt_parser.add_argument('target', help='Name or ID of the target')
+ tgt_parser.add_argument('operation', choices=['add', 'rm', 'add_target', 'rm_target', 'info'],
+ help='Action to execute')
+ tgt_parser.add_argument('sources', nargs='*', help='Sources to add/remove')
+
+ # KWARGS
+ kw_parser = subparsers.add_parser('kwargs', help='Modify keyword arguments', formatter_class=formater)
+ kw_parser.add_argument('operation', choices=rewriter_keys['kwargs']['operation'][2],
+ help='Action to execute')
+ kw_parser.add_argument('function', choices=list(rewriter_func_kwargs.keys()),
+ help='Function type to modify')
+ kw_parser.add_argument('id', help='ID of the function to modify (can be anything for "project")')
+ kw_parser.add_argument('kwargs', nargs='*', help='Pairs of keyword and value')
+
+ # Default options
+ def_parser = subparsers.add_parser('default-options', help='Modify the project default options', formatter_class=formater)
+ def_parser.add_argument('operation', choices=rewriter_keys['default_options']['operation'][2],
+ help='Action to execute')
+ def_parser.add_argument('options', nargs='*', help='Key, value pairs of configuration option')
+
+ # JSON file/command
+ cmd_parser = subparsers.add_parser('command', help='Execute a JSON array of commands', formatter_class=formater)
+ cmd_parser.add_argument('json', help='JSON string or file to execute')
class RequiredKeys:
def __init__(self, keys):
@@ -73,7 +101,7 @@ class RequiredKeys:
return wrapped
class MTypeBase:
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
if node is None:
self.node = self._new_node()
else:
@@ -85,7 +113,7 @@ class MTypeBase:
def _new_node(self):
# Overwrite in derived class
- return mparser.BaseNode()
+ return BaseNode()
def can_modify(self):
return self.node_type is not None
@@ -114,57 +142,57 @@ class MTypeBase:
mlog.warning('Cannot remove a regex in type', mlog.bold(type(self).__name__), '--> skipping')
class MTypeStr(MTypeBase):
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
- return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, ''))
+ return StringNode(Token('', '', 0, 0, 0, None, ''))
def supported_nodes(self):
- return [mparser.StringNode]
+ return [StringNode]
def set_value(self, value):
self.node.value = str(value)
class MTypeBool(MTypeBase):
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
- return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, False))
+ return StringNode(Token('', '', 0, 0, 0, None, False))
def supported_nodes(self):
- return [mparser.BooleanNode]
+ return [BooleanNode]
def set_value(self, value):
self.node.value = bool(value)
class MTypeID(MTypeBase):
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
- return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, ''))
+ return StringNode(Token('', '', 0, 0, 0, None, ''))
def supported_nodes(self):
- return [mparser.IdNode]
+ return [IdNode]
def set_value(self, value):
self.node.value = str(value)
class MTypeList(MTypeBase):
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
- return mparser.ArrayNode(mparser.ArgumentNode(mparser.Token('', '', 0, 0, 0, None, '')), 0, 0, 0, 0)
+ return ArrayNode(ArgumentNode(Token('', '', 0, 0, 0, None, '')), 0, 0, 0, 0)
def _new_element_node(self, value):
# Overwrite in derived class
- return mparser.BaseNode()
+ return BaseNode()
def _ensure_array_node(self):
- if not isinstance(self.node, mparser.ArrayNode):
+ if not isinstance(self.node, ArrayNode):
tmp = self.node
self.node = self._new_node()
self.node.args.arguments += [tmp]
@@ -178,7 +206,7 @@ class MTypeList(MTypeBase):
return False
def get_node(self):
- if isinstance(self.node, mparser.ArrayNode):
+ if isinstance(self.node, ArrayNode):
if len(self.node.args.arguments) == 1:
return self.node.args.arguments[0]
return self.node
@@ -188,7 +216,7 @@ class MTypeList(MTypeBase):
return []
def supported_nodes(self):
- return [mparser.ArrayNode] + self.supported_element_nodes()
+ return [ArrayNode] + self.supported_element_nodes()
def set_value(self, value):
if not isinstance(value, list):
@@ -228,44 +256,44 @@ class MTypeList(MTypeBase):
self._remove_helper(regex, self._check_regex_matches)
class MTypeStrList(MTypeList):
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
super().__init__(node)
def _new_element_node(self, value):
- return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, str(value)))
+ return StringNode(Token('', '', 0, 0, 0, None, str(value)))
def _check_is_equal(self, node, value) -> bool:
- if isinstance(node, mparser.StringNode):
+ if isinstance(node, StringNode):
return node.value == value
return False
def _check_regex_matches(self, node, regex: str) -> bool:
- if isinstance(node, mparser.StringNode):
+ if isinstance(node, StringNode):
return re.match(regex, node.value) is not None
return False
def supported_element_nodes(self):
- return [mparser.StringNode]
+ return [StringNode]
class MTypeIDList(MTypeList):
- def __init__(self, node: mparser.BaseNode):
+ def __init__(self, node: Optional[BaseNode] = None):
super().__init__(node)
def _new_element_node(self, value):
- return mparser.IdNode(mparser.Token('', '', 0, 0, 0, None, str(value)))
+ return IdNode(Token('', '', 0, 0, 0, None, str(value)))
def _check_is_equal(self, node, value) -> bool:
- if isinstance(node, mparser.IdNode):
+ if isinstance(node, IdNode):
return node.value == value
return False
def _check_regex_matches(self, node, regex: str) -> bool:
- if isinstance(node, mparser.StringNode):
+ if isinstance(node, StringNode):
return re.match(regex, node.value) is not None
return False
def supported_element_nodes(self):
- return [mparser.IdNode]
+ return [IdNode]
rewriter_keys = {
'default_options': {
@@ -280,11 +308,10 @@ rewriter_keys = {
},
'target': {
'target': (str, None, None),
- 'operation': (str, None, ['src_add', 'src_rm', 'tgt_rm', 'tgt_add', 'info']),
+ 'operation': (str, None, ['src_add', 'src_rm', 'target_rm', 'target_add', 'info']),
'sources': (list, [], None),
'subdir': (str, '', None),
'target_type': (str, 'executable', ['both_libraries', 'executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library']),
- 'debug': (bool, False, None)
}
}
@@ -322,9 +349,10 @@ rewriter_func_kwargs = {
}
class Rewriter:
- def __init__(self, sourcedir: str, generator: str = 'ninja'):
+ def __init__(self, sourcedir: str, generator: str = 'ninja', skip_errors: bool = False):
self.sourcedir = sourcedir
self.interpreter = IntrospectionInterpreter(sourcedir, '', generator, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()])
+ self.skip_errors = skip_errors
self.modefied_nodes = []
self.to_remove_nodes = []
self.to_add_nodes = []
@@ -351,29 +379,45 @@ class Rewriter:
def print_info(self):
if self.info_dump is None:
return
- # Wrap the dump in magic strings
- print('!!==JSON DUMP: BEGIN==!!')
- print(json.dumps(self.info_dump, indent=2))
- print('!!==JSON DUMP: END==!!')
+ sys.stderr.write(json.dumps(self.info_dump, indent=2))
+
+ def on_error(self):
+ if self.skip_errors:
+ return mlog.cyan('-->'), mlog.yellow('skipping')
+ return mlog.cyan('-->'), mlog.red('aborting')
+
+ def handle_error(self):
+ if self.skip_errors:
+ return None
+ raise MesonException('Rewriting the meson.build failed')
def find_target(self, target: str):
- def check_list(name: str):
+ def check_list(name: str) -> List[BaseNode]:
+ result = []
for i in self.interpreter.targets:
if name == i['name'] or name == i['id']:
- return i
- return None
+ result += [i]
+ return result
- tgt = check_list(target)
- if tgt is not None:
- return tgt
+ targets = check_list(target)
+ if targets:
+ if len(targets) == 1:
+ return targets[0]
+ else:
+ mlog.error('There are multiple targets matching', mlog.bold(target))
+ for i in targets:
+ mlog.error(' -- Target name', mlog.bold(i['name']), 'with ID', mlog.bold(i['id']))
+ mlog.error('Please try again with the unique ID of the target', *self.on_error())
+ self.handle_error()
+ return None
# Check the assignments
+ tgt = None
if target in self.interpreter.assignments:
node = self.interpreter.assignments[target][0]
- if isinstance(node, mparser.FunctionNode):
+ if isinstance(node, FunctionNode):
if node.func_name in ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries']:
- name = self.interpreter.flatten_args(node.args)[0]
- tgt = check_list(name)
+ tgt = self.interpreter.assign_vals[target][0]
return tgt
@@ -391,7 +435,7 @@ class Rewriter:
# Check the assignments
if dependency in self.interpreter.assignments:
node = self.interpreter.assignments[dependency][0]
- if isinstance(node, mparser.FunctionNode):
+ if isinstance(node, FunctionNode):
if node.func_name in ['dependency']:
name = self.interpreter.flatten_args(node.args)[0]
dep = check_list(name)
@@ -429,13 +473,15 @@ class Rewriter:
for key, val in sorted(cmd['options'].items()):
if key not in options:
- mlog.error('Unknown options', mlog.bold(key), '--> skipping')
+ mlog.error('Unknown options', mlog.bold(key), *self.on_error())
+ self.handle_error()
continue
try:
val = options[key].validate_value(val)
except MesonException as e:
- mlog.error('Unable to set', mlog.bold(key), mlog.red(str(e)), '--> skipping')
+ mlog.error('Unable to set', mlog.bold(key), mlog.red(str(e)), *self.on_error())
+ self.handle_error()
continue
kwargs_cmd['kwargs']['default_options'] += ['{}={}'.format(key, val)]
@@ -446,14 +492,17 @@ class Rewriter:
def process_kwargs(self, cmd):
mlog.log('Processing function type', mlog.bold(cmd['function']), 'with id', mlog.cyan("'" + cmd['id'] + "'"))
if cmd['function'] not in rewriter_func_kwargs:
- mlog.error('Unknown function type {} --> skipping'.format(cmd['function']))
- return
+ mlog.error('Unknown function type', cmd['function'], *self.on_error())
+ return self.handle_error()
kwargs_def = rewriter_func_kwargs[cmd['function']]
# Find the function node to modify
node = None
arg_node = None
if cmd['function'] == 'project':
+ if cmd['id'] != '/':
+ mlog.error('The ID for the function type project must be an empty string', *self.on_error())
+ self.handle_error()
node = self.interpreter.project_node
arg_node = node.args
elif cmd['function'] == 'target':
@@ -468,21 +517,21 @@ class Rewriter:
arg_node = node.args
if not node:
mlog.error('Unable to find the function node')
- assert(isinstance(node, mparser.FunctionNode))
- assert(isinstance(arg_node, mparser.ArgumentNode))
+ assert(isinstance(node, FunctionNode))
+ assert(isinstance(arg_node, ArgumentNode))
# Print kwargs info
if cmd['operation'] == 'info':
info_data = {}
for key, val in sorted(arg_node.kwargs.items()):
info_data[key] = None
- if isinstance(val, mparser.ElementaryNode):
+ if isinstance(val, ElementaryNode):
info_data[key] = val.value
- elif isinstance(val, mparser.ArrayNode):
+ elif isinstance(val, ArrayNode):
data_list = []
for i in val.args.arguments:
element = None
- if isinstance(i, mparser.ElementaryNode):
+ if isinstance(i, ElementaryNode):
element = i.value
data_list += [element]
info_data[key] = data_list
@@ -494,7 +543,8 @@ class Rewriter:
num_changed = 0
for key, val in sorted(cmd['kwargs'].items()):
if key not in kwargs_def:
- mlog.error('Cannot modify unknown kwarg --> skipping', mlog.bold(key))
+ mlog.error('Cannot modify unknown kwarg', mlog.bold(key), *self.on_error())
+ self.handle_error()
continue
# Remove the key from the kwargs
@@ -535,7 +585,7 @@ class Rewriter:
if num_changed > 0 and node not in self.modefied_nodes:
self.modefied_nodes += [node]
- def find_assignment_node(self, node: mparser) -> AssignmentNode:
+ def find_assignment_node(self, node: BaseNode) -> AssignmentNode:
if hasattr(node, 'ast_id') and node.ast_id in self.interpreter.reverse_assignment:
return self.interpreter.reverse_assignment[node.ast_id]
return None
@@ -544,13 +594,22 @@ class Rewriter:
def process_target(self, cmd):
mlog.log('Processing target', mlog.bold(cmd['target']), 'operation', mlog.cyan(cmd['operation']))
target = self.find_target(cmd['target'])
- if target is None and cmd['operation'] != 'tgt_add':
- mlog.error('Unknown target "{}" --> skipping'.format(cmd['target']))
- if cmd['debug']:
- pprint(self.interpreter.targets)
- return
- if cmd['debug']:
- pprint(target)
+ if target is None and cmd['operation'] != 'target_add':
+ mlog.error('Unknown target', mlog.bold(cmd['target']), *self.on_error())
+ return self.handle_error()
+
+ # Make source paths relative to the current subdir
+ def rel_source(src: str) -> str:
+ subdir = os.path.abspath(os.path.join(self.sourcedir, target['subdir']))
+ if os.path.isabs(src):
+ return os.path.relpath(src, subdir)
+ elif not os.path.exists(src):
+ return src # Trust the user when the source doesn't exist
+ # Make sure that the path is relative to the subdir
+ return os.path.relpath(os.path.abspath(src), subdir)
+
+ if target is not None:
+ cmd['sources'] = [rel_source(x) for x in cmd['sources']]
# Utility function to get a list of the sources from a node
def arg_list_from_node(n):
@@ -642,34 +701,38 @@ class Rewriter:
if root not in self.modefied_nodes:
self.modefied_nodes += [root]
- elif cmd['operation'] == 'tgt_add':
+ elif cmd['operation'] == 'target_add':
if target is not None:
- mlog.error('Can not add target', mlog.bold(cmd['target']), 'because it already exists')
- return
+ mlog.error('Can not add target', mlog.bold(cmd['target']), 'because it already exists', *self.on_error())
+ return self.handle_error()
+
+ id_base = re.sub(r'[- ]', '_', cmd['target'])
+ target_id = id_base + '_exe' if cmd['target_type'] == 'executable' else '_lib'
+ source_id = id_base + '_sources'
# Build src list
src_arg_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, ''))
src_arr_node = ArrayNode(src_arg_node, 0, 0, 0, 0)
src_far_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, ''))
src_fun_node = FunctionNode(cmd['subdir'], 0, 0, 0, 0, 'files', src_far_node)
- src_ass_node = AssignmentNode(cmd['subdir'], 0, 0, '{}_src'.format(cmd['target']), src_fun_node)
+ src_ass_node = AssignmentNode(cmd['subdir'], 0, 0, source_id, src_fun_node)
src_arg_node.arguments = [StringNode(Token('string', cmd['subdir'], 0, 0, 0, None, x)) for x in cmd['sources']]
src_far_node.arguments = [src_arr_node]
# Build target
tgt_arg_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, ''))
tgt_fun_node = FunctionNode(cmd['subdir'], 0, 0, 0, 0, cmd['target_type'], tgt_arg_node)
- tgt_ass_node = AssignmentNode(cmd['subdir'], 0, 0, '{}_tgt'.format(cmd['target']), tgt_fun_node)
+ tgt_ass_node = AssignmentNode(cmd['subdir'], 0, 0, target_id, tgt_fun_node)
tgt_arg_node.arguments = [
StringNode(Token('string', cmd['subdir'], 0, 0, 0, None, cmd['target'])),
- IdNode(Token('string', cmd['subdir'], 0, 0, 0, None, '{}_src'.format(cmd['target'])))
+ IdNode(Token('string', cmd['subdir'], 0, 0, 0, None, source_id))
]
src_ass_node.accept(AstIndentationGenerator())
tgt_ass_node.accept(AstIndentationGenerator())
self.to_add_nodes += [src_ass_node, tgt_ass_node]
- elif cmd['operation'] == 'tgt_rm':
+ elif cmd['operation'] == 'target_rm':
to_remove = self.find_assignment_node(target['node'])
if to_remove is None:
to_remove = target['node']
@@ -717,7 +780,7 @@ class Rewriter:
# Sort based on line and column in reversed order
work_nodes = [{'node': x, 'action': 'modify'} for x in self.modefied_nodes]
work_nodes += [{'node': x, 'action': 'rm'} for x in self.to_remove_nodes]
- work_nodes = list(sorted(work_nodes, key=lambda x: x['node'].lineno * 1000 + x['node'].colno, reverse=True))
+ work_nodes = list(sorted(work_nodes, key=lambda x: (x['node'].lineno, x['node'].colno), reverse=True))
work_nodes += [{'node': x, 'action': 'add'} for x in self.to_add_nodes]
# Generating the new replacement string
@@ -802,23 +865,92 @@ class Rewriter:
with open(val['path'], 'w') as fp:
fp.write(val['raw'])
-def run(options):
- rewriter = Rewriter(options.sourcedir)
- rewriter.analyze_meson()
- if os.path.exists(options.command):
- with open(options.command, 'r') as fp:
- commands = json.load(fp)
- else:
- commands = json.loads(options.command)
-
- if not isinstance(commands, list):
- raise TypeError('Command is not a list')
+target_operation_map = {
+ 'add': 'src_add',
+ 'rm': 'src_rm',
+ 'add_target': 'target_add',
+ 'rm_target': 'target_rm',
+ 'info': 'info',
+}
- for i in commands:
- if not isinstance(i, object):
- raise TypeError('Command is not an object')
- rewriter.process(i)
+def list_to_dict(in_list: List[str]) -> Dict[str, str]:
+ if len(in_list) % 2 != 0:
+ raise TypeError('An even ammount of arguments are required')
+ result = {}
+ for i in range(0, len(in_list), 2):
+ result[in_list[i]] = in_list[i + 1]
+ return result
+
+def generate_target(options) -> List[dict]:
+ return [{
+ 'type': 'target',
+ 'target': options.target,
+ 'operation': target_operation_map[options.operation],
+ 'sources': options.sources,
+ 'subdir': options.subdir,
+ 'target_type': options.tgt_type,
+ }]
+
+def generate_kwargs(options) -> List[dict]:
+ return [{
+ 'type': 'kwargs',
+ 'function': options.function,
+ 'id': options.id,
+ 'operation': options.operation,
+ 'kwargs': list_to_dict(options.kwargs),
+ }]
+
+def generate_def_opts(options) -> List[dict]:
+ return [{
+ 'type': 'default_options',
+ 'operation': options.operation,
+ 'options': list_to_dict(options.options),
+ }]
+
+def genreate_cmd(options) -> List[dict]:
+ if os.path.exists(options.json):
+ with open(options.json, 'r') as fp:
+ return json.load(fp)
+ else:
+ return json.loads(options.json)
+
+# Map options.type to the actual type name
+cli_type_map = {
+ 'target': generate_target,
+ 'tgt': generate_target,
+ 'kwargs': generate_kwargs,
+ 'default-options': generate_def_opts,
+ 'def': generate_def_opts,
+ 'command': genreate_cmd,
+ 'cmd': genreate_cmd,
+}
- rewriter.apply_changes()
- rewriter.print_info()
- return 0
+def run(options):
+ if not options.verbose:
+ mlog.set_quiet()
+
+ try:
+ rewriter = Rewriter(options.sourcedir, skip_errors=options.skip)
+ rewriter.analyze_meson()
+
+ if options.type is None:
+ mlog.error('No command specified')
+ return 1
+
+ commands = cli_type_map[options.type](options)
+
+ if not isinstance(commands, list):
+ raise TypeError('Command is not a list')
+
+ for i in commands:
+ if not isinstance(i, object):
+ raise TypeError('Command is not an object')
+ rewriter.process(i)
+
+ rewriter.apply_changes()
+ rewriter.print_info()
+ return 0
+ except Exception as e:
+ raise e
+ finally:
+ mlog.set_verbose()
diff --git a/run_unittests.py b/run_unittests.py
index 90a726b..690ea33 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -5187,8 +5187,6 @@ class PythonTests(BasePlatformTests):
class RewriterTests(BasePlatformTests):
- data_regex = re.compile(r'.*\n!!==JSON DUMP: BEGIN==!!\n(.*)\n!!==JSON DUMP: END==!!\n', re.MULTILINE | re.DOTALL)
-
def setUp(self):
super().setUp()
self.maxDiff = None
@@ -5196,30 +5194,32 @@ class RewriterTests(BasePlatformTests):
def prime(self, dirname):
copy_tree(os.path.join(self.rewrite_test_dir, dirname), self.builddir)
- def rewrite(self, directory, args):
+ def rewrite_raw(self, directory, args):
if isinstance(args, str):
args = [args]
- command = self.rewrite_command + ['--sourcedir', directory] + args
- p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ command = self.rewrite_command + ['--verbose', '--skip', '--sourcedir', directory] + args
+ p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, timeout=60)
+ print('STDOUT:')
print(p.stdout)
+ print('STDERR:')
+ print(p.stderr)
if p.returncode != 0:
if 'MESON_SKIP_TEST' in p.stdout:
raise unittest.SkipTest('Project requested skipping.')
raise subprocess.CalledProcessError(p.returncode, command, output=p.stdout)
- return p.stdout
+ if not p.stderr:
+ return {}
+ return json.loads(p.stderr)
- def extract_test_data(self, out):
- match = RewriterTests.data_regex.match(out)
- result = {}
- if match:
- result = json.loads(match.group(1))
- return result
+ def rewrite(self, directory, args):
+ if isinstance(args, str):
+ args = [args]
+ return self.rewrite_raw(directory, ['command'] + args)
def test_target_source_list(self):
self.prime('1 basic')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'target': {
'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp', 'fileC.cpp']},
@@ -5239,7 +5239,6 @@ class RewriterTests(BasePlatformTests):
def test_target_add_sources(self):
self.prime('1 basic')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
- out = self.extract_test_data(out)
expected = {
'target': {
'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp', 'a7.cpp', 'fileB.cpp', 'fileC.cpp']},
@@ -5258,13 +5257,21 @@ class RewriterTests(BasePlatformTests):
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
+ self.assertDictEqual(out, expected)
+
+ def test_target_add_sources_abs(self):
+ self.prime('1 basic')
+ abs_src = [os.path.join(self.builddir, x) for x in ['a1.cpp', 'a2.cpp', 'a6.cpp']]
+ add = json.dumps([{"type": "target", "target": "trivialprog1", "operation": "src_add", "sources": abs_src}])
+ inf = json.dumps([{"type": "target", "target": "trivialprog1", "operation": "info"}])
+ self.rewrite(self.builddir, add)
+ out = self.rewrite(self.builddir, inf)
+ expected = {'target': {'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp']}}}
self.assertDictEqual(out, expected)
def test_target_remove_sources(self):
self.prime('1 basic')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'rmSrc.json'))
- out = self.extract_test_data(out)
expected = {
'target': {
'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp', 'fileC.cpp']},
@@ -5283,26 +5290,22 @@ class RewriterTests(BasePlatformTests):
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
self.assertDictEqual(out, expected)
def test_target_subdir(self):
self.prime('2 subdirs')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
- out = self.extract_test_data(out)
expected = {'name': 'something', 'sources': ['first.c', 'second.c', 'third.c']}
self.assertDictEqual(list(out['target'].values())[0], expected)
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
self.assertDictEqual(list(out['target'].values())[0], expected)
def test_target_remove(self):
self.prime('1 basic')
self.rewrite(self.builddir, os.path.join(self.builddir, 'rmTgt.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'target': {
@@ -5321,7 +5324,6 @@ class RewriterTests(BasePlatformTests):
self.prime('1 basic')
self.rewrite(self.builddir, os.path.join(self.builddir, 'addTgt.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'target': {
@@ -5344,14 +5346,12 @@ class RewriterTests(BasePlatformTests):
self.prime('2 subdirs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'rmTgt.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
self.assertDictEqual(out, {})
def test_target_add_subdir(self):
self.prime('2 subdirs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'addTgt.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {'name': 'something', 'sources': ['first.c', 'second.c']}
self.assertDictEqual(out['target']['94b671c@@something@exe'], expected)
@@ -5361,7 +5361,6 @@ class RewriterTests(BasePlatformTests):
inf_json = json.dumps([{'type': 'target', 'target': 'exe1', 'operation': 'info'}])
out = self.rewrite(self.builddir, add_json)
out = self.rewrite(self.builddir, inf_json)
- out = self.extract_test_data(out)
expected = {
'target': {
'exe1@exe': {
@@ -5401,13 +5400,21 @@ class RewriterTests(BasePlatformTests):
}
self.assertDictEqual(out, expected)
+ def test_target_same_name_skip(self):
+ self.prime('4 same name targets')
+ out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
+ out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
+ expected = {'name': 'myExe', 'sources': ['main.cpp']}
+ self.assertEqual(len(out['target']), 2)
+ for _, val in out['target'].items():
+ self.assertDictEqual(expected, val)
+
def test_kwargs_info(self):
self.prime('3 kwargs')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.1'},
+ 'project#/': {'version': '0.0.1'},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
@@ -5418,10 +5425,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'set.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.2', 'meson_version': '0.50.0', 'license': ['GPL', 'MIT']},
+ 'project#/': {'version': '0.0.2', 'meson_version': '0.50.0', 'license': ['GPL', 'MIT']},
'target#tgt1': {'build_by_default': False, 'build_rpath': '/usr/local', 'dependencies': 'dep1'},
'dependency#dep1': {'required': True, 'method': 'cmake'}
}
@@ -5432,10 +5438,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'add.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.1', 'license': ['GPL', 'MIT', 'BSD']},
+ 'project#/': {'version': '0.0.1', 'license': ['GPL', 'MIT', 'BSD']},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
@@ -5446,10 +5451,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'remove.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.1', 'license': 'GPL'},
+ 'project#/': {'version': '0.0.1', 'license': 'GPL'},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
@@ -5460,10 +5464,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'remove_regex.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.1', 'default_options': ['buildtype=release', 'debug=true']},
+ 'project#/': {'version': '0.0.1', 'default_options': ['buildtype=release', 'debug=true']},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
@@ -5474,10 +5477,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'delete.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {},
+ 'project#/': {},
'target#tgt1': {},
'dependency#dep1': {'required': False}
}
@@ -5488,10 +5490,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'defopts_set.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.1', 'default_options': ['buildtype=release', 'debug=True', 'cpp_std=c++11']},
+ 'project#/': {'version': '0.0.1', 'default_options': ['buildtype=release', 'debug=True', 'cpp_std=c++11']},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
@@ -5502,10 +5503,9 @@ class RewriterTests(BasePlatformTests):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'defopts_delete.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- out = self.extract_test_data(out)
expected = {
'kwargs': {
- 'project#': {'version': '0.0.1', 'default_options': ['cpp_std=c++14', 'debug=true']},
+ 'project#/': {'version': '0.0.1', 'default_options': ['cpp_std=c++14', 'debug=true']},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
diff --git a/test cases/rewrite/1 basic/addTgt.json b/test cases/rewrite/1 basic/addTgt.json
index 432e299..2f4e7e2 100644
--- a/test cases/rewrite/1 basic/addTgt.json
+++ b/test cases/rewrite/1 basic/addTgt.json
@@ -2,7 +2,7 @@
{
"type": "target",
"target": "trivialprog10",
- "operation": "tgt_add",
+ "operation": "target_add",
"sources": ["new1.cpp", "new2.cpp"],
"target_type": "shared_library"
}
diff --git a/test cases/rewrite/1 basic/rmTgt.json b/test cases/rewrite/1 basic/rmTgt.json
index 5774157..dbaf025 100644
--- a/test cases/rewrite/1 basic/rmTgt.json
+++ b/test cases/rewrite/1 basic/rmTgt.json
@@ -2,16 +2,16 @@
{
"type": "target",
"target": "exe0",
- "operation": "tgt_rm"
+ "operation": "target_rm"
},
{
"type": "target",
"target": "trivialprog1",
- "operation": "tgt_rm"
+ "operation": "target_rm"
},
{
"type": "target",
"target": "trivialprog9",
- "operation": "tgt_rm"
+ "operation": "target_rm"
}
]
diff --git a/test cases/rewrite/2 subdirs/addTgt.json b/test cases/rewrite/2 subdirs/addTgt.json
index 01e9a6e..2e1e8bc 100644
--- a/test cases/rewrite/2 subdirs/addTgt.json
+++ b/test cases/rewrite/2 subdirs/addTgt.json
@@ -2,7 +2,7 @@
{
"type": "target",
"target": "newLib",
- "operation": "tgt_add",
+ "operation": "target_add",
"sources": ["new1.cpp", "new2.cpp"],
"target_type": "shared_library",
"subdir": "sub2"
diff --git a/test cases/rewrite/2 subdirs/rmTgt.json b/test cases/rewrite/2 subdirs/rmTgt.json
index 73a7b1d..9b112f9 100644
--- a/test cases/rewrite/2 subdirs/rmTgt.json
+++ b/test cases/rewrite/2 subdirs/rmTgt.json
@@ -2,6 +2,6 @@
{
"type": "target",
"target": "something",
- "operation": "tgt_rm"
+ "operation": "target_rm"
}
]
diff --git a/test cases/rewrite/3 kwargs/add.json b/test cases/rewrite/3 kwargs/add.json
index e398b7b..2148a1e 100644
--- a/test cases/rewrite/3 kwargs/add.json
+++ b/test cases/rewrite/3 kwargs/add.json
@@ -2,7 +2,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "set",
"kwargs": {
"license": "GPL"
@@ -11,7 +11,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "add",
"kwargs": {
"license": ["MIT"]
@@ -20,7 +20,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "add",
"kwargs": {
"license": "BSD"
diff --git a/test cases/rewrite/3 kwargs/defopts_delete.json b/test cases/rewrite/3 kwargs/defopts_delete.json
index 06232dd..4fe39e2 100644
--- a/test cases/rewrite/3 kwargs/defopts_delete.json
+++ b/test cases/rewrite/3 kwargs/defopts_delete.json
@@ -2,7 +2,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "set",
"kwargs": {
"default_options": ["cpp_std=c++14", "buildtype=release", "debug=true"]
diff --git a/test cases/rewrite/3 kwargs/info.json b/test cases/rewrite/3 kwargs/info.json
index 5fd1a64..0eed404 100644
--- a/test cases/rewrite/3 kwargs/info.json
+++ b/test cases/rewrite/3 kwargs/info.json
@@ -2,7 +2,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "info"
},
{
diff --git a/test cases/rewrite/3 kwargs/remove.json b/test cases/rewrite/3 kwargs/remove.json
index bd7596f..5dc7836 100644
--- a/test cases/rewrite/3 kwargs/remove.json
+++ b/test cases/rewrite/3 kwargs/remove.json
@@ -2,7 +2,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "set",
"kwargs": {
"license": ["GPL", "MIT", "BSD"]
@@ -11,7 +11,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "remove",
"kwargs": {
"license": ["MIT"]
@@ -20,7 +20,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "remove",
"kwargs": {
"license": "BSD"
diff --git a/test cases/rewrite/3 kwargs/remove_regex.json b/test cases/rewrite/3 kwargs/remove_regex.json
index fcb04eb..1043101 100644
--- a/test cases/rewrite/3 kwargs/remove_regex.json
+++ b/test cases/rewrite/3 kwargs/remove_regex.json
@@ -2,7 +2,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "set",
"kwargs": {
"default_options": ["cpp_std=c++14", "buildtype=release", "debug=true"]
@@ -11,7 +11,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "remove_regex",
"kwargs": {
"default_options": ["cpp_std=.*"]
diff --git a/test cases/rewrite/3 kwargs/set.json b/test cases/rewrite/3 kwargs/set.json
index 7d60c4f..a56c599 100644
--- a/test cases/rewrite/3 kwargs/set.json
+++ b/test cases/rewrite/3 kwargs/set.json
@@ -2,7 +2,7 @@
{
"type": "kwargs",
"function": "project",
- "id": "",
+ "id": "/",
"operation": "set",
"kwargs": {
"version": "0.0.2",
diff --git a/test cases/rewrite/4 same name targets/addSrc.json b/test cases/rewrite/4 same name targets/addSrc.json
new file mode 100644
index 0000000..98d0d1e
--- /dev/null
+++ b/test cases/rewrite/4 same name targets/addSrc.json
@@ -0,0 +1,8 @@
+[
+ {
+ "type": "target",
+ "target": "myExe",
+ "operation": "src_add",
+ "sources": ["a1.cpp", "a2.cpp"]
+ }
+]
diff --git a/test cases/rewrite/4 same name targets/info.json b/test cases/rewrite/4 same name targets/info.json
new file mode 100644
index 0000000..a9fc2dd
--- /dev/null
+++ b/test cases/rewrite/4 same name targets/info.json
@@ -0,0 +1,12 @@
+[
+ {
+ "type": "target",
+ "target": "exe1",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "exe2",
+ "operation": "info"
+ }
+]
diff --git a/test cases/rewrite/4 same name targets/meson.build b/test cases/rewrite/4 same name targets/meson.build
new file mode 100644
index 0000000..384fa2b
--- /dev/null
+++ b/test cases/rewrite/4 same name targets/meson.build
@@ -0,0 +1,6 @@
+project('rewrite same name targets', 'cpp')
+
+src1 = ['main.cpp']
+
+exe1 = executable('myExe', src1)
+subdir('sub1')
diff --git a/test cases/rewrite/4 same name targets/sub1/meson.build b/test cases/rewrite/4 same name targets/sub1/meson.build
new file mode 100644
index 0000000..ac53667
--- /dev/null
+++ b/test cases/rewrite/4 same name targets/sub1/meson.build
@@ -0,0 +1,3 @@
+src2 = ['main.cpp']
+
+exe2 = executable('myExe', src2)