aboutsummaryrefslogtreecommitdiff
path: root/unittests/darwintests.py
blob: afc663a57ae151ca5529847467ed7f5acf7aaf7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# SPDX-License-Identifier: Apache-2.0
# Copyright 2016-2021 The Meson development team

import subprocess
import re
import os
import unittest

from mesonbuild.mesonlib import (
    MachineChoice, is_osx
)
from mesonbuild.compilers import (
    detect_c_compiler
)


from run_tests import (
    get_fake_env
)

from .baseplatformtests import BasePlatformTests
from .helpers import *

@unittest.skipUnless(is_osx(), "requires Darwin")
class DarwinTests(BasePlatformTests):
    '''
    Tests that should run on macOS
    '''

    def setUp(self):
        super().setUp()
        self.platform_test_dir = os.path.join(self.src_root, 'test cases/osx')

    def test_apple_bitcode(self):
        '''
        Test that -fembed-bitcode is correctly added while compiling and
        -bitcode_bundle is added while linking when b_bitcode is true and not
        when it is false.  This can't be an ordinary test case because we need
        to inspect the compiler database.
        '''
        testdir = os.path.join(self.platform_test_dir, '7 bitcode')
        env = get_fake_env(testdir, self.builddir, self.prefix)
        cc = detect_c_compiler(env, MachineChoice.HOST)
        if cc.id != 'clang':
            raise unittest.SkipTest('Not using Clang on OSX')
        # Try with bitcode enabled
        out = self.init(testdir, extra_args='-Db_bitcode=true')
        # Warning was printed
        self.assertRegex(out, 'WARNING:.*b_bitcode')
        # Compiler options were added
        for compdb in self.get_compdb():
            if 'module' in compdb['file']:
                self.assertNotIn('-fembed-bitcode', compdb['command'])
            else:
                self.assertIn('-fembed-bitcode', compdb['command'])
        build_ninja = os.path.join(self.builddir, 'build.ninja')
        # Linker options were added
        with open(build_ninja, encoding='utf-8') as f:
            contents = f.read()
            m = re.search('LINK_ARGS =.*-bitcode_bundle', contents)
        self.assertIsNotNone(m, msg=contents)
        # Try with bitcode disabled
        self.setconf('-Db_bitcode=false')
        # Regenerate build
        self.build()
        for compdb in self.get_compdb():
            self.assertNotIn('-fembed-bitcode', compdb['command'])
        build_ninja = os.path.join(self.builddir, 'build.ninja')
        with open(build_ninja, encoding='utf-8') as f:
            contents = f.read()
            m = re.search('LINK_ARGS =.*-bitcode_bundle', contents)
        self.assertIsNone(m, msg=contents)

    def test_apple_bitcode_modules(self):
        '''
        Same as above, just for shared_module()
        '''
        testdir = os.path.join(self.common_test_dir, '148 shared module resolving symbol in executable')
        # Ensure that it builds even with bitcode enabled
        self.init(testdir, extra_args='-Db_bitcode=true')
        self.build()
        self.run_tests()

    def test_apple_lto_export_dynamic(self):
        '''
        Tests that -Wl,-export_dynamic is correctly added, when export_dynamic: true is set.
        On macOS, this is relevant for LTO builds only.
        '''
        testdir = os.path.join(self.common_test_dir, '148 shared module resolving symbol in executable')
        # Ensure that it builds even with LTO enabled
        env = {'CFLAGS': '-flto'}
        self.init(testdir, override_envvars=env)
        self.build()
        self.run_tests()

    def _get_darwin_versions(self, fname):
        fname = os.path.join(self.builddir, fname)
        out = subprocess.check_output(['otool', '-L', fname], universal_newlines=True)
        m = re.match(r'.*version (.*), current version (.*)\)', out.split('\n')[1])
        self.assertIsNotNone(m, msg=out)
        return m.groups()

    def _get_darwin_rpaths(self, fname: str) -> T.List[str]:
        out = subprocess.check_output(['otool', '-l', fname], universal_newlines=True)
        pattern = re.compile(r'path (.*) \(offset \d+\)')
        rpaths = pattern.findall(out)
        return rpaths

    @skipIfNoPkgconfig
    def test_library_versioning(self):
        '''
        Ensure that compatibility_version and current_version are set correctly
        '''
        testdir = os.path.join(self.platform_test_dir, '2 library versions')
        self.init(testdir)
        self.build()
        targets = {}
        for t in self.introspect('--targets'):
            targets[t['name']] = t['filename'][0] if isinstance(t['filename'], list) else t['filename']
        self.assertEqual(self._get_darwin_versions(targets['some']), ('7.0.0', '7.0.0'))
        self.assertEqual(self._get_darwin_versions(targets['noversion']), ('0.0.0', '0.0.0'))
        self.assertEqual(self._get_darwin_versions(targets['onlyversion']), ('1.0.0', '1.0.0'))
        self.assertEqual(self._get_darwin_versions(targets['onlysoversion']), ('5.0.0', '5.0.0'))
        self.assertEqual(self._get_darwin_versions(targets['intver']), ('2.0.0', '2.0.0'))
        self.assertEqual(self._get_darwin_versions(targets['stringver']), ('2.3.0', '2.3.0'))
        self.assertEqual(self._get_darwin_versions(targets['stringlistver']), ('2.4.0', '2.4.0'))
        self.assertEqual(self._get_darwin_versions(targets['intstringver']), ('1111.0.0', '2.5.0'))
        self.assertEqual(self._get_darwin_versions(targets['stringlistvers']), ('2.6.0', '2.6.1'))

    def test_duplicate_rpath(self):
        testdir = os.path.join(self.unit_test_dir, '10 build_rpath')
        # We purposely pass a duplicate rpath to Meson, in order
        # to ascertain that Meson does not call install_name_tool
        # with duplicate -delete_rpath arguments, which would
        # lead to erroring out on installation
        env = {"LDFLAGS": "-Wl,-rpath,/foo/bar"}
        self.init(testdir, override_envvars=env)
        self.build()
        self.install()

    def test_removing_unused_linker_args(self):
        testdir = os.path.join(self.common_test_dir, '104 has arg')
        env = {'CFLAGS': '-L/tmp -L /var/tmp -headerpad_max_install_names -Wl,-export_dynamic -framework Foundation'}
        self.init(testdir, override_envvars=env)

    def test_objc_versions(self):
        # Objective-C always uses the C standard version.
        # Objective-C++ always uses the C++ standard version.
        # This is what most people seem to want and in addition
        # it is the only setup supported by Xcode.
        testdir = os.path.join(self.objc_test_dir, '1 simple')
        self.init(testdir)
        self.assertIn('-std=c99', self.get_compdb()[0]['command'])
        self.wipe()
        testdir = os.path.join(self.objcpp_test_dir, '1 simple')
        self.init(testdir)
        self.assertIn('-std=c++14', self.get_compdb()[0]['command'])

    def test_darwin_get_object_archs(self):
        from mesonbuild.mesonlib import darwin_get_object_archs
        archs = darwin_get_object_archs('/bin/cat')
        self.assertEqual(archs, ['x86_64', 'aarch64'])

    def test_darwin_meson_rpaths_removed_on_install(self):
        testdir = os.path.join(self.darwin_test_dir, '1 rpath removal on install')
        self.init(testdir)
        self.build()
        # Meson-created RPATHs are usually only valid in the build directory
        rpaths = self._get_darwin_rpaths(os.path.join(self.builddir, 'libbar.dylib'))
        self.assertListEqual(rpaths, ['@loader_path/foo'])
        self.install()
        # Those RPATHs are no longer valid and should not be present after installation
        rpaths = self._get_darwin_rpaths(os.path.join(self.installdir, 'usr/lib/libbar.dylib'))
        self.assertListEqual(rpaths, [])