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 build target keyword parameter 'build_subdir' [v3] #14002

Open
wants to merge 1 commit 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/yaml/functions/_build_target_base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,20 @@ kwargs:

This allows renaming similar to the dependency renaming feature of cargo
or `extern crate foo as bar` inside rust code.

build_subdir:
type: str
since: 1.7.0
description:
Places the build results in a subdirectory of the given name
rather than directly into the build directory. This does not
affect the install directory, which uses install_dir.

This allows inserting a directory name into the build path,
either when needed to use the build result while building other
targets or as a way to support multiple targets with the same
basename by using unique build_subdir values for each one.

build_subdir may not match a file or directory in the source
directory, nor may it include '..' to refer to the parent of the
build directory.
17 changes: 17 additions & 0 deletions docs/yaml/functions/configure_file.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,20 @@ kwargs:
description: |
When specified, macro guards will be used instead of '#pragma once'. The
macro guard name will be the specified name.

build_subdir:
type: str
since: 1.7.0
description:
Places the build results in a subdirectory of the given name
rather than directly into the build directory. This does not
affect the install directory, which uses install_dir.

This allows inserting a directory name into the build path,
either when needed to use the build result while building other
targets or as a way to support multiple targets with the same
basename by using unique build_subdir values for each one.

build_subdir may not match a file or directory in the source
directory, nor may it include '..' to refer to the parent of the
build directory.
13 changes: 8 additions & 5 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,9 @@ def get_source_dir_include_args(self, target: build.BuildTarget, compiler: 'Comp

def get_build_dir_include_args(self, target: build.BuildTarget, compiler: 'Compiler', *, absolute_path: bool = False) -> T.List[str]:
if absolute_path:
curdir = os.path.join(self.build_dir, target.get_subdir())
curdir = os.path.join(self.build_dir, target.get_builddir())
else:
curdir = target.get_subdir()
curdir = target.get_builddir()
if curdir == '':
curdir = '.'
return compiler.get_include_args(curdir, False)
Expand Down Expand Up @@ -370,9 +370,12 @@ def get_target_dir(self, target: T.Union[build.Target, build.CustomTargetIndex])
# this produces no output, only a dummy top-level name
dirname = ''
elif self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
dirname = target.get_subdir()
dirname = target.get_builddir()
else:
dirname = 'meson-out'
build_subdir = target.get_build_subdir()
if build_subdir:
dirname = os.path.join(dirname, build_subdir)
return dirname

def get_target_dir_relative_to(self, t: build.Target, o: build.Target) -> str:
Expand Down Expand Up @@ -482,7 +485,7 @@ def _flatten_object_list(self, target: build.BuildTarget,
for obj in objects:
if isinstance(obj, str):
o = os.path.join(proj_dir_to_build_root,
self.build_to_src, target.get_subdir(), obj)
self.build_to_src, target.get_builddir(), obj)
obj_list.append(o)
elif isinstance(obj, mesonlib.File):
if obj.is_built:
Expand Down Expand Up @@ -1276,7 +1279,7 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali
ld_lib_path_libs.add(l)

env_build_dir = self.environment.get_build_dir()
ld_lib_path: T.Set[str] = set(os.path.join(env_build_dir, l.get_subdir()) for l in ld_lib_path_libs)
ld_lib_path: T.Set[str] = set(os.path.join(env_build_dir, l.get_builddir()) for l in ld_lib_path_libs)

if ld_lib_path:
t_env.prepend('LD_LIBRARY_PATH', list(ld_lib_path), ':')
Expand Down
24 changes: 21 additions & 3 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class DFeatures(TypedDict):
buildtarget_kwargs = {
'build_by_default',
'build_rpath',
'build_subdir',
'dependencies',
'extra_files',
'gui_app',
Expand Down Expand Up @@ -525,6 +526,7 @@ class Target(HoldableObject, metaclass=abc.ABCMeta):
build_always_stale: bool = False
extra_files: T.List[File] = field(default_factory=list)
override_options: InitVar[T.Optional[T.Dict[OptionKey, str]]] = None
build_subdir: str = ''

@abc.abstractproperty
def typename(self) -> str:
Expand All @@ -548,6 +550,9 @@ def __post_init__(self, overrides: T.Optional[T.Dict[OptionKey, str]]) -> None:
Target "{self.name}" has a path separator in its name.
This is not supported, it can cause unexpected failures and will become
a hard error in the future.'''))
self.builddir = self.subdir
if self.build_subdir:
self.builddir = os.path.join(self.subdir, self.build_subdir)

# dataclass comparators?
def __lt__(self, other: object) -> bool:
Expand Down Expand Up @@ -608,6 +613,12 @@ def get_subdir(self) -> str:
def get_typename(self) -> str:
return self.typename

def get_build_subdir(self) -> str:
return self.build_subdir

def get_builddir(self) -> str:
return self.builddir

@staticmethod
def _get_id_hash(target_id: str) -> str:
# We don't really need cryptographic security here.
Expand Down Expand Up @@ -642,7 +653,7 @@ def get_id(self) -> str:
if getattr(self, 'name_suffix_set', False):
name += '.' + self.suffix
return self.construct_id_from_path(
self.subdir, name, self.type_suffix())
self.builddir, name, self.type_suffix())

def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None:
if 'build_by_default' in kwargs:
Expand Down Expand Up @@ -738,7 +749,7 @@ def __init__(
environment: environment.Environment,
compilers: T.Dict[str, 'Compiler'],
kwargs: T.Dict[str, T.Any]):
super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False))
super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False), build_subdir=kwargs.get('build_subdir', ''))
self.all_compilers = compilers
self.compilers: OrderedDict[str, Compiler] = OrderedDict()
self.objects: T.List[ObjectTypes] = []
Expand Down Expand Up @@ -2647,10 +2658,11 @@ def __init__(self,
absolute_paths: bool = False,
backend: T.Optional['Backend'] = None,
description: str = 'Generating {} with a custom command',
build_subdir: str = '',
):
# TODO expose keyword arg to make MachineChoice.HOST configurable
super().__init__(name, subdir, subproject, False, MachineChoice.HOST, environment,
install, build_always_stale)
install, build_always_stale, build_subdir = build_subdir)
self.sources = list(sources)
self.outputs = substitute_values(
outputs, get_filenames_templates_dict(
Expand Down Expand Up @@ -3027,6 +3039,12 @@ def get_outputs(self) -> T.List[str]:
def get_subdir(self) -> str:
return self.target.get_subdir()

def get_build_subdir(self) -> str:
return self.target.get_build_subdir()

def get_builddir(self) -> str:
return self.target.get_builddir()

def get_filename(self) -> str:
return self.output

Expand Down
31 changes: 27 additions & 4 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2055,6 +2055,7 @@ def _validate_custom_target_outputs(self, has_multi_in: bool, outputs: T.Iterabl
KwargInfo('feed', bool, default=False, since='0.59.0'),
KwargInfo('capture', bool, default=False),
KwargInfo('console', bool, default=False, since='0.48.0'),
KwargInfo('build_subdir', str, default='', since='1.7.0'),
)
def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str],
kwargs: 'kwtypes.CustomTarget') -> build.CustomTarget:
Expand Down Expand Up @@ -2145,7 +2146,8 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str],
install_dir=kwargs['install_dir'],
install_mode=install_mode,
install_tag=kwargs['install_tag'],
backend=self.backend)
backend=self.backend,
build_subdir=kwargs['build_subdir'])
self.add_target(tg.name, tg)
return tg

Expand Down Expand Up @@ -2643,6 +2645,7 @@ def func_install_subdir(self, node: mparser.BaseNode, args: T.Tuple[str],
KwargInfo('output_format', str, default='c', since='0.47.0', since_values={'json': '1.3.0'},
validator=in_set_validator({'c', 'json', 'nasm'})),
KwargInfo('macro_name', (str, NoneType), default=None, since='1.3.0'),
KwargInfo('build_subdir', str, default='', since='1.7.0'),
)
def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var],
kwargs: kwtypes.ConfigureFile):
Expand Down Expand Up @@ -2698,8 +2701,19 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var],
mlog.warning('Output file', mlog.bold(ofile_rpath, True), 'for configure_file() at', current_call, 'overwrites configure_file() output at', first_call)
else:
self.configure_file_outputs[ofile_rpath] = self.current_node.lineno
(ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output))

# Validate build_subdir
build_subdir = kwargs['build_subdir']
self.build_subdir = build_subdir
if self.build_subdir and self.build_subdir != '.':
if os.path.exists(os.path.join(self.source_root, self.subdir, build_subdir)):
raise InvalidArguments(f'Build subdir "{build_subdir}" in output "{output}" exists in source tree.')
if '..' in build_subdir:
raise InvalidArguments(f'Build subdir "{build_subdir}" in output "{output}" contains ..')

(ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, self.build_subdir, output))
ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname)
os.makedirs(os.path.split(ofile_abs)[0], exist_ok=True)

# Perform the appropriate action
if kwargs['configuration'] is not None:
Expand All @@ -2715,7 +2729,6 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var],
if len(inputs) > 1:
raise InterpreterException('At most one input file can given in configuration mode')
if inputs:
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
file_encoding = kwargs['encoding']
missing_variables, confdata_useless = \
mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf,
Expand Down Expand Up @@ -3224,11 +3237,21 @@ def add_target(self, name: str, tobj: build.Target) -> None:
To define a target that builds in that directory you must define it
in the meson.build file in that directory.
'''))

