Merge pull request #4858 from mensinda/rwKWARGS

rewriter: Add support for kwargs modification
pull/4940/head
Jussi Pakkanen 6 years ago committed by GitHub
commit 939b011114
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      mesonbuild/ast/interpreter.py
  2. 22
      mesonbuild/ast/introspection.py
  3. 367
      mesonbuild/rewriter.py
  4. 96
      run_unittests.py
  5. 18
      test cases/rewrite/1 basic/addSrc.json
  6. 26
      test cases/rewrite/1 basic/info.json
  7. 18
      test cases/rewrite/1 basic/rmSrc.json
  8. 2
      test cases/rewrite/2 subdirs/addSrc.json
  9. 2
      test cases/rewrite/2 subdirs/info.json
  10. 29
      test cases/rewrite/3 kwargs/add.json
  11. 20
      test cases/rewrite/3 kwargs/delete.json
  12. 20
      test cases/rewrite/3 kwargs/info.json
  13. 7
      test cases/rewrite/3 kwargs/meson.build
  14. 29
      test cases/rewrite/3 kwargs/remove.json
  15. 34
      test cases/rewrite/3 kwargs/set.json

@ -113,7 +113,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
prev_subdir = self.subdir
subdir = os.path.join(prev_subdir, args[0])
absdir = os.path.join(self.source_root, subdir)
buildfilename = os.path.join(self.subdir, environment.build_filename)
buildfilename = os.path.join(subdir, environment.build_filename)
absname = os.path.join(self.source_root, buildfilename)
symlinkless_dir = os.path.realpath(absdir)
if symlinkless_dir in self.visited_subdirs:
@ -128,7 +128,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
code = f.read()
assert(isinstance(code, str))
try:
codeblock = mparser.Parser(code, self.subdir).parse()
codeblock = mparser.Parser(code, subdir).parse()
except mesonlib.MesonException as me:
me.file = buildfilename
raise me

@ -51,9 +51,12 @@ class IntrospectionInterpreter(AstInterpreter):
self.default_options = {'backend': self.backend}
self.project_data = {}
self.targets = []
self.dependencies = []
self.project_node = None
self.funcs.update({
'add_languages': self.func_add_languages,
'dependency': self.func_dependency,
'executable': self.func_executable,
'jar': self.func_jar,
'library': self.func_library,
@ -65,6 +68,9 @@ class IntrospectionInterpreter(AstInterpreter):
})
def func_project(self, node, args, kwargs):
if self.project_node:
raise InvalidArguments('Second call to project()')
self.project_node = node
if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.')
@ -125,6 +131,16 @@ class IntrospectionInterpreter(AstInterpreter):
if lang not in self.coredata.compilers:
self.environment.detect_compilers(lang, need_cross_compiler)
def func_dependency(self, node, args, kwargs):
args = self.flatten_args(args)
if not args:
return
name = args[0]
self.dependencies += [{
'name': name,
'node': node
}]
def build_target(self, node, args, kwargs, targetclass):
if not args:
return
@ -159,10 +175,8 @@ class IntrospectionInterpreter(AstInterpreter):
if elemetary_nodes:
source_nodes += [curr]
# Filter out kwargs from other target types. For example 'soversion'
# passed to library() when default_library == 'static'.
kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs}
# Make sure nothing can crash when creating the build class
kwargs = {}
is_cross = False
objects = []
empty_sources = [] # Passing the unresolved sources list causes errors

