diff --git a/docs/markdown/snippets/strip.md b/docs/markdown/snippets/strip.md new file mode 100644 index 000000000..e971df699 --- /dev/null +++ b/docs/markdown/snippets/strip.md @@ -0,0 +1,5 @@ +## `meson install --strip` + +It is now possible to strip targets using `meson install --strip` even if +`-Dstrip=true` option was not set during configuration. This allows doing +stripped and not stripped installations without reconfiguring the build. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index eeb7d62d6..6a78ed054 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -144,6 +144,7 @@ class TargetInstallData: subproject: str optional: bool = False tag: T.Optional[str] = None + can_strip: bool = False def __post_init__(self, outdir_name: str) -> None: self.out_name = os.path.join(outdir_name, os.path.basename(self.fname)) @@ -1573,7 +1574,8 @@ class Backend: # # TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more # fine-grained stripping of static archives. - should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target(OptionKey('strip'), t) + can_strip = not isinstance(t, build.StaticLibrary) + should_strip = can_strip and self.get_option_for_target(OptionKey('strip'), t) assert isinstance(should_strip, bool), 'for mypy' # Install primary build output (library/executable/jar, etc) # Done separately because of strip/aliases/rpath @@ -1584,7 +1586,7 @@ class Backend: install_dir_name, should_strip, mappings, t.rpath_dirs_to_remove, t.install_rpath, install_mode, t.subproject, - tag=tag) + tag=tag, can_strip=can_strip) d.targets.append(i) for alias, to, tag in t.get_aliases(): diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index b51d4f0db..f33ad0aa3 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -61,6 +61,7 @@ if T.TYPE_CHECKING: dry_run: bool skip_subprojects: str tags: str + strip: bool symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file, @@ -88,6 +89,8 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='Do not install files from given subprojects. (Since 0.58.0)') parser.add_argument('--tags', default=None, help='Install only targets having one of the given tags. (Since 0.60.0)') + parser.add_argument('--strip', action='store_true', + help='Strip targets even if strip option was not set during configure. (Since 0.62.0)') class DirMaker: def __init__(self, lf: T.TextIO, makedirs: T.Callable[..., None]): @@ -564,6 +567,15 @@ class Installer: else: raise + def do_strip(self, strip_bin: T.List[str], fname: str, outname: str) -> None: + self.log(f'Stripping target {fname!r}.') + returncode, stdo, stde = self.Popen_safe(strip_bin + [outname]) + if returncode != 0: + print('Could not strip file.\n') + print(f'Stdout:\n{stdo}\n') + print(f'Stderr:\n{stde}\n') + sys.exit(1) + def install_subdirs(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for i in d.install_subdirs: if not self.should_install(i): @@ -676,7 +688,7 @@ class Installer: outdir = get_destdir_path(destdir, fullprefix, t.outdir) outname = os.path.join(outdir, os.path.basename(fname)) final_path = os.path.join(d.prefix, t.outdir, os.path.basename(fname)) - should_strip = t.strip + should_strip = t.strip or (t.can_strip and self.options.strip) install_rpath = t.install_rpath install_name_mappings = t.install_name_mappings install_mode = t.install_mode @@ -689,13 +701,7 @@ class Installer: if fname.endswith('.jar'): self.log('Not stripping jar target: {}'.format(os.path.basename(fname))) continue - self.log('Stripping target {!r} using {}.'.format(fname, d.strip_bin[0])) - returncode, stdo, stde = self.Popen_safe(d.strip_bin + [outname]) - if returncode != 0: - print('Could not strip file.\n') - print(f'Stdout:\n{stdo}\n') - print(f'Stderr:\n{stde}\n') - sys.exit(1) + self.do_strip(d.strip_bin, fname, outname) if fname.endswith('.js'): # Emscripten outputs js files and optionally a wasm file. # If one was generated, install it as well. @@ -721,7 +727,6 @@ class Installer: else: raise - def rebuild_all(wd: str) -> bool: if not (Path(wd) / 'build.ninja').is_file(): print('Only ninja backend is supported to rebuild the project before installation.') diff --git a/test cases/unit/104 strip/lib.c b/test cases/unit/104 strip/lib.c new file mode 100644 index 000000000..3940fde7e --- /dev/null +++ b/test cases/unit/104 strip/lib.c @@ -0,0 +1 @@ +void func(void){} diff --git a/test cases/unit/104 strip/meson.build b/test cases/unit/104 strip/meson.build new file mode 100644 index 000000000..dff61ab43 --- /dev/null +++ b/test cases/unit/104 strip/meson.build @@ -0,0 +1,3 @@ +project('strip', 'c') + +shared_library('a', 'lib.c', install: true) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index f3dc7b3e7..79db0b896 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1760,3 +1760,23 @@ class LinuxlikeTests(BasePlatformTests): # If so, we can test that cmake works with "gcc -m32" self.do_one_test_with_nativefile('../cmake/1 basic', "['gcc', '-m32']") + + @skipUnless(is_linux(), 'Test only applicable to Linux') + def test_install_strip(self): + testdir = os.path.join(self.unit_test_dir, '104 strip') + self.init(testdir) + self.build() + + destdir = self.installdir + self.prefix + lib = os.path.join(destdir, self.libdir, 'liba.so') + install_cmd = self.meson_command + ['install', '--destdir', self.installdir] + + # Check we have debug symbols by default + self._run(install_cmd, workdir=self.builddir) + stdout = self._run(['file', '-b', lib]) + self.assertIn('not stripped', stdout) + + # Check debug symbols got removed with --strip + self._run(install_cmd + ['--strip'], workdir=self.builddir) + stdout = self._run(['file', '-b', lib]) + self.assertNotIn('not stripped', stdout)