Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add include() function #3557

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/markdown/Reference-manual.md
Expand Up @@ -674,6 +674,23 @@ hypothetical `testmod` module.
tmod.do_something()
```

### include()

``` meson
void include(file, ...)
```

Executes the `file`. Once that is done, it returns and execution continues on the
line following this `include()` command. Variables defined in that
`file` are then available for use in later parts of the
current build file and in all subsequent build files executed with `subdir()`.

This function has one keyword argument.

- `if_found` takes one or several dependency objects and will only
execute the file if they all return `true` when queried with
`.found()`

### include_directories()

``` meson
Expand Down Expand Up @@ -1165,7 +1182,7 @@ the invocation. All steps executed up to this point are valid and will
be executed by meson. This means that all targets defined before the call
of `subdir_done` will be build.

If the current script was called by `subdir` the execution returns to the
If the current script was called by `subdir` or `include` the execution returns to the
calling directory and continues as if the script had reached the end.
If the current script is the top level script meson configures the project
as defined up to this point.
Expand Down
42 changes: 42 additions & 0 deletions mesonbuild/interpreter.py
Expand Up @@ -1666,6 +1666,7 @@ def get_cross_property_method(self, args, kwargs):
'executable': build.known_exe_kwargs,
'find_program': {'required', 'native'},
'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'},
'include': {'if_found'},
'include_directories': {'is_system'},
'install_data': {'install_dir', 'install_mode', 'rename', 'sources'},
'install_headers': {'install_dir', 'subdir'},
Expand Down Expand Up @@ -1711,6 +1712,7 @@ def __init__(self, build, backend, subproject='', subdir='', subproject_dir='sub
self.builtin.update({'meson': MesonMain(build, self)})
self.generators = []
self.visited_subdirs = {}
self.included_files = set()
self.project_args_frozen = False
self.global_args_frozen = False # implies self.project_args_frozen
self.subprojects = {}
Expand Down Expand Up @@ -1761,6 +1763,7 @@ def build_func_dict(self):
'files': self.func_files,
'find_library': self.func_find_library,
'find_program': self.func_find_program,
'include': self.func_include,
'include_directories': self.func_include_directories,
'import': self.func_import,
'install_data': self.func_install_data,
Expand Down Expand Up @@ -3226,6 +3229,45 @@ def func_configure_file(self, node, args, kwargs):
self.build.data.append(build.Data([cfile], idir))
return mesonlib.File.from_built_file(self.subdir, output)

@permittedKwargs(permitted_kwargs['include'])
def func_include(self, node, args, kwargs):
self.validate_arguments(args, 1, [str])
mesonlib.check_direntry_issues(args)
for i in mesonlib.extract_as_list(kwargs, 'if_found'):
if not hasattr(i, 'found_method'):
raise InterpreterException('Object used in if_found does not have a found method.')
if not i.found_method([], {}):
return
if os.path.isabs(args[0]):
absname = os.path.abspath(args[0])
else:
absname = os.path.abspath(os.path.join(self.environment.get_source_dir(), self.subdir, args[0]))
prev_current_file = self.current_file
if absname in self.included_files:
raise InvalidArguments('Tried to include file "%s", which has already been included.'
% absname)
self.included_files.add(absname)
if absname not in self.build_def_files:
self.build_def_files.append(absname)
if not os.path.isfile(absname):
raise InterpreterException('Non-existent build file {!r}'.format(absname))
with open(absname, encoding='utf8') as f:
code = f.read()
assert(isinstance(code, str))
try:
codeblock = mparser.Parser(code, self.subdir).parse()
except mesonlib.MesonException as me:
me.file = self.current_file
raise me
self.current_file = absname
try:
self.evaluate_codeblock(codeblock)
except mesonlib.MesonException as me:
me.file = self.current_file
raise me
self.included_files.clear()
self.current_file = prev_current_file

@permittedKwargs(permitted_kwargs['include_directories'])
@stringArgs
def func_include_directories(self, node, args, kwargs):
Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/interpreterbase.py
Expand Up @@ -181,12 +181,14 @@ def __init__(self, source_root, subdir):
self.funcs = {}
self.builtin = {}
self.subdir = subdir
self.current_file = ''
self.variables = {}
self.argument_depth = 0
self.current_lineno = -1

def load_root_meson_file(self):
mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
self.current_file = mesonfile
if not os.path.isfile(mesonfile):
raise InvalidArguments('Missing Meson file in %s' % mesonfile)
with open(mesonfile, encoding='utf8') as mf:
Expand Down
2 changes: 2 additions & 0 deletions test cases/common/199 include/included_file
@@ -0,0 +1,2 @@
executable('test_include', 'main.c')

1 change: 1 addition & 0 deletions test cases/common/199 include/incr_variable
@@ -0,0 +1 @@
variable += 1
5 changes: 5 additions & 0 deletions test cases/common/199 include/main.c
@@ -0,0 +1,5 @@
int main(int argc, char **argv)
{
return 0;
}

18 changes: 18 additions & 0 deletions test cases/common/199 include/meson.build
@@ -0,0 +1,18 @@
# Should read instructions given in included_file

project('example include', 'c')

variable = 1

assert(variable == 1, 'variable doesn\'t have correct value')

# Check whether it's possible to include the same file several times
include('incr_variable')
include('incr_variable')
include('incr_variable')

assert(variable == 4, 'variable doesn\'t have correct value')

# An executable is declared in included_file
include('included_file')

1 change: 1 addition & 0 deletions test cases/common/200 include if_found/included_file
@@ -0,0 +1 @@
variable = 5
8 changes: 8 additions & 0 deletions test cases/common/200 include if_found/meson.build
@@ -0,0 +1,8 @@
project('subdir if found', 'c')

not_found_dep = dependency('nonexisting', required : false)

variable = 3

include('included_file', if_found : not_found_dep)
assert(variable == 3, 'included_file was unexpectedly included.')
6 changes: 6 additions & 0 deletions test cases/failing/75 missing included file/meson.build
@@ -0,0 +1,6 @@
# Should fail because included_file is missing

project('missing included file', 'c')

include('included_file')

@@ -0,0 +1 @@
include('included_file2')
@@ -0,0 +1 @@
include('included_file1')
@@ -0,0 +1,7 @@
# Should fail because included_file1 includes included_file2 and
# included_file2 includes included_file1

project('included file detected cyclic dependency', 'c')

include('included_file2')