@ -71,15 +71,224 @@ class RequiredKeys:
return wrapped
class MTypeBase:
def __init__(self, node: mparser.BaseNode):
if node is None:
self.node = self._new_node()
else:
self.node = node
self.node_type = None
for i in self.supported_nodes():
if isinstance(self.node, i):
self.node_type = i
def _new_node(self):
# Overwrite in derived class
return mparser.BaseNode()
def can_modify(self):
return self.node_type is not None
def get_node(self):
return self.node
def supported_nodes(self):
# Overwrite in derived class
return []
def set_value(self, value):
# Overwrite in derived class
mlog.warning('Cannot set the value of type', mlog.bold(type(self).__name__), '--> skipping')
def add_value(self, value):
# Overwrite in derived class
mlog.warning('Cannot add a value of type', mlog.bold(type(self).__name__), '--> skipping')
def remove_value(self, value):
# Overwrite in derived class
mlog.warning('Cannot remove a value of type', mlog.bold(type(self).__name__), '--> skipping')
class MTypeStr(MTypeBase):
def __init__(self, node: mparser.BaseNode):
super().__init__(node)
def _new_node(self):
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, ''))
def supported_nodes(self):
return [mparser.StringNode]
def set_value(self, value):
self.node.value = str(value)
class MTypeBool(MTypeBase):
def __init__(self, node: mparser.BaseNode):
super().__init__(node)
def _new_node(self):
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, False))
def supported_nodes(self):
return [mparser.BooleanNode]
def set_value(self, value):
self.node.value = bool(value)
class MTypeID(MTypeBase):
def __init__(self, node: mparser.BaseNode):
super().__init__(node)
def _new_node(self):
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, ''))
def supported_nodes(self):
return [mparser.IdNode]
def set_value(self, value):
self.node.value = str(value)
class MTypeList(MTypeBase):
def __init__(self, node: mparser.BaseNode):
super().__init__(node)
def _new_node(self):
return mparser.ArrayNode(mparser.ArgumentNode(mparser.Token('', '', 0, 0, 0, None, '')), 0, 0)
def _new_element_node(self, value):
# Overwrite in derived class
return mparser.BaseNode()
def _ensure_array_node(self):
if not isinstance(self.node, mparser.ArrayNode):
tmp = self.node
self.node = self._new_node()
self.node.args.arguments += [tmp]
def _check_is_equal(self, node, value):
# Overwrite in derived class
return False
def get_node(self):
if isinstance(self.node, mparser.ArrayNode):
if len(self.node.args.arguments) == 1:
return self.node.args.arguments[0]
return self.node
def supported_element_nodes(self):
# Overwrite in derived class
return []
def supported_nodes(self):
return [mparser.ArrayNode] + self.supported_element_nodes()
def set_value(self, value):
if not isinstance(value, list):
value = [value]
self._ensure_array_node()
self.node.args.arguments = [] # Remove all current nodes
for i in value:
self.node.args.arguments += [self._new_element_node(i)]
def add_value(self, value):
if not isinstance(value, list):
value = [value]
self._ensure_array_node()
for i in value:
self.node.args.arguments += [self._new_element_node(i)]
def remove_value(self, value):
def check_remove_node(node):
for j in value:
if self._check_is_equal(i, j):
return True
return False
if not isinstance(value, list):
value = [value]
self._ensure_array_node()
removed_list = []
for i in self.node.args.arguments:
if not check_remove_node(i):
removed_list += [i]
self.node.args.arguments = removed_list
class MtypeStrList(MTypeList):
def __init__(self, node: mparser.BaseNode):
super().__init__(node)
def _new_element_node(self, value):
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, str(value)))
def _check_is_equal(self, node, value):
if isinstance(node, mparser.StringNode):
return node.value == value
return False
def supported_element_nodes(self):
return [mparser.StringNode]
class MTypeIDList(MTypeList):
def __init__(self, node: mparser.BaseNode):
super().__init__(node)
def _new_element_node(self, value):
return mparser.IdNode(mparser.Token('', '', 0, 0, 0, None, str(value)))
def _check_is_equal(self, node, value):
if isinstance(node, mparser.IdNode):
return node.value == value
return False
def supported_element_nodes(self):
return [mparser.IdNode]
rewriter_keys = {
'kwargs': {
'function': (str, None, None),
'id': (str, None, None),
'operation': (str, None, ['set', 'delete', 'add', 'remove', 'info']),
'kwargs': (dict, {}, None)
},
'target': {
'target': (str, None, None),
'operation': (str, None, ['src_add', 'src_rm', 'test']),
'operation': (str, None, ['src_add', 'src_rm', 'info']),
'sources': (list, [], None),
'debug': (bool, False, None)
}
}
rewriter_func_kwargs = {
'dependency': {
'language': MTypeStr,
'method': MTypeStr,
'native': MTypeBool,
'not_found_message': MTypeStr,
'required': MTypeBool,
'static': MTypeBool,
'version': MtypeStrList,
'modules': MtypeStrList
},
'target': {
'build_by_default': MTypeBool,
'build_rpath': MTypeStr,
'dependencies': MTypeIDList,
'gui_app': MTypeBool,
'link_with': MTypeIDList,
'export_dynamic': MTypeBool,
'implib': MTypeBool,
'install': MTypeBool,
'install_dir': MTypeStr,
'install_rpath': MTypeStr,
'pie': MTypeBool
},
'project': {
'meson_version': MTypeStr,
'license': MtypeStrList,
'subproject_dir': MTypeStr,
'version': MTypeStr
}
}
class Rewriter:
def __init__(self, sourcedir: str, generator: str = 'ninja'):
self.sourcedir = sourcedir
@ -87,8 +296,10 @@ class Rewriter:
self.id_generator = AstIDGenerator()
self.modefied_nodes = []
self.functions = {
'kwargs': self.process_kwargs,
'target': self.process_target,
}
self.info_dump = None
def analyze_meson(self):
mlog.log('Analyzing meson file:', mlog.bold(os.path.join(self.sourcedir, environment.build_filename)))
@ -98,11 +309,152 @@ class Rewriter:
self.interpreter.ast.accept(AstIndentationGenerator())
self.interpreter.ast.accept(self.id_generator)
def add_info(self, cmd_type: str, cmd_id: str, data: dict):
if self.info_dump is None:
self.info_dump = {}
if cmd_type not in self.info_dump:
self.info_dump[cmd_type] = {}
self.info_dump[cmd_type][cmd_id] = data
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==!!')
def find_target(self, target: str):
for i in self.interpreter.targets:
if target == i['name'] or target == i['id']:
return i
return None
def check_list(name: str):
for i in self.interpreter.targets:
if name == i['name'] or name == i['id']:
return i
return None
tgt = check_list(target)
if tgt is not None:
return tgt
# Check the assignments
if target in self.interpreter.assignments:
node = self.interpreter.assignments[target][0]
if isinstance(node, mparser.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)
return tgt
def find_dependency(self, dependency: str):
def check_list(name: str):
for i in self.interpreter.dependencies:
if name == i['name']:
return i
return None
dep = check_list(dependency)
if dep is not None:
return dep
# Check the assignments
if dependency in self.interpreter.assignments:
node = self.interpreter.assignments[dependency][0]
if isinstance(node, mparser.FunctionNode):
if node.func_name in ['dependency']:
name = self.interpreter.flatten_args(node.args)[0]
dep = check_list(name)
return dep
@RequiredKeys(rewriter_keys['kwargs'])
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
kwargs_def = rewriter_func_kwargs[cmd['function']]
# Find the function node to modify
node = None
arg_node = None
if cmd['function'] == 'project':
node = self.interpreter.project_node
arg_node = node.args
elif cmd['function'] == 'target':
tmp = self.find_target(cmd['id'])
if tmp:
node = tmp['node']
arg_node = node.args
elif cmd['function'] == 'dependency':
tmp = self.find_dependency(cmd['id'])
if tmp:
node = tmp['node']
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))
# Print kwargs info
if cmd['operation'] == 'info':
info_data = {}
for key, val in arg_node.kwargs.items():
info_data[key] = None
if isinstance(val, mparser.ElementaryNode):
info_data[key] = val.value
elif isinstance(val, mparser.ArrayNode):
data_list = []
for i in val.args.arguments:
element = None
if isinstance(i, mparser.ElementaryNode):
element = i.value
data_list += [element]
info_data[key] = data_list
self.add_info('kwargs', '{}#{}'.format(cmd['function'], cmd['id']), info_data)
return # Nothing else to do
# Modify the kwargs
num_changed = 0
for key, val in cmd['kwargs'].items():
if key not in kwargs_def:
mlog.error('Cannot modify unknown kwarg --> skipping', mlog.bold(key))
continue
# Remove the key from the kwargs
if cmd['operation'] == 'delete':
if key in arg_node.kwargs:
mlog.log(' -- Deleting', mlog.bold(key), 'from the kwargs')
del arg_node.kwargs[key]
num_changed += 1
else:
mlog.log(' -- Key', mlog.bold(key), 'is already deleted')
continue
if key not in arg_node.kwargs:
arg_node.kwargs[key] = None
modifyer = kwargs_def[key](arg_node.kwargs[key])
if not modifyer.can_modify():
mlog.log(' -- Skipping', mlog.bold(key), 'because it is to complex to modify')
# Apply the operation
val_str = str(val)
if cmd['operation'] == 'set':
mlog.log(' -- Setting', mlog.bold(key), 'to', mlog.yellow(val_str))
modifyer.set_value(val)
elif cmd['operation'] == 'add':
mlog.log(' -- Adding', mlog.yellow(val_str), 'to', mlog.bold(key))
modifyer.add_value(val)
elif cmd['operation'] == 'remove':
mlog.log(' -- Removing', mlog.yellow(val_str), 'from', mlog.bold(key))
modifyer.remove_value(val)
# Write back the result
arg_node.kwargs[key] = modifyer.get_node()
num_changed += 1
if num_changed > 0 and node not in self.modefied_nodes:
self.modefied_nodes += [node]
@RequiredKeys(rewriter_keys['target'])
def process_target(self, cmd):
@ -191,7 +543,7 @@ class Rewriter:
if root not in self.modefied_nodes:
self.modefied_nodes += [root]
elif cmd['operation'] == 'test':
elif cmd['operation'] == 'info':
# List all sources in the target
src_list = []
for i in target['sources']:
@ -202,7 +554,7 @@ class Rewriter:
'name': target['name'],
'sources': src_list
}
mlog.log(' !! target {}={}'.format(target['id'], json.dumps(test_data)))
self.add_info('target', target['id'], test_data)
def process(self, cmd):
if 'type' not in cmd:
@ -314,4 +666,5 @@ def run(options):
rewriter.process(i)
rewriter.apply_changes()
rewriter.print_info()
return 0

