aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNirbheek Chauhan <nirbheek@centricular.com>2017-02-03 17:21:08 +0530
committerNirbheek Chauhan <nirbheek@centricular.com>2017-02-18 02:38:54 +0530
commita14eba27a9d68fa128dbcb9d4f4fd4d4a4e70f09 (patch)
tree487aa9bfcd4a96d41d819452ccd68d2d4af4d41c
parent217eae4011fa7693f6538d4240009fca7722d756 (diff)
downloadmeson-a14eba27a9d68fa128dbcb9d4f4fd4d4a4e70f09.zip
meson-a14eba27a9d68fa128dbcb9d4f4fd4d4a4e70f09.tar.gz
meson-a14eba27a9d68fa128dbcb9d4f4fd4d4a4e70f09.tar.bz2
ninja: Delete output static lib before calling `ar`
Otherwise if the list of sources changes on reconfigure after building, the static library will contain both the old and new objects. Closes https://github.com/mesonbuild/meson/issues/1355
-rw-r--r--mesonbuild/backend/ninjabackend.py14
-rwxr-xr-xrun_unittests.py41
-rw-r--r--test cases/common/3 static/libfile2.c3
-rw-r--r--test cases/common/3 static/meson.build3
-rw-r--r--test cases/common/3 static/meson_options.txt1
5 files changed, 58 insertions, 4 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index a22e0ab..27e1e9a 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -30,9 +30,11 @@ from collections import OrderedDict
if mesonlib.is_windows():
quote_char = '"'
execute_wrapper = 'cmd /c'
+ rmfile_prefix = 'del /f /s /q {} &&'
else:
quote_char = "'"
execute_wrapper = ''
+ rmfile_prefix = 'rm -f {} &&'
def ninja_quote(text):
return text.replace(' ', '$ ').replace(':', '$:')
@@ -1238,10 +1240,16 @@ int dummy;
'''
else:
command_template = ' command = {executable} $LINK_ARGS {output_args} $in\n'
+ cmdlist = []
+ if isinstance(static_linker, compilers.ArLinker):
+ # `ar` has no options to overwrite archives. It always appends,
+ # which is never what we want. Delete an existing library first if
+ # it exists. https://github.com/mesonbuild/meson/issues/1355
+ cmdlist = [execute_wrapper, rmfile_prefix.format('$out')]
+ cmdlist += static_linker.get_exelist()
command = command_template.format(
- executable=' '.join(static_linker.get_exelist()),
- output_args=' '.join(static_linker.get_output_args('$out'))
- )
+ executable=' '.join(cmdlist),
+ output_args=' '.join(static_linker.get_output_args('$out')))
description = ' description = Static linking library $out\n\n'
outfile.write(rule)
outfile.write(command)
diff --git a/run_unittests.py b/run_unittests.py
index 115a8a7..0a53733 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -328,6 +328,47 @@ class AllPlatformTests(BasePlatformTests):
self.init(testdir)
self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False)
+ def test_static_library_overwrite(self):
+ '''
+ Tests that static libraries are never appended to, always overwritten.
+ Has to be a unit test because this involves building a project,
+ reconfiguring, and building it again so that `ar` is run twice on the
+ same static library.
+ https://github.com/mesonbuild/meson/issues/1355
+ '''
+ testdir = os.path.join(self.common_test_dir, '3 static')
+ env = Environment(testdir, self.builddir, self.meson_command,
+ get_fake_options(self.prefix), [])
+ cc = env.detect_c_compiler(False)
+ static_linker = env.detect_static_linker(cc)
+ if not isinstance(static_linker, mesonbuild.compilers.ArLinker):
+ raise unittest.SkipTest('static linker is not `ar`')
+ # Configure
+ self.init(testdir)
+ # Get name of static library
+ targets = self.introspect('--targets')
+ self.assertEqual(len(targets), 1)
+ libname = targets[0]['filename']
+ # Build and get contents of static library
+ self.build()
+ before = self._run(['ar', 't', os.path.join(self.builddir, libname)],
+ return_output=True).split()
+ # Filter out non-object-file contents
+ before = [f for f in before if f.endswith((b'.o', b'.obj'))]
+ # Static library should contain only one object
+ self.assertEqual(len(before), 1, msg=before)
+ # Change the source to be built into the static library
+ self.setconf('-Dsource=libfile2.c')
+ self.build()
+ after = self._run(['ar', 't', os.path.join(self.builddir, libname)],
+ return_output=True).split()
+ # Filter out non-object-file contents
+ after = [f for f in after if f.endswith((b'.o', b'.obj'))]
+ # Static library should contain only one object
+ self.assertEqual(len(after), 1, msg=after)
+ # and the object must have changed
+ self.assertNotEqual(before, after)
+
class LinuxlikeTests(BasePlatformTests):
'''
diff --git a/test cases/common/3 static/libfile2.c b/test cases/common/3 static/libfile2.c
new file mode 100644
index 0000000..86bbb2c
--- /dev/null
+++ b/test cases/common/3 static/libfile2.c
@@ -0,0 +1,3 @@
+int libfunc2() {
+ return 4;
+}
diff --git a/test cases/common/3 static/meson.build b/test cases/common/3 static/meson.build
index 3dee93b..e539956 100644
--- a/test cases/common/3 static/meson.build
+++ b/test cases/common/3 static/meson.build
@@ -1,3 +1,4 @@
project('static library test', 'c')
-lib = static_library('mylib', 'libfile.c',
+
+lib = static_library('mylib', get_option('source'),
link_args : '-THISMUSTNOBEUSED') # Static linker needs to ignore all link args.
diff --git a/test cases/common/3 static/meson_options.txt b/test cases/common/3 static/meson_options.txt
new file mode 100644
index 0000000..7261a19
--- /dev/null
+++ b/test cases/common/3 static/meson_options.txt
@@ -0,0 +1 @@
+option('source', type : 'combo', choices : ['libfile.c', 'libfile2.c'], value : 'libfile.c')