# Make sure build_subdir doesn't exist in the source tree and
# doesn't contain ..
build_subdir = tobj.get_build_subdir()
if build_subdir and build_subdir != '.':
if os.path.exists(os.path.join(self.source_root, self.subdir, build_subdir)):
raise InvalidArguments(f'Build subdir "{build_subdir}" in target "{name}" exists in source tree.')
if '..' in build_subdir:
raise InvalidArguments(f'Build subdir "{build_subdir}" in target "{name}" contains ..')

self.validate_forbidden_targets(name)
# To permit an executable and a shared library to have the
# same name, such as "foo.exe" and "libfoo.a".
idname = tobj.get_id()
subdir = tobj.get_subdir()
subdir = tobj.get_builddir()
namedir = (name, subdir)

if idname in self.build.targets:
Expand Down
1 change: 1 addition & 0 deletions mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ def _objects_validator(vals: T.List[ObjectTypes]) -> T.Optional[str]:
('1.1.0', 'generated sources as positional "objects" arguments')
},
),
KwargInfo('build_subdir', str, default='', since='1.7.0')
]


Expand Down
10 changes: 10 additions & 0 deletions test cases/common/109 custom target capture/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,13 @@ if not os.path.exists(sys.argv[1]):
'''

test('capture-wrote', python3, args : ['-c', ct_output_exists, mytarget])

mytarget = custom_target('bindat',
output : 'data.dat',
input : 'data_source.txt',
build_subdir : 'subdir2',
capture : true,
command : [python3, comp, '@INPUT@'],
install : true,
install_dir : 'subdir2'
)
3 changes: 2 additions & 1 deletion test cases/common/109 custom target capture/test.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"installed": [
{"type": "file", "file": "usr/subdir/data.dat"}
{"type": "file", "file": "usr/subdir/data.dat"},
{"type": "file", "file": "usr/subdir2/data.dat"}
]
}
8 changes: 8 additions & 0 deletions test cases/common/117 shared module/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ test('import test', e, args : m)
m2 = build_target('mymodule2', 'module.c', target_type: 'shared_module')
test('import test 2', e, args : m2)

# Same as above, but built and installed in a sub directory
m2_subdir = build_target('mymodule2', 'module.c',
target_type: 'shared_module',
build_subdir: 'subdir',
install: true,
install_dir: join_paths(get_option('libdir'), 'modules/subdir'))
test('import test 2 subdir', e, args : m2_subdir)

# Shared module that does not export any symbols
shared_module('nosyms', 'nosyms.c',
override_options: ['werror=false'],
Expand Down
5 changes: 4 additions & 1 deletion test cases/common/117 shared module/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"installed": [
{"type": "expr", "file": "usr/lib/modules/libnosyms?so"},
{"type": "implibempty", "file": "usr/lib/modules/libnosyms"},
{"type": "pdb", "file": "usr/lib/modules/nosyms"}
{"type": "pdb", "file": "usr/lib/modules/nosyms"},
{"type": "expr", "file": "usr/lib/modules/subdir/libmymodule2?so"},
{"type": "implib", "file": "usr/lib/modules/subdir/libmymodule2"},
{"type": "pdb", "file": "usr/lib/modules/subdir/mymodule2"}
]
}
7 changes: 7 additions & 0 deletions test cases/common/14 configure file/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ configure_file(input : files('config.h.in'),
output : 'config2.h',
configuration : conf)

# Test if build_subdir works
configure_file(input : files('config.h.in'),
output : 'config2.h',
build_subdir : 'config-subdir',
install_dir : 'share/appdir/config-subdir',
configuration : conf)

# Now generate a header file with an external script.
genprog = import('python3').find_python()
scriptfile = '@0@/generator.py'.format(meson.current_source_dir())
Expand Down
3 changes: 2 additions & 1 deletion test cases/common/14 configure file/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{"type": "file", "file": "usr/share/appdir/config2b.h"},
{"type": "file", "file": "usr/share/appdireh/config2-1.h"},
{"type": "file", "file": "usr/share/appdirok/config2-2.h"},
{"type": "file", "file": "usr/share/configure file test/invalid-utf8-1.bin"}
{"type": "file", "file": "usr/share/configure file test/invalid-utf8-1.bin"},
{"type": "file", "file": "usr/share/appdir/config-subdir/config2.h"}
]
}
Loading