@ -5053,7 +5053,7 @@ class PythonTests(BasePlatformTests):
class RewriterTests(BasePlatformTests):
data_regex = re.compile(r'^\s*!!\s*(\w+)\s+([^=]+)=(.*)$')
data_regex = re.compile(r'.*\n!!==JSON DUMP: BEGIN==!!\n(.*)\n!!==JSON DUMP: END==!!\n', re.MULTILINE | re.DOTALL)
def setUp(self):
super().setUp()
@ -5065,22 +5065,21 @@ class RewriterTests(BasePlatformTests):
def rewrite(self, directory, args):
if isinstance(args, str):
args = [args]
out = subprocess.check_output(self.rewrite_command + ['--sourcedir', directory] + args,
universal_newlines=True)
return out
command = self.rewrite_command + ['--sourcedir', directory] + args
p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
universal_newlines=True, timeout=60)
print(p.stdout)
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
def extract_test_data(self, out):
lines = out.split('\n')
match = RewriterTests.data_regex.match(out)
result = {}
for i in lines:
match = RewriterTests.data_regex.match(i)
if match:
typ = match.group(1)
id = match.group(2)
data = json.loads(match.group(3))
if typ not in result:
result[typ] = {}
result[typ][id] = data
if match:
result = json.loads(match.group(1))
return result
def test_target_source_list(self):
@ -5162,6 +5161,75 @@ class RewriterTests(BasePlatformTests):
out = self.extract_test_data(out)
self.assertDictEqual(list(out['target'].values())[0], expected)
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'},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
}
self.assertDictEqual(out, expected)
def test_kwargs_set(self):
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']},
'target#tgt1': {'build_by_default': False, 'build_rpath': '/usr/local', 'dependencies': 'dep1'},
'dependency#dep1': {'required': True, 'method': 'cmake'}
}
}
self.assertDictEqual(out, expected)
def test_kwargs_add(self):
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']},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
}
self.assertDictEqual(out, expected)
def test_kwargs_remove(self):
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'},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
}
self.assertDictEqual(out, expected)
def test_kwargs_delete(self):
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#': {},
'target#tgt1': {},
'dependency#dep1': {'required': False}
}
}
self.assertDictEqual(out, expected)
class NativeFileTests(BasePlatformTests):
def setUp(self):

