build: use suffix when getting target id for exes

When checking target names, meson explictly forbids having multiple
targets with the same name. This is good, but it is strict and it is
impossible to have targets with the same basename and differing suffixes
(e.g. foo and foo.bin) in the same directory. Allow this for executables
by including the suffix (if it exists) in the interal target id. So foo
would be foo@exe and foo.bin would be foo.bin@exe.
pull/12340/head
Dudemanguy 2 years ago committed by Dylan Baker
parent 4fadb2a296
commit adb1a360b9
  1. 14
      docs/markdown/snippets/executable-suffixes.md
  2. 3
      docs/yaml/functions/executable.yaml
  3. 14
      mesonbuild/build.py
  4. 8
      mesonbuild/interpreter/interpreter.py
  5. 1
      test cases/unit/119 executable suffix/main.c
  6. 3
      test cases/unit/119 executable suffix/meson.build
  7. 10
      unittests/allplatformstests.py

@ -0,0 +1,14 @@
## Target names for executables now take into account suffixes.
In previous versions of meson, a `meson.build` file like this:
```
exectuable('foo', 'main.c')
exectuable('foo', 'main.c', name_suffix: 'bar')
```
would result in a configure error because meson internally used
the same id for both executables. This build file is now allowed
since meson takes into account the `bar` suffix when generating the
second executable. This allows for executables with the same basename
but different suffixes to be built in the same subdirectory.

@ -10,6 +10,9 @@ description: |
The returned object also has methods that are documented in [[@exe]].
*Since 1.3.0* executable names can be the same across multiple targets as
long as they each have a different `name_suffix`.
warnings:
- The `link_language` kwarg was broken until 0.55.0

@ -634,8 +634,11 @@ class Target(HoldableObject, metaclass=abc.ABCMeta):
return my_id
def get_id(self) -> str:
name = self.name
if getattr(self, 'name_suffix_set', False):
name += '.' + self.suffix
return self.construct_id_from_path(
self.subdir, self.name, self.type_suffix())
self.subdir, name, self.type_suffix())
def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None:
if 'build_by_default' in kwargs:
@ -2013,7 +2016,14 @@ class Executable(BuildTarget):
and self.environment.coredata.get_option(OptionKey("debug"))
)
if create_debug_file:
self.debug_filename = self.name + '.pdb'
# If the target is has a standard exe extension (i.e. 'foo.exe'),
# then the pdb name simply becomes 'foo.pdb'. If the extension is
# something exotic, then include that in the name for uniqueness
# reasons (e.g. 'foo_com.pdb').
name = self.name
if getattr(self, 'suffix', 'exe') != 'exe':
name += '_' + self.suffix
self.debug_filename = name + '.pdb'
def process_kwargs(self, kwargs):
super().process_kwargs(kwargs)

@ -3169,8 +3169,12 @@ class Interpreter(InterpreterBase, HoldableObject):
# To permit an executable and a shared library to have the
# same name, such as "foo.exe" and "libfoo.a".
idname = tobj.get_id()
if idname in self.build.targets:
raise InvalidCode(f'Tried to create target "{name}", but a target of that name already exists.')
for t in self.build.targets.values():
if t.get_id() == idname:
raise InvalidCode(f'Tried to create target "{name}", but a target of that name already exists.')
if isinstance(tobj, build.Executable) and isinstance(t, build.Executable) and t.name == tobj.name:
FeatureNew.single_use('multiple executables with the same name but different suffixes',
'1.3.0', self.subproject, location=self.current_node)
if isinstance(tobj, build.BuildTarget):
self.add_languages(tobj.missing_languages, True, tobj.for_machine)

@ -0,0 +1 @@
int main(void) { return 0; }

@ -0,0 +1,3 @@
project('exectuable suffix', 'c')
foo = executable('foo', 'main.c')
foo_bin = executable('foo', 'main.c', name_suffix: 'bin')

@ -2039,6 +2039,16 @@ class AllPlatformTests(BasePlatformTests):
original = get_opt()
self.assertDictEqual(original, expected)
def test_executable_names(self):
testdir = os.path.join(self.unit_test_dir, '119 executable suffix')
self.init(testdir)
self.build()
exe1 = os.path.join(self.builddir, 'foo' + exe_suffix)
exe2 = os.path.join(self.builddir, 'foo.bin')
self.assertPathExists(exe1)
self.assertPathExists(exe2)
self.assertNotEqual(exe1, exe2)
def opt_has(self, name, value):
res = self.introspect('--buildoptions')
found = False

Loading…
Cancel
Save