Skip to content

Commit

Permalink
Add build target keyword parameter 'build_subdir' [v3]
Browse files Browse the repository at this point in the history
Place the build products in a directory of the specified name
somewhere within the build directory. This allows use of the target
that includes a specific directory name:

        #include <subdir/configure.h>

This also allows creating targets with the same basename by using
different subdirectory names.

v2:
    Move build_subdir to Target class.
    Error if path separator in build_dir

v3:
    Rename to 'build_subdir' to make it clear that the name is
    appended to a meson-specific build directory, and does not
    provide the user with a way to define the overall meson build
    heirarchy.

    Allow build_subdir to include path separators.

    Support 'build_subdir' for configure_file.

Signed-off-by: Keith Packard <keithp@keithp.com>
  • Loading branch information
keith-packard committed Dec 11, 2024
1 parent 54cab09 commit d5c8541
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 14 deletions.
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
10 changes: 7 additions & 3 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 @@ -2670,6 +2673,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var],
fmt = kwargs['format']
output_format = kwargs['output_format']
depfile = kwargs['depfile']
self.build_subdir = kwargs['build_subdir']

# Validate input
inputs = self.source_strings_to_files(kwargs['input'])
Expand Down Expand Up @@ -2698,8 +2702,9 @@ 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))
(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 +2720,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
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 : 'subdir',
install_dir : 'share/appdir/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/subdir/config2.h"}
]
}

0 comments on commit d5c8541

Please sign in to comment.