@ -44,46 +44,46 @@
{
"type": "target",
"target": "trivialprog1",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog2",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog3",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog4",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog5",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog6",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog7",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog8",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog9",
"operation": "test"
"operation": "info"
}
]

@ -2,46 +2,46 @@
{
"type": "target",
"target": "trivialprog1",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog2",
"operation": "test"
"target": "exe2",
"operation": "info"
},
{
"type": "target",
"target": "trivialprog3",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog4",
"operation": "test"
"target": "exe4",
"operation": "info"
},
{
"type": "target",
"target": "trivialprog5",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog6",
"operation": "test"
"target": "exe6",
"operation": "info"
},
{
"type": "target",
"target": "trivialprog7",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog8",
"operation": "test"
"target": "exe8",
"operation": "info"
},
{
"type": "target",
"target": "trivialprog9",
"operation": "test"
"operation": "info"
}
]

@ -38,46 +38,46 @@
{
"type": "target",
"target": "trivialprog1",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog2",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog3",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog4",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog5",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog6",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog7",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog8",
"operation": "test"
"operation": "info"
},
{
"type": "target",
"target": "trivialprog9",
"operation": "test"
"operation": "info"
}
]

@ -8,6 +8,6 @@
{
"type": "target",
"target": "something",
"operation": "test"
"operation": "info"
}
]

