1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import, print_function, unicode_literals 6 7import json 8import os 9import shutil 10import sys 11import tempfile 12import unittest 13 14from six import StringIO 15from mozfile.mozfile import NamedTemporaryFile 16 17from mozunit import main 18 19from mach.logging import LoggingManager 20 21from mozbuild.base import ( 22 BadEnvironmentException, 23 MachCommandBase, 24 MozbuildObject, 25 PathArgument, 26) 27 28from mozbuild.backend.configenvironment import ConfigEnvironment 29from buildconfig import topsrcdir, topobjdir 30import mozpack.path as mozpath 31 32from mozbuild.test.common import prepare_tmp_topsrcdir 33 34 35curdir = os.path.dirname(__file__) 36log_manager = LoggingManager() 37 38 39class TestMozbuildObject(unittest.TestCase): 40 def setUp(self): 41 self._old_cwd = os.getcwd() 42 self._old_env = dict(os.environ) 43 os.environ.pop("MOZCONFIG", None) 44 os.environ.pop("MOZ_OBJDIR", None) 45 46 def tearDown(self): 47 os.chdir(self._old_cwd) 48 os.environ.clear() 49 os.environ.update(self._old_env) 50 51 def get_base(self, topobjdir=None): 52 return MozbuildObject(topsrcdir, None, log_manager, topobjdir=topobjdir) 53 54 def test_objdir_config_guess(self): 55 base = self.get_base() 56 57 with NamedTemporaryFile(mode="wt") as mozconfig: 58 os.environ["MOZCONFIG"] = mozconfig.name 59 60 self.assertIsNotNone(base.topobjdir) 61 self.assertEqual(len(base.topobjdir.split()), 1) 62 config_guess = base.resolve_config_guess() 63 self.assertTrue(base.topobjdir.endswith(config_guess)) 64 self.assertTrue(os.path.isabs(base.topobjdir)) 65 self.assertTrue(base.topobjdir.startswith(base.topsrcdir)) 66 67 def test_objdir_trailing_slash(self): 68 """Trailing slashes in topobjdir should be removed.""" 69 base = self.get_base() 70 71 with NamedTemporaryFile(mode="wt") as mozconfig: 72 mozconfig.write("mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/foo/") 73 mozconfig.flush() 74 os.environ["MOZCONFIG"] = mozconfig.name 75 76 self.assertEqual(base.topobjdir, mozpath.join(base.topsrcdir, "foo")) 77 self.assertTrue(base.topobjdir.endswith("foo")) 78 79 def test_objdir_config_status(self): 80 """Ensure @CONFIG_GUESS@ is handled when loading mozconfig.""" 81 base = self.get_base() 82 guess = base.resolve_config_guess() 83 84 # There may be symlinks involved, so we use real paths to ensure 85 # path consistency. 86 d = os.path.realpath(tempfile.mkdtemp()) 87 try: 88 mozconfig = os.path.join(d, "mozconfig") 89 with open(mozconfig, "wt") as fh: 90 fh.write("mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/foo/@CONFIG_GUESS@") 91 print("Wrote mozconfig %s" % mozconfig) 92 93 topobjdir = os.path.join(d, "foo", guess) 94 os.makedirs(topobjdir) 95 96 # Create a fake topsrcdir. 97 prepare_tmp_topsrcdir(d) 98 99 mozinfo = os.path.join(topobjdir, "mozinfo.json") 100 with open(mozinfo, "wt") as fh: 101 json.dump( 102 dict( 103 topsrcdir=d, 104 mozconfig=mozconfig, 105 ), 106 fh, 107 ) 108 109 os.environ["MOZCONFIG"] = mozconfig 110 os.chdir(topobjdir) 111 112 obj = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) 113 114 self.assertEqual(obj.topobjdir, mozpath.normsep(topobjdir)) 115 finally: 116 os.chdir(self._old_cwd) 117 shutil.rmtree(d) 118 119 def test_relative_objdir(self): 120 """Relative defined objdirs are loaded properly.""" 121 d = os.path.realpath(tempfile.mkdtemp()) 122 try: 123 mozconfig = os.path.join(d, "mozconfig") 124 with open(mozconfig, "wt") as fh: 125 fh.write("mk_add_options MOZ_OBJDIR=./objdir") 126 127 topobjdir = mozpath.join(d, "objdir") 128 os.mkdir(topobjdir) 129 130 mozinfo = os.path.join(topobjdir, "mozinfo.json") 131 with open(mozinfo, "wt") as fh: 132 json.dump( 133 dict( 134 topsrcdir=d, 135 mozconfig=mozconfig, 136 ), 137 fh, 138 ) 139 140 os.environ["MOZCONFIG"] = mozconfig 141 child = os.path.join(topobjdir, "foo", "bar") 142 os.makedirs(child) 143 os.chdir(child) 144 145 obj = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) 146 147 self.assertEqual(obj.topobjdir, topobjdir) 148 149 finally: 150 os.chdir(self._old_cwd) 151 shutil.rmtree(d) 152 153 @unittest.skipIf( 154 not hasattr(os, "symlink") or os.name == "nt", "symlinks not available." 155 ) 156 def test_symlink_objdir(self): 157 """Objdir that is a symlink is loaded properly.""" 158 d = os.path.realpath(tempfile.mkdtemp()) 159 try: 160 topobjdir_real = os.path.join(d, "objdir") 161 topobjdir_link = os.path.join(d, "objlink") 162 163 os.mkdir(topobjdir_real) 164 os.symlink(topobjdir_real, topobjdir_link) 165 166 mozconfig = os.path.join(d, "mozconfig") 167 with open(mozconfig, "wt") as fh: 168 fh.write("mk_add_options MOZ_OBJDIR=%s" % topobjdir_link) 169 170 mozinfo = os.path.join(topobjdir_real, "mozinfo.json") 171 with open(mozinfo, "wt") as fh: 172 json.dump( 173 dict( 174 topsrcdir=d, 175 mozconfig=mozconfig, 176 ), 177 fh, 178 ) 179 180 os.chdir(topobjdir_link) 181 obj = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) 182 self.assertEqual(obj.topobjdir, topobjdir_real) 183 184 os.chdir(topobjdir_real) 185 obj = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) 186 self.assertEqual(obj.topobjdir, topobjdir_real) 187 188 finally: 189 os.chdir(self._old_cwd) 190 shutil.rmtree(d) 191 192 def test_mach_command_base_inside_objdir(self): 193 """Ensure a MachCommandBase constructed from inside the objdir works.""" 194 195 d = os.path.realpath(tempfile.mkdtemp()) 196 197 try: 198 topobjdir = os.path.join(d, "objdir") 199 os.makedirs(topobjdir) 200 201 topsrcdir = os.path.join(d, "srcdir") 202 prepare_tmp_topsrcdir(topsrcdir) 203 204 mozinfo = os.path.join(topobjdir, "mozinfo.json") 205 with open(mozinfo, "wt") as fh: 206 json.dump( 207 dict( 208 topsrcdir=topsrcdir, 209 ), 210 fh, 211 ) 212 213 os.chdir(topobjdir) 214 215 class MockMachContext(object): 216 pass 217 218 context = MockMachContext() 219 context.cwd = topobjdir 220 context.topdir = topsrcdir 221 context.settings = None 222 context.log_manager = None 223 context.detect_virtualenv_mozinfo = False 224 225 o = MachCommandBase(context, None) 226 227 self.assertEqual(o.topobjdir, mozpath.normsep(topobjdir)) 228 self.assertEqual(o.topsrcdir, mozpath.normsep(topsrcdir)) 229 230 finally: 231 os.chdir(self._old_cwd) 232 shutil.rmtree(d) 233 234 def test_objdir_is_srcdir_rejected(self): 235 """Ensure the srcdir configurations are rejected.""" 236 d = os.path.realpath(tempfile.mkdtemp()) 237 238 try: 239 # The easiest way to do this is to create a mozinfo.json with data 240 # that will never happen. 241 mozinfo = os.path.join(d, "mozinfo.json") 242 with open(mozinfo, "wt") as fh: 243 json.dump({"topsrcdir": d}, fh) 244 245 os.chdir(d) 246 247 with self.assertRaises(BadEnvironmentException): 248 MozbuildObject.from_environment(detect_virtualenv_mozinfo=False) 249 250 finally: 251 os.chdir(self._old_cwd) 252 shutil.rmtree(d) 253 254 def test_objdir_mismatch(self): 255 """Ensure MachCommandBase throwing on objdir mismatch.""" 256 d = os.path.realpath(tempfile.mkdtemp()) 257 258 try: 259 real_topobjdir = os.path.join(d, "real-objdir") 260 os.makedirs(real_topobjdir) 261 262 topobjdir = os.path.join(d, "objdir") 263 os.makedirs(topobjdir) 264 265 topsrcdir = os.path.join(d, "srcdir") 266 prepare_tmp_topsrcdir(topsrcdir) 267 268 mozconfig = os.path.join(d, "mozconfig") 269 with open(mozconfig, "wt") as fh: 270 fh.write("mk_add_options MOZ_OBJDIR=%s" % real_topobjdir) 271 272 mozinfo = os.path.join(topobjdir, "mozinfo.json") 273 with open(mozinfo, "wt") as fh: 274 json.dump( 275 dict( 276 topsrcdir=topsrcdir, 277 mozconfig=mozconfig, 278 ), 279 fh, 280 ) 281 282 os.chdir(topobjdir) 283 284 class MockMachContext(object): 285 pass 286 287 context = MockMachContext() 288 context.cwd = topobjdir 289 context.topdir = topsrcdir 290 context.settings = None 291 context.log_manager = None 292 context.detect_virtualenv_mozinfo = False 293 294 stdout = sys.stdout 295 sys.stdout = StringIO() 296 try: 297 with self.assertRaises(SystemExit): 298 MachCommandBase(context, None) 299 300 self.assertTrue( 301 sys.stdout.getvalue().startswith( 302 "Ambiguous object directory detected." 303 ) 304 ) 305 finally: 306 sys.stdout = stdout 307 308 finally: 309 os.chdir(self._old_cwd) 310 shutil.rmtree(d) 311 312 def test_config_environment(self): 313 d = os.path.realpath(tempfile.mkdtemp()) 314 315 try: 316 with open(os.path.join(d, "config.status"), "w") as fh: 317 fh.write("# coding=utf-8\n") 318 fh.write("from __future__ import unicode_literals\n") 319 fh.write("topobjdir = '%s'\n" % mozpath.normsep(d)) 320 fh.write("topsrcdir = '%s'\n" % topsrcdir) 321 fh.write("mozconfig = None\n") 322 fh.write("defines = { 'FOO': 'foo' }\n") 323 fh.write("substs = { 'QUX': 'qux' }\n") 324 fh.write( 325 "__all__ = ['topobjdir', 'topsrcdir', 'defines', " 326 "'substs', 'mozconfig']" 327 ) 328 329 base = self.get_base(topobjdir=d) 330 331 ce = base.config_environment 332 self.assertIsInstance(ce, ConfigEnvironment) 333 334 self.assertEqual(base.defines, ce.defines) 335 self.assertEqual(base.substs, ce.substs) 336 337 self.assertEqual(base.defines, {"FOO": "foo"}) 338 self.assertEqual( 339 base.substs, 340 { 341 "ACDEFINES": "-DFOO=foo", 342 "ALLEMPTYSUBSTS": "", 343 "ALLSUBSTS": "ACDEFINES = -DFOO=foo\nQUX = qux", 344 "QUX": "qux", 345 }, 346 ) 347 finally: 348 shutil.rmtree(d) 349 350 def test_get_binary_path(self): 351 base = self.get_base(topobjdir=topobjdir) 352 353 platform = sys.platform 354 355 # We should ideally use the config.status from the build. Let's install 356 # a fake one. 357 substs = [ 358 ("MOZ_APP_NAME", "awesomeapp"), 359 ("MOZ_BUILD_APP", "awesomeapp"), 360 ] 361 if sys.platform.startswith("darwin"): 362 substs.append(("OS_ARCH", "Darwin")) 363 substs.append(("BIN_SUFFIX", "")) 364 substs.append(("MOZ_MACBUNDLE_NAME", "Nightly.app")) 365 elif sys.platform.startswith(("win32", "cygwin")): 366 substs.append(("OS_ARCH", "WINNT")) 367 substs.append(("BIN_SUFFIX", ".exe")) 368 else: 369 substs.append(("OS_ARCH", "something")) 370 substs.append(("BIN_SUFFIX", "")) 371 372 base._config_environment = ConfigEnvironment( 373 base.topsrcdir, base.topobjdir, substs=substs 374 ) 375 376 p = base.get_binary_path("xpcshell", False) 377 if platform.startswith("darwin"): 378 self.assertTrue(p.endswith("Contents/MacOS/xpcshell")) 379 elif platform.startswith(("win32", "cygwin")): 380 self.assertTrue(p.endswith("xpcshell.exe")) 381 else: 382 self.assertTrue(p.endswith("dist/bin/xpcshell")) 383 384 p = base.get_binary_path(validate_exists=False) 385 if platform.startswith("darwin"): 386 self.assertTrue(p.endswith("Contents/MacOS/awesomeapp")) 387 elif platform.startswith(("win32", "cygwin")): 388 self.assertTrue(p.endswith("awesomeapp.exe")) 389 else: 390 self.assertTrue(p.endswith("dist/bin/awesomeapp")) 391 392 p = base.get_binary_path(validate_exists=False, where="staged-package") 393 if platform.startswith("darwin"): 394 self.assertTrue( 395 p.endswith("awesomeapp/Nightly.app/Contents/MacOS/awesomeapp") 396 ) 397 elif platform.startswith(("win32", "cygwin")): 398 self.assertTrue(p.endswith("awesomeapp\\awesomeapp.exe")) 399 else: 400 self.assertTrue(p.endswith("awesomeapp/awesomeapp")) 401 402 self.assertRaises(Exception, base.get_binary_path, where="somewhere") 403 404 p = base.get_binary_path("foobar", validate_exists=False) 405 if platform.startswith("win32"): 406 self.assertTrue(p.endswith("foobar.exe")) 407 else: 408 self.assertTrue(p.endswith("foobar")) 409 410 411class TestPathArgument(unittest.TestCase): 412 def test_path_argument(self): 413 # Absolute path 414 p = PathArgument("/obj/foo", "/src", "/obj", "/src") 415 self.assertEqual(p.relpath(), "foo") 416 self.assertEqual(p.srcdir_path(), "/src/foo") 417 self.assertEqual(p.objdir_path(), "/obj/foo") 418 419 # Relative path within srcdir 420 p = PathArgument("foo", "/src", "/obj", "/src") 421 self.assertEqual(p.relpath(), "foo") 422 self.assertEqual(p.srcdir_path(), "/src/foo") 423 self.assertEqual(p.objdir_path(), "/obj/foo") 424 425 # Relative path within subdirectory 426 p = PathArgument("bar", "/src", "/obj", "/src/foo") 427 self.assertEqual(p.relpath(), "foo/bar") 428 self.assertEqual(p.srcdir_path(), "/src/foo/bar") 429 self.assertEqual(p.objdir_path(), "/obj/foo/bar") 430 431 # Relative path within objdir 432 p = PathArgument("foo", "/src", "/obj", "/obj") 433 self.assertEqual(p.relpath(), "foo") 434 self.assertEqual(p.srcdir_path(), "/src/foo") 435 self.assertEqual(p.objdir_path(), "/obj/foo") 436 437 # "." path 438 p = PathArgument(".", "/src", "/obj", "/src/foo") 439 self.assertEqual(p.relpath(), "foo") 440 self.assertEqual(p.srcdir_path(), "/src/foo") 441 self.assertEqual(p.objdir_path(), "/obj/foo") 442 443 # Nested src/obj directories 444 p = PathArgument("bar", "/src", "/src/obj", "/src/obj/foo") 445 self.assertEqual(p.relpath(), "foo/bar") 446 self.assertEqual(p.srcdir_path(), "/src/foo/bar") 447 self.assertEqual(p.objdir_path(), "/src/obj/foo/bar") 448 449 450if __name__ == "__main__": 451 main() 452