@ -2,6 +2,6 @@
{
"type": "target",
"target": "something",
"operation": "test"
"operation": "info"
}
]

@ -0,0 +1,29 @@
[
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "set",
"kwargs": {
"license": "GPL"
}
},
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "add",
"kwargs": {
"license": ["MIT"]
}
},
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "add",
"kwargs": {
"license": "BSD"
}
}
]

@ -0,0 +1,20 @@
[
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "delete",
"kwargs": {
"version": null
}
},
{
"type": "kwargs",
"function": "target",
"id": "helloWorld",
"operation": "delete",
"kwargs": {
"build_by_default": false
}
}
]

@ -0,0 +1,20 @@
[
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "info"
},
{
"type": "kwargs",
"function": "target",
"id": "tgt1",
"operation": "info"
},
{
"type": "kwargs",
"function": "dependency",
"id": "dep1",
"operation": "info"
}
]

@ -0,0 +1,7 @@
project('rewritetest', 'cpp', version: '0.0.1')
# Find ZLIB
dep1 = dependency('zlib', required: false)
# Make a test exe
tgt1 = executable('helloWorld', 'main.cpp', build_by_default: true)

@ -0,0 +1,29 @@
[
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "set",
"kwargs": {
"license": ["GPL", "MIT", "BSD"]
}
},
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "remove",
"kwargs": {
"license": ["MIT"]
}
},
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "remove",
"kwargs": {
"license": "BSD"
}
}
]

@ -0,0 +1,34 @@
[
{
"type": "kwargs",
"function": "project",
"id": "",
"operation": "set",
"kwargs": {
"version": "0.0.2",
"meson_version": "0.50.0",
"license": ["GPL", "MIT"]
}
},
{
"type": "kwargs",
"function": "target",
"id": "helloWorld",
"operation": "set",
"kwargs": {
"build_by_default": false,
"build_rpath": "/usr/local",
"dependencies": "dep1"
}
},
{
"type": "kwargs",
"function": "dependency",
"id": "zlib",
"operation": "set",
"kwargs": {
"required": true,
"method": "cmake"
}
}
]
Loading…
Cancel
Save