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 7from six import StringIO 8import os 9import six 10import sys 11import textwrap 12import unittest 13 14from mozunit import ( 15 main, 16 MockedOpen, 17) 18 19from mozbuild.configure.options import ( 20 InvalidOptionError, 21 NegativeOptionValue, 22 PositiveOptionValue, 23) 24from mozbuild.configure import ( 25 ConfigureError, 26 ConfigureSandbox, 27) 28from mozbuild.util import exec_, memoized_property, ReadOnlyNamespace 29 30import mozpack.path as mozpath 31 32test_data_path = mozpath.abspath(mozpath.dirname(__file__)) 33test_data_path = mozpath.join(test_data_path, "data") 34 35 36class TestConfigure(unittest.TestCase): 37 def get_config( 38 self, options=[], env={}, configure="moz.configure", prog="/bin/configure" 39 ): 40 config = {} 41 out = StringIO() 42 sandbox = ConfigureSandbox(config, env, [prog] + options, out, out) 43 44 sandbox.run(mozpath.join(test_data_path, configure)) 45 46 if "--help" in options: 47 return six.ensure_text(out.getvalue()), config 48 self.assertEquals("", out.getvalue()) 49 return config 50 51 def moz_configure(self, source): 52 return MockedOpen( 53 {os.path.join(test_data_path, "moz.configure"): textwrap.dedent(source)} 54 ) 55 56 def test_defaults(self): 57 config = self.get_config() 58 self.maxDiff = None 59 self.assertEquals( 60 { 61 "CHOICES": NegativeOptionValue(), 62 "DEFAULTED": PositiveOptionValue(("not-simple",)), 63 "IS_GCC": NegativeOptionValue(), 64 "REMAINDER": ( 65 PositiveOptionValue(), 66 NegativeOptionValue(), 67 NegativeOptionValue(), 68 NegativeOptionValue(), 69 ), 70 "SIMPLE": NegativeOptionValue(), 71 "VALUES": NegativeOptionValue(), 72 "VALUES2": NegativeOptionValue(), 73 "VALUES3": NegativeOptionValue(), 74 "WITH_ENV": NegativeOptionValue(), 75 }, 76 config, 77 ) 78 79 def test_help(self): 80 help, config = self.get_config(["--help"], prog="configure") 81 82 self.assertEquals({}, config) 83 self.maxDiff = None 84 self.assertEquals( 85 "Usage: configure [options]\n" 86 "\n" 87 "Options: [defaults in brackets after descriptions]\n" 88 " Help options:\n" 89 " --help print this message\n" 90 "\n" 91 " Options from python/mozbuild/mozbuild/test/configure/data/included.configure:\n" 92 " --enable-imports-in-template\n Imports in template\n" 93 "\n" 94 " Options from python/mozbuild/mozbuild/test/configure/data/moz.configure:\n" 95 " --enable-include Include\n" 96 " --enable-simple Enable simple\n" 97 " --enable-values Enable values\n" 98 " --enable-with-env Enable with env\n" 99 " --indirect-option Indirectly defined option\n" 100 " --option Option\n" 101 " --returned-choices Choices\n" 102 " --with-imports Imports\n" 103 " --with-returned-default Returned default [not-simple]\n" 104 " --with-stuff Build with stuff\n" 105 " --without-thing Build without thing\n" 106 "\n" 107 "\n" 108 "Environment variables:\n" 109 " Options from python/mozbuild/mozbuild/test/configure/data/moz.configure:\n" 110 " CC C Compiler\n" 111 "\n", 112 help.replace("\\", "/"), 113 ) 114 115 def test_unknown(self): 116 with self.assertRaises(InvalidOptionError): 117 self.get_config(["--unknown"]) 118 119 def test_simple(self): 120 for config in ( 121 self.get_config(), 122 self.get_config(["--disable-simple"]), 123 # Last option wins. 124 self.get_config(["--enable-simple", "--disable-simple"]), 125 ): 126 self.assertNotIn("ENABLED_SIMPLE", config) 127 self.assertIn("SIMPLE", config) 128 self.assertEquals(NegativeOptionValue(), config["SIMPLE"]) 129 130 for config in ( 131 self.get_config(["--enable-simple"]), 132 self.get_config(["--disable-simple", "--enable-simple"]), 133 ): 134 self.assertIn("ENABLED_SIMPLE", config) 135 self.assertIn("SIMPLE", config) 136 self.assertEquals(PositiveOptionValue(), config["SIMPLE"]) 137 self.assertIs(config["SIMPLE"], config["ENABLED_SIMPLE"]) 138 139 # --enable-simple doesn't take values. 140 with self.assertRaises(InvalidOptionError): 141 self.get_config(["--enable-simple=value"]) 142 143 def test_with_env(self): 144 for config in ( 145 self.get_config(), 146 self.get_config(["--disable-with-env"]), 147 self.get_config(["--enable-with-env", "--disable-with-env"]), 148 self.get_config(env={"MOZ_WITH_ENV": ""}), 149 # Options win over environment 150 self.get_config(["--disable-with-env"], env={"MOZ_WITH_ENV": "1"}), 151 ): 152 self.assertIn("WITH_ENV", config) 153 self.assertEquals(NegativeOptionValue(), config["WITH_ENV"]) 154 155 for config in ( 156 self.get_config(["--enable-with-env"]), 157 self.get_config(["--disable-with-env", "--enable-with-env"]), 158 self.get_config(env={"MOZ_WITH_ENV": "1"}), 159 self.get_config(["--enable-with-env"], env={"MOZ_WITH_ENV": ""}), 160 ): 161 self.assertIn("WITH_ENV", config) 162 self.assertEquals(PositiveOptionValue(), config["WITH_ENV"]) 163 164 with self.assertRaises(InvalidOptionError): 165 self.get_config(["--enable-with-env=value"]) 166 167 with self.assertRaises(InvalidOptionError): 168 self.get_config(env={"MOZ_WITH_ENV": "value"}) 169 170 def test_values(self, name="VALUES"): 171 for config in ( 172 self.get_config(), 173 self.get_config(["--disable-values"]), 174 self.get_config(["--enable-values", "--disable-values"]), 175 ): 176 self.assertIn(name, config) 177 self.assertEquals(NegativeOptionValue(), config[name]) 178 179 for config in ( 180 self.get_config(["--enable-values"]), 181 self.get_config(["--disable-values", "--enable-values"]), 182 ): 183 self.assertIn(name, config) 184 self.assertEquals(PositiveOptionValue(), config[name]) 185 186 config = self.get_config(["--enable-values=foo"]) 187 self.assertIn(name, config) 188 self.assertEquals(PositiveOptionValue(("foo",)), config[name]) 189 190 config = self.get_config(["--enable-values=foo,bar"]) 191 self.assertIn(name, config) 192 self.assertTrue(config[name]) 193 self.assertEquals(PositiveOptionValue(("foo", "bar")), config[name]) 194 195 def test_values2(self): 196 self.test_values("VALUES2") 197 198 def test_values3(self): 199 self.test_values("VALUES3") 200 201 def test_returned_default(self): 202 config = self.get_config(["--enable-simple"]) 203 self.assertIn("DEFAULTED", config) 204 self.assertEquals(PositiveOptionValue(("simple",)), config["DEFAULTED"]) 205 206 config = self.get_config(["--disable-simple"]) 207 self.assertIn("DEFAULTED", config) 208 self.assertEquals(PositiveOptionValue(("not-simple",)), config["DEFAULTED"]) 209 210 def test_returned_choices(self): 211 for val in ("a", "b", "c"): 212 config = self.get_config( 213 ["--enable-values=alpha", "--returned-choices=%s" % val] 214 ) 215 self.assertIn("CHOICES", config) 216 self.assertEquals(PositiveOptionValue((val,)), config["CHOICES"]) 217 218 for val in ("0", "1", "2"): 219 config = self.get_config( 220 ["--enable-values=numeric", "--returned-choices=%s" % val] 221 ) 222 self.assertIn("CHOICES", config) 223 self.assertEquals(PositiveOptionValue((val,)), config["CHOICES"]) 224 225 with self.assertRaises(InvalidOptionError): 226 self.get_config(["--enable-values=numeric", "--returned-choices=a"]) 227 228 with self.assertRaises(InvalidOptionError): 229 self.get_config(["--enable-values=alpha", "--returned-choices=0"]) 230 231 def test_included(self): 232 config = self.get_config(env={"CC": "gcc"}) 233 self.assertIn("IS_GCC", config) 234 self.assertEquals(config["IS_GCC"], True) 235 236 config = self.get_config(["--enable-include=extra.configure", "--extra"]) 237 self.assertIn("EXTRA", config) 238 self.assertEquals(PositiveOptionValue(), config["EXTRA"]) 239 240 with self.assertRaises(InvalidOptionError): 241 self.get_config(["--extra"]) 242 243 def test_template(self): 244 config = self.get_config(env={"CC": "gcc"}) 245 self.assertIn("CFLAGS", config) 246 self.assertEquals(config["CFLAGS"], ["-Werror=foobar"]) 247 248 config = self.get_config(env={"CC": "clang"}) 249 self.assertNotIn("CFLAGS", config) 250 251 def test_imports(self): 252 config = {} 253 out = StringIO() 254 sandbox = ConfigureSandbox(config, {}, ["configure"], out, out) 255 256 with self.assertRaises(ImportError): 257 exec_( 258 textwrap.dedent( 259 """ 260 @template 261 def foo(): 262 import sys 263 foo()""" 264 ), 265 sandbox, 266 ) 267 268 exec_( 269 textwrap.dedent( 270 """ 271 @template 272 @imports('sys') 273 def foo(): 274 return sys""" 275 ), 276 sandbox, 277 ) 278 279 self.assertIs(sandbox["foo"](), sys) 280 281 exec_( 282 textwrap.dedent( 283 """ 284 @template 285 @imports(_from='os', _import='path') 286 def foo(): 287 return path""" 288 ), 289 sandbox, 290 ) 291 292 self.assertIs(sandbox["foo"](), os.path) 293 294 exec_( 295 textwrap.dedent( 296 """ 297 @template 298 @imports(_from='os', _import='path', _as='os_path') 299 def foo(): 300 return os_path""" 301 ), 302 sandbox, 303 ) 304 305 self.assertIs(sandbox["foo"](), os.path) 306 307 exec_( 308 textwrap.dedent( 309 """ 310 @template 311 @imports('__builtin__') 312 def foo(): 313 return __builtin__""" 314 ), 315 sandbox, 316 ) 317 318 self.assertIs(sandbox["foo"](), six.moves.builtins) 319 320 exec_( 321 textwrap.dedent( 322 """ 323 @template 324 @imports(_from='__builtin__', _import='open') 325 def foo(): 326 return open('%s')""" 327 % os.devnull 328 ), 329 sandbox, 330 ) 331 332 f = sandbox["foo"]() 333 self.assertEquals(f.name, os.devnull) 334 f.close() 335 336 # This unlocks the sandbox 337 exec_( 338 textwrap.dedent( 339 """ 340 @template 341 @imports(_import='__builtin__', _as='__builtins__') 342 def foo(): 343 import sys 344 return sys""" 345 ), 346 sandbox, 347 ) 348 349 self.assertIs(sandbox["foo"](), sys) 350 351 exec_( 352 textwrap.dedent( 353 """ 354 @template 355 @imports('__sandbox__') 356 def foo(): 357 return __sandbox__""" 358 ), 359 sandbox, 360 ) 361 362 self.assertIs(sandbox["foo"](), sandbox) 363 364 exec_( 365 textwrap.dedent( 366 """ 367 @template 368 @imports(_import='__sandbox__', _as='s') 369 def foo(): 370 return s""" 371 ), 372 sandbox, 373 ) 374 375 self.assertIs(sandbox["foo"](), sandbox) 376 377 # Nothing leaked from the function being executed 378 self.assertEquals(list(sandbox), ["__builtins__", "foo"]) 379 self.assertEquals(sandbox["__builtins__"], ConfigureSandbox.BUILTINS) 380 381 exec_( 382 textwrap.dedent( 383 """ 384 @template 385 @imports('sys') 386 def foo(): 387 @depends(when=True) 388 def bar(): 389 return sys 390 return bar 391 bar = foo()""" 392 ), 393 sandbox, 394 ) 395 396 with self.assertRaises(NameError) as e: 397 sandbox._depends[sandbox["bar"]].result() 398 399 self.assertIn("name 'sys' is not defined", str(e.exception)) 400 401 def test_apply_imports(self): 402 imports = [] 403 404 class CountApplyImportsSandbox(ConfigureSandbox): 405 def _apply_imports(self, *args, **kwargs): 406 imports.append((args, kwargs)) 407 super(CountApplyImportsSandbox, self)._apply_imports(*args, **kwargs) 408 409 config = {} 410 out = StringIO() 411 sandbox = CountApplyImportsSandbox(config, {}, ["configure"], out, out) 412 413 exec_( 414 textwrap.dedent( 415 """ 416 @template 417 @imports('sys') 418 def foo(): 419 return sys 420 foo() 421 foo()""" 422 ), 423 sandbox, 424 ) 425 426 self.assertEquals(len(imports), 1) 427 428 def test_import_wrapping(self): 429 bar = object() 430 foo = ReadOnlyNamespace(bar=bar) 431 432 class BasicWrappingSandbox(ConfigureSandbox): 433 @memoized_property 434 def _wrapped_foo(self): 435 return foo 436 437 config = {} 438 out = StringIO() 439 sandbox = BasicWrappingSandbox(config, {}, ["configure"], out, out) 440 441 exec_( 442 textwrap.dedent( 443 """ 444 @template 445 @imports('foo') 446 def toplevel(): 447 return foo 448 @template 449 @imports('foo.bar') 450 def bar(): 451 return foo.bar 452 @template 453 @imports('foo.bar') 454 def bar_upper(): 455 return foo 456 @template 457 @imports(_from='foo', _import='bar') 458 def from_import(): 459 return bar 460 @template 461 @imports(_from='foo', _import='bar', _as='custom_name') 462 def from_import_as(): 463 return custom_name 464 @template 465 @imports(_import='foo', _as='custom_name') 466 def import_as(): 467 return custom_name 468 """ 469 ), 470 sandbox, 471 ) 472 self.assertIs(sandbox["toplevel"](), foo) 473 self.assertIs(sandbox["bar"](), bar) 474 self.assertIs(sandbox["bar_upper"](), foo) 475 self.assertIs(sandbox["from_import"](), bar) 476 self.assertIs(sandbox["from_import_as"](), bar) 477 self.assertIs(sandbox["import_as"](), foo) 478 479 def test_os_path(self): 480 config = self.get_config(["--with-imports=%s" % __file__]) 481 self.assertIn("HAS_ABSPATH", config) 482 self.assertEquals(config["HAS_ABSPATH"], True) 483 self.assertIn("HAS_GETATIME", config) 484 self.assertEquals(config["HAS_GETATIME"], True) 485 self.assertIn("HAS_GETATIME2", config) 486 self.assertEquals(config["HAS_GETATIME2"], False) 487 488 def test_template_call(self): 489 config = self.get_config(env={"CC": "gcc"}) 490 self.assertIn("TEMPLATE_VALUE", config) 491 self.assertEquals(config["TEMPLATE_VALUE"], 42) 492 self.assertIn("TEMPLATE_VALUE_2", config) 493 self.assertEquals(config["TEMPLATE_VALUE_2"], 21) 494 495 def test_template_imports(self): 496 config = self.get_config(["--enable-imports-in-template"]) 497 self.assertIn("PLATFORM", config) 498 self.assertEquals(config["PLATFORM"], sys.platform) 499 500 def test_decorators(self): 501 config = {} 502 out = StringIO() 503 sandbox = ConfigureSandbox(config, {}, ["configure"], out, out) 504 505 sandbox.include_file(mozpath.join(test_data_path, "decorators.configure")) 506 507 self.assertNotIn("FOO", sandbox) 508 self.assertNotIn("BAR", sandbox) 509 self.assertNotIn("QUX", sandbox) 510 511 def test_set_config(self): 512 def get_config(*args): 513 return self.get_config(*args, configure="set_config.configure") 514 515 help, config = get_config(["--help"]) 516 self.assertEquals(config, {}) 517 518 config = get_config(["--set-foo"]) 519 self.assertIn("FOO", config) 520 self.assertEquals(config["FOO"], True) 521 522 config = get_config(["--set-bar"]) 523 self.assertNotIn("FOO", config) 524 self.assertIn("BAR", config) 525 self.assertEquals(config["BAR"], True) 526 527 config = get_config(["--set-value=qux"]) 528 self.assertIn("VALUE", config) 529 self.assertEquals(config["VALUE"], "qux") 530 531 config = get_config(["--set-name=hoge"]) 532 self.assertIn("hoge", config) 533 self.assertEquals(config["hoge"], True) 534 535 config = get_config([]) 536 self.assertEquals(config, {"BAR": False}) 537 538 with self.assertRaises(ConfigureError): 539 # Both --set-foo and --set-name=FOO are going to try to 540 # set_config('FOO'...) 541 get_config(["--set-foo", "--set-name=FOO"]) 542 543 def test_set_config_when(self): 544 with self.moz_configure( 545 """ 546 option('--with-qux', help='qux') 547 set_config('FOO', 'foo', when=True) 548 set_config('BAR', 'bar', when=False) 549 set_config('QUX', 'qux', when='--with-qux') 550 """ 551 ): 552 config = self.get_config() 553 self.assertEquals( 554 config, 555 { 556 "FOO": "foo", 557 }, 558 ) 559 config = self.get_config(["--with-qux"]) 560 self.assertEquals( 561 config, 562 { 563 "FOO": "foo", 564 "QUX": "qux", 565 }, 566 ) 567 568 def test_set_define(self): 569 def get_config(*args): 570 return self.get_config(*args, configure="set_define.configure") 571 572 help, config = get_config(["--help"]) 573 self.assertEquals(config, {"DEFINES": {}}) 574 575 config = get_config(["--set-foo"]) 576 self.assertIn("FOO", config["DEFINES"]) 577 self.assertEquals(config["DEFINES"]["FOO"], True) 578 579 config = get_config(["--set-bar"]) 580 self.assertNotIn("FOO", config["DEFINES"]) 581 self.assertIn("BAR", config["DEFINES"]) 582 self.assertEquals(config["DEFINES"]["BAR"], True) 583 584 config = get_config(["--set-value=qux"]) 585 self.assertIn("VALUE", config["DEFINES"]) 586 self.assertEquals(config["DEFINES"]["VALUE"], "qux") 587 588 config = get_config(["--set-name=hoge"]) 589 self.assertIn("hoge", config["DEFINES"]) 590 self.assertEquals(config["DEFINES"]["hoge"], True) 591 592 config = get_config([]) 593 self.assertEquals(config["DEFINES"], {"BAR": False}) 594 595 with self.assertRaises(ConfigureError): 596 # Both --set-foo and --set-name=FOO are going to try to 597 # set_define('FOO'...) 598 get_config(["--set-foo", "--set-name=FOO"]) 599 600 def test_set_define_when(self): 601 with self.moz_configure( 602 """ 603 option('--with-qux', help='qux') 604 set_define('FOO', 'foo', when=True) 605 set_define('BAR', 'bar', when=False) 606 set_define('QUX', 'qux', when='--with-qux') 607 """ 608 ): 609 config = self.get_config() 610 self.assertEquals( 611 config["DEFINES"], 612 { 613 "FOO": "foo", 614 }, 615 ) 616 config = self.get_config(["--with-qux"]) 617 self.assertEquals( 618 config["DEFINES"], 619 { 620 "FOO": "foo", 621 "QUX": "qux", 622 }, 623 ) 624 625 def test_imply_option_simple(self): 626 def get_config(*args): 627 return self.get_config(*args, configure="imply_option/simple.configure") 628 629 help, config = get_config(["--help"]) 630 self.assertEquals(config, {}) 631 632 config = get_config([]) 633 self.assertEquals(config, {}) 634 635 config = get_config(["--enable-foo"]) 636 self.assertIn("BAR", config) 637 self.assertEquals(config["BAR"], PositiveOptionValue()) 638 639 with self.assertRaises(InvalidOptionError) as e: 640 get_config(["--enable-foo", "--disable-bar"]) 641 642 self.assertEquals( 643 str(e.exception), 644 "'--enable-bar' implied by '--enable-foo' conflicts with " 645 "'--disable-bar' from the command-line", 646 ) 647 648 def test_imply_option_negative(self): 649 def get_config(*args): 650 return self.get_config(*args, configure="imply_option/negative.configure") 651 652 help, config = get_config(["--help"]) 653 self.assertEquals(config, {}) 654 655 config = get_config([]) 656 self.assertEquals(config, {}) 657 658 config = get_config(["--enable-foo"]) 659 self.assertIn("BAR", config) 660 self.assertEquals(config["BAR"], NegativeOptionValue()) 661 662 with self.assertRaises(InvalidOptionError) as e: 663 get_config(["--enable-foo", "--enable-bar"]) 664 665 self.assertEquals( 666 str(e.exception), 667 "'--disable-bar' implied by '--enable-foo' conflicts with " 668 "'--enable-bar' from the command-line", 669 ) 670 671 config = get_config(["--disable-hoge"]) 672 self.assertIn("BAR", config) 673 self.assertEquals(config["BAR"], NegativeOptionValue()) 674 675 with self.assertRaises(InvalidOptionError) as e: 676 get_config(["--disable-hoge", "--enable-bar"]) 677 678 self.assertEquals( 679 str(e.exception), 680 "'--disable-bar' implied by '--disable-hoge' conflicts with " 681 "'--enable-bar' from the command-line", 682 ) 683 684 def test_imply_option_values(self): 685 def get_config(*args): 686 return self.get_config(*args, configure="imply_option/values.configure") 687 688 help, config = get_config(["--help"]) 689 self.assertEquals(config, {}) 690 691 config = get_config([]) 692 self.assertEquals(config, {}) 693 694 config = get_config(["--enable-foo=a"]) 695 self.assertIn("BAR", config) 696 self.assertEquals(config["BAR"], PositiveOptionValue(("a",))) 697 698 config = get_config(["--enable-foo=a,b"]) 699 self.assertIn("BAR", config) 700 self.assertEquals(config["BAR"], PositiveOptionValue(("a", "b"))) 701 702 with self.assertRaises(InvalidOptionError) as e: 703 get_config(["--enable-foo=a,b", "--disable-bar"]) 704 705 self.assertEquals( 706 str(e.exception), 707 "'--enable-bar=a,b' implied by '--enable-foo' conflicts with " 708 "'--disable-bar' from the command-line", 709 ) 710 711 def test_imply_option_infer(self): 712 def get_config(*args): 713 return self.get_config(*args, configure="imply_option/infer.configure") 714 715 help, config = get_config(["--help"]) 716 self.assertEquals(config, {}) 717 718 config = get_config([]) 719 self.assertEquals(config, {}) 720 721 with self.assertRaises(InvalidOptionError) as e: 722 get_config(["--enable-foo", "--disable-bar"]) 723 724 self.assertEquals( 725 str(e.exception), 726 "'--enable-bar' implied by '--enable-foo' conflicts with " 727 "'--disable-bar' from the command-line", 728 ) 729 730 with self.assertRaises(ConfigureError) as e: 731 self.get_config([], configure="imply_option/infer_ko.configure") 732 733 self.assertEquals( 734 str(e.exception), 735 "Cannot infer what implies '--enable-bar'. Please add a `reason` " 736 "to the `imply_option` call.", 737 ) 738 739 def test_imply_option_immediate_value(self): 740 def get_config(*args): 741 return self.get_config(*args, configure="imply_option/imm.configure") 742 743 help, config = get_config(["--help"]) 744 self.assertEquals(config, {}) 745 746 config = get_config([]) 747 self.assertEquals(config, {}) 748 749 config_path = mozpath.abspath( 750 mozpath.join(test_data_path, "imply_option", "imm.configure") 751 ) 752 753 with self.assertRaisesRegexp( 754 InvalidOptionError, 755 "--enable-foo' implied by 'imply_option at %s:7' conflicts " 756 "with '--disable-foo' from the command-line" % config_path, 757 ): 758 get_config(["--disable-foo"]) 759 760 with self.assertRaisesRegexp( 761 InvalidOptionError, 762 "--enable-bar=foo,bar' implied by 'imply_option at %s:18' " 763 "conflicts with '--enable-bar=a,b,c' from the command-line" % config_path, 764 ): 765 get_config(["--enable-bar=a,b,c"]) 766 767 with self.assertRaisesRegexp( 768 InvalidOptionError, 769 "--enable-baz=BAZ' implied by 'imply_option at %s:29' " 770 "conflicts with '--enable-baz=QUUX' from the command-line" % config_path, 771 ): 772 get_config(["--enable-baz=QUUX"]) 773 774 def test_imply_option_failures(self): 775 with self.assertRaises(ConfigureError) as e: 776 with self.moz_configure( 777 """ 778 imply_option('--with-foo', ('a',), 'bar') 779 """ 780 ): 781 self.get_config() 782 783 self.assertEquals( 784 str(e.exception), 785 "`--with-foo`, emitted from `%s` line 2, is unknown." 786 % mozpath.join(test_data_path, "moz.configure"), 787 ) 788 789 with self.assertRaises(TypeError) as e: 790 with self.moz_configure( 791 """ 792 imply_option('--with-foo', 42, 'bar') 793 794 option('--with-foo', help='foo') 795 @depends('--with-foo') 796 def foo(value): 797 return value 798 """ 799 ): 800 self.get_config() 801 802 self.assertEquals(str(e.exception), "Unexpected type: 'int'") 803 804 def test_imply_option_when(self): 805 with self.moz_configure( 806 """ 807 option('--with-foo', help='foo') 808 imply_option('--with-qux', True, when='--with-foo') 809 option('--with-qux', help='qux') 810 set_config('QUX', depends('--with-qux')(lambda x: x)) 811 """ 812 ): 813 config = self.get_config() 814 self.assertEquals( 815 config, 816 { 817 "QUX": NegativeOptionValue(), 818 }, 819 ) 820 821 config = self.get_config(["--with-foo"]) 822 self.assertEquals( 823 config, 824 { 825 "QUX": PositiveOptionValue(), 826 }, 827 ) 828 829 def test_imply_option_dependency_loop(self): 830 with self.moz_configure( 831 """ 832 option('--without-foo', help='foo') 833 834 @depends('--with-foo') 835 def qux_default(foo): 836 return bool(foo) 837 838 option('--with-qux', default=qux_default, help='qux') 839 840 imply_option('--with-foo', depends('--with-qux')(lambda x: x or None)) 841 842 set_config('FOO', depends('--with-foo')(lambda x: x)) 843 set_config('QUX', depends('--with-qux')(lambda x: x)) 844 """ 845 ): 846 config = self.get_config() 847 self.assertEquals( 848 config, 849 { 850 "FOO": PositiveOptionValue(), 851 "QUX": PositiveOptionValue(), 852 }, 853 ) 854 855 config = self.get_config(["--without-foo"]) 856 self.assertEquals( 857 config, 858 { 859 "FOO": NegativeOptionValue(), 860 "QUX": NegativeOptionValue(), 861 }, 862 ) 863 864 config = self.get_config(["--with-qux"]) 865 self.assertEquals( 866 config, 867 { 868 "FOO": PositiveOptionValue(), 869 "QUX": PositiveOptionValue(), 870 }, 871 ) 872 873 with self.assertRaises(InvalidOptionError) as e: 874 config = self.get_config(["--without-foo", "--with-qux"]) 875 876 self.assertEquals( 877 str(e.exception), 878 "'--with-foo' implied by '--with-qux' conflicts " 879 "with '--without-foo' from the command-line", 880 ) 881 882 config = self.get_config(["--without-qux"]) 883 self.assertEquals( 884 config, 885 { 886 "FOO": PositiveOptionValue(), 887 "QUX": NegativeOptionValue(), 888 }, 889 ) 890 891 with self.moz_configure( 892 """ 893 option('--with-foo', help='foo') 894 895 @depends('--with-foo') 896 def qux_default(foo): 897 return bool(foo) 898 899 option('--with-qux', default=qux_default, help='qux') 900 901 imply_option('--with-foo', depends('--with-qux')(lambda x: x or None)) 902 903 set_config('FOO', depends('--with-foo')(lambda x: x)) 904 set_config('QUX', depends('--with-qux')(lambda x: x)) 905 """ 906 ): 907 config = self.get_config() 908 self.assertEquals( 909 config, 910 { 911 "FOO": NegativeOptionValue(), 912 "QUX": NegativeOptionValue(), 913 }, 914 ) 915 916 config = self.get_config(["--with-foo"]) 917 self.assertEquals( 918 config, 919 { 920 "FOO": PositiveOptionValue(), 921 "QUX": PositiveOptionValue(), 922 }, 923 ) 924 925 with self.assertRaises(InvalidOptionError) as e: 926 config = self.get_config(["--with-qux"]) 927 928 self.assertEquals( 929 str(e.exception), 930 "'--with-foo' implied by '--with-qux' conflicts " 931 "with '--without-foo' from the default", 932 ) 933 934 with self.assertRaises(InvalidOptionError) as e: 935 config = self.get_config(["--without-foo", "--with-qux"]) 936 937 self.assertEquals( 938 str(e.exception), 939 "'--with-foo' implied by '--with-qux' conflicts " 940 "with '--without-foo' from the command-line", 941 ) 942 943 config = self.get_config(["--without-qux"]) 944 self.assertEquals( 945 config, 946 { 947 "FOO": NegativeOptionValue(), 948 "QUX": NegativeOptionValue(), 949 }, 950 ) 951 952 config_path = mozpath.abspath(mozpath.join(test_data_path, "moz.configure")) 953 954 # Same test as above, but using `when` in the `imply_option`. 955 with self.moz_configure( 956 """ 957 option('--with-foo', help='foo') 958 959 @depends('--with-foo') 960 def qux_default(foo): 961 return bool(foo) 962 963 option('--with-qux', default=qux_default, help='qux') 964 965 imply_option('--with-foo', True, when='--with-qux') 966 967 set_config('FOO', depends('--with-foo')(lambda x: x)) 968 set_config('QUX', depends('--with-qux')(lambda x: x)) 969 """ 970 ): 971 config = self.get_config() 972 self.assertEquals( 973 config, 974 { 975 "FOO": NegativeOptionValue(), 976 "QUX": NegativeOptionValue(), 977 }, 978 ) 979 980 config = self.get_config(["--with-foo"]) 981 self.assertEquals( 982 config, 983 { 984 "FOO": PositiveOptionValue(), 985 "QUX": PositiveOptionValue(), 986 }, 987 ) 988 989 with self.assertRaises(InvalidOptionError) as e: 990 config = self.get_config(["--with-qux"]) 991 992 self.assertEquals( 993 str(e.exception), 994 "'--with-foo' implied by 'imply_option at %s:10' conflicts " 995 "with '--without-foo' from the default" % config_path, 996 ) 997 998 with self.assertRaises(InvalidOptionError) as e: 999 config = self.get_config(["--without-foo", "--with-qux"]) 1000 1001 self.assertEquals( 1002 str(e.exception), 1003 "'--with-foo' implied by 'imply_option at %s:10' conflicts " 1004 "with '--without-foo' from the command-line" % config_path, 1005 ) 1006 1007 config = self.get_config(["--without-qux"]) 1008 self.assertEquals( 1009 config, 1010 { 1011 "FOO": NegativeOptionValue(), 1012 "QUX": NegativeOptionValue(), 1013 }, 1014 ) 1015 1016 def test_imply_option_recursion(self): 1017 config_path = mozpath.abspath(mozpath.join(test_data_path, "moz.configure")) 1018 1019 message = ( 1020 "'--without-foo' appears somewhere in the direct or indirect dependencies " 1021 "when resolving imply_option at %s:8" % config_path 1022 ) 1023 1024 with self.moz_configure( 1025 """ 1026 option('--without-foo', help='foo') 1027 1028 imply_option('--with-qux', depends('--with-foo')(lambda x: x or None)) 1029 1030 option('--with-qux', help='qux') 1031 1032 imply_option('--with-foo', depends('--with-qux')(lambda x: x or None)) 1033 1034 set_config('FOO', depends('--with-foo')(lambda x: x)) 1035 set_config('QUX', depends('--with-qux')(lambda x: x)) 1036 """ 1037 ): 1038 # Note: no error is detected when the depends function in the 1039 # imply_options resolve to None, which disables the imply_option. 1040 1041 with self.assertRaises(ConfigureError) as e: 1042 self.get_config() 1043 1044 self.assertEquals(str(e.exception), message) 1045 1046 with self.assertRaises(ConfigureError) as e: 1047 self.get_config(["--with-qux"]) 1048 1049 self.assertEquals(str(e.exception), message) 1050 1051 with self.assertRaises(ConfigureError) as e: 1052 self.get_config(["--without-foo", "--with-qux"]) 1053 1054 self.assertEquals(str(e.exception), message) 1055 1056 def test_option_failures(self): 1057 with self.assertRaises(ConfigureError) as e: 1058 with self.moz_configure('option("--with-foo", help="foo")'): 1059 self.get_config() 1060 1061 self.assertEquals( 1062 str(e.exception), 1063 "Option `--with-foo` is not handled ; reference it with a @depends", 1064 ) 1065 1066 with self.assertRaises(ConfigureError) as e: 1067 with self.moz_configure( 1068 """ 1069 option("--with-foo", help="foo") 1070 option("--with-foo", help="foo") 1071 """ 1072 ): 1073 self.get_config() 1074 1075 self.assertEquals(str(e.exception), "Option `--with-foo` already defined") 1076 1077 with self.assertRaises(ConfigureError) as e: 1078 with self.moz_configure( 1079 """ 1080 option(env="MOZ_FOO", help="foo") 1081 option(env="MOZ_FOO", help="foo") 1082 """ 1083 ): 1084 self.get_config() 1085 1086 self.assertEquals(str(e.exception), "Option `MOZ_FOO` already defined") 1087 1088 with self.assertRaises(ConfigureError) as e: 1089 with self.moz_configure( 1090 """ 1091 option('--with-foo', env="MOZ_FOO", help="foo") 1092 option(env="MOZ_FOO", help="foo") 1093 """ 1094 ): 1095 self.get_config() 1096 1097 self.assertEquals(str(e.exception), "Option `MOZ_FOO` already defined") 1098 1099 with self.assertRaises(ConfigureError) as e: 1100 with self.moz_configure( 1101 """ 1102 option(env="MOZ_FOO", help="foo") 1103 option('--with-foo', env="MOZ_FOO", help="foo") 1104 """ 1105 ): 1106 self.get_config() 1107 1108 self.assertEquals(str(e.exception), "Option `MOZ_FOO` already defined") 1109 1110 with self.assertRaises(ConfigureError) as e: 1111 with self.moz_configure( 1112 """ 1113 option('--with-foo', env="MOZ_FOO", help="foo") 1114 option('--with-foo', help="foo") 1115 """ 1116 ): 1117 self.get_config() 1118 1119 self.assertEquals(str(e.exception), "Option `--with-foo` already defined") 1120 1121 def test_option_when(self): 1122 with self.moz_configure( 1123 """ 1124 option('--with-foo', help='foo', when=True) 1125 option('--with-bar', help='bar', when=False) 1126 option('--with-qux', env="QUX", help='qux', when='--with-foo') 1127 1128 set_config('FOO', depends('--with-foo', when=True)(lambda x: x)) 1129 set_config('BAR', depends('--with-bar', when=False)(lambda x: x)) 1130 set_config('QUX', depends('--with-qux', when='--with-foo')(lambda x: x)) 1131 """ 1132 ): 1133 config = self.get_config() 1134 self.assertEquals( 1135 config, 1136 { 1137 "FOO": NegativeOptionValue(), 1138 }, 1139 ) 1140 1141 config = self.get_config(["--with-foo"]) 1142 self.assertEquals( 1143 config, 1144 { 1145 "FOO": PositiveOptionValue(), 1146 "QUX": NegativeOptionValue(), 1147 }, 1148 ) 1149 1150 config = self.get_config(["--with-foo", "--with-qux"]) 1151 self.assertEquals( 1152 config, 1153 { 1154 "FOO": PositiveOptionValue(), 1155 "QUX": PositiveOptionValue(), 1156 }, 1157 ) 1158 1159 with self.assertRaises(InvalidOptionError) as e: 1160 self.get_config(["--with-bar"]) 1161 1162 self.assertEquals( 1163 str(e.exception), "--with-bar is not available in this configuration" 1164 ) 1165 1166 with self.assertRaises(InvalidOptionError) as e: 1167 self.get_config(["--with-qux"]) 1168 1169 self.assertEquals( 1170 str(e.exception), "--with-qux is not available in this configuration" 1171 ) 1172 1173 with self.assertRaises(InvalidOptionError) as e: 1174 self.get_config(["QUX=1"]) 1175 1176 self.assertEquals( 1177 str(e.exception), "QUX is not available in this configuration" 1178 ) 1179 1180 config = self.get_config(env={"QUX": "1"}) 1181 self.assertEquals( 1182 config, 1183 { 1184 "FOO": NegativeOptionValue(), 1185 }, 1186 ) 1187 1188 help, config = self.get_config(["--help"]) 1189 self.assertEquals( 1190 help.replace("\\", "/"), 1191 textwrap.dedent( 1192 """\ 1193 Usage: configure [options] 1194 1195 Options: [defaults in brackets after descriptions] 1196 Help options: 1197 --help print this message 1198 1199 Options from python/mozbuild/mozbuild/test/configure/data/moz.configure: 1200 --with-foo foo 1201 1202 1203 Environment variables: 1204 """ 1205 ), 1206 ) 1207 1208 help, config = self.get_config(["--help", "--with-foo"]) 1209 self.assertEquals( 1210 help.replace("\\", "/"), 1211 textwrap.dedent( 1212 """\ 1213 Usage: configure [options] 1214 1215 Options: [defaults in brackets after descriptions] 1216 Help options: 1217 --help print this message 1218 1219 Options from python/mozbuild/mozbuild/test/configure/data/moz.configure: 1220 --with-foo foo 1221 --with-qux qux 1222 1223 1224 Environment variables: 1225 """ 1226 ), 1227 ) 1228 1229 with self.moz_configure( 1230 """ 1231 option('--with-foo', help='foo', when=True) 1232 set_config('FOO', depends('--with-foo')(lambda x: x)) 1233 """ 1234 ): 1235 with self.assertRaises(ConfigureError) as e: 1236 self.get_config() 1237 1238 self.assertEquals( 1239 str(e.exception), 1240 "@depends function needs the same `when` as " "options it depends on", 1241 ) 1242 1243 with self.moz_configure( 1244 """ 1245 @depends(when=True) 1246 def always(): 1247 return True 1248 @depends(when=True) 1249 def always2(): 1250 return True 1251 option('--with-foo', help='foo', when=always) 1252 set_config('FOO', depends('--with-foo', when=always2)(lambda x: x)) 1253 """ 1254 ): 1255 with self.assertRaises(ConfigureError) as e: 1256 self.get_config() 1257 1258 self.assertEquals( 1259 str(e.exception), 1260 "@depends function needs the same `when` as " "options it depends on", 1261 ) 1262 1263 with self.moz_configure( 1264 """ 1265 @depends(when=True) 1266 def always(): 1267 return True 1268 @depends(when=True) 1269 def always2(): 1270 return True 1271 with only_when(always2): 1272 option('--with-foo', help='foo', when=always) 1273 # include() triggers resolution of its dependencies, and their 1274 # side effects. 1275 include(depends('--with-foo', when=always)(lambda x: x)) 1276 # The sandbox should figure that the `when` here is 1277 # appropriate. Bad behavior in CombinedDependsFunction.__eq__ 1278 # made this fail in the past. 1279 set_config('FOO', depends('--with-foo', when=always)(lambda x: x)) 1280 """ 1281 ): 1282 self.get_config() 1283 1284 with self.moz_configure( 1285 """ 1286 option('--with-foo', help='foo') 1287 option('--without-bar', help='bar', when='--with-foo') 1288 option('--with-qux', help='qux', when='--with-bar') 1289 set_config('QUX', True, when='--with-qux') 1290 """ 1291 ): 1292 # These are valid: 1293 self.get_config(["--with-foo"]) 1294 self.get_config(["--with-foo", "--with-bar"]) 1295 self.get_config(["--with-foo", "--without-bar"]) 1296 self.get_config(["--with-foo", "--with-bar", "--with-qux"]) 1297 self.get_config(["--with-foo", "--with-bar", "--without-qux"]) 1298 with self.assertRaises(InvalidOptionError) as e: 1299 self.get_config(["--with-bar"]) 1300 with self.assertRaises(InvalidOptionError) as e: 1301 self.get_config(["--without-bar"]) 1302 with self.assertRaises(InvalidOptionError) as e: 1303 self.get_config(["--with-qux"]) 1304 with self.assertRaises(InvalidOptionError) as e: 1305 self.get_config(["--without-qux"]) 1306 with self.assertRaises(InvalidOptionError) as e: 1307 self.get_config(["--with-foo", "--without-bar", "--with-qux"]) 1308 with self.assertRaises(InvalidOptionError) as e: 1309 self.get_config(["--with-foo", "--without-bar", "--without-qux"]) 1310 1311 def test_include_failures(self): 1312 with self.assertRaises(ConfigureError) as e: 1313 with self.moz_configure('include("../foo.configure")'): 1314 self.get_config() 1315 1316 self.assertEquals( 1317 str(e.exception), 1318 "Cannot include `%s` because it is not in a subdirectory of `%s`" 1319 % ( 1320 mozpath.normpath(mozpath.join(test_data_path, "..", "foo.configure")), 1321 mozpath.normsep(test_data_path), 1322 ), 1323 ) 1324 1325 with self.assertRaises(ConfigureError) as e: 1326 with self.moz_configure( 1327 """ 1328 include('extra.configure') 1329 include('extra.configure') 1330 """ 1331 ): 1332 self.get_config() 1333 1334 self.assertEquals( 1335 str(e.exception), 1336 "Cannot include `%s` because it was included already." 1337 % mozpath.normpath(mozpath.join(test_data_path, "extra.configure")), 1338 ) 1339 1340 with self.assertRaises(TypeError) as e: 1341 with self.moz_configure( 1342 """ 1343 include(42) 1344 """ 1345 ): 1346 self.get_config() 1347 1348 self.assertEquals(str(e.exception), "Unexpected type: 'int'") 1349 1350 def test_include_when(self): 1351 with MockedOpen( 1352 { 1353 os.path.join(test_data_path, "moz.configure"): textwrap.dedent( 1354 """ 1355 option('--with-foo', help='foo') 1356 1357 include('always.configure', when=True) 1358 include('never.configure', when=False) 1359 include('foo.configure', when='--with-foo') 1360 1361 set_config('FOO', foo) 1362 set_config('BAR', bar) 1363 set_config('QUX', qux) 1364 """ 1365 ), 1366 os.path.join(test_data_path, "always.configure"): textwrap.dedent( 1367 """ 1368 option('--with-bar', help='bar') 1369 @depends('--with-bar') 1370 def bar(x): 1371 if x: 1372 return 'bar' 1373 """ 1374 ), 1375 os.path.join(test_data_path, "never.configure"): textwrap.dedent( 1376 """ 1377 option('--with-qux', help='qux') 1378 @depends('--with-qux') 1379 def qux(x): 1380 if x: 1381 return 'qux' 1382 """ 1383 ), 1384 os.path.join(test_data_path, "foo.configure"): textwrap.dedent( 1385 """ 1386 option('--with-foo-really', help='really foo') 1387 @depends('--with-foo-really') 1388 def foo(x): 1389 if x: 1390 return 'foo' 1391 1392 include('foo2.configure', when='--with-foo-really') 1393 """ 1394 ), 1395 os.path.join(test_data_path, "foo2.configure"): textwrap.dedent( 1396 """ 1397 set_config('FOO2', True) 1398 """ 1399 ), 1400 } 1401 ): 1402 config = self.get_config() 1403 self.assertEquals(config, {}) 1404 1405 config = self.get_config(["--with-foo"]) 1406 self.assertEquals(config, {}) 1407 1408 config = self.get_config(["--with-bar"]) 1409 self.assertEquals( 1410 config, 1411 { 1412 "BAR": "bar", 1413 }, 1414 ) 1415 1416 with self.assertRaises(InvalidOptionError) as e: 1417 self.get_config(["--with-qux"]) 1418 1419 self.assertEquals( 1420 str(e.exception), "--with-qux is not available in this configuration" 1421 ) 1422 1423 config = self.get_config(["--with-foo", "--with-foo-really"]) 1424 self.assertEquals( 1425 config, 1426 { 1427 "FOO": "foo", 1428 "FOO2": True, 1429 }, 1430 ) 1431 1432 def test_sandbox_failures(self): 1433 with self.assertRaises(KeyError) as e: 1434 with self.moz_configure( 1435 """ 1436 include = 42 1437 """ 1438 ): 1439 self.get_config() 1440 1441 self.assertIn("Cannot reassign builtins", str(e.exception)) 1442 1443 with self.assertRaises(KeyError) as e: 1444 with self.moz_configure( 1445 """ 1446 foo = 42 1447 """ 1448 ): 1449 self.get_config() 1450 1451 self.assertIn( 1452 "Cannot assign `foo` because it is neither a @depends nor a " "@template", 1453 str(e.exception), 1454 ) 1455 1456 def test_depends_failures(self): 1457 with self.assertRaises(ConfigureError) as e: 1458 with self.moz_configure( 1459 """ 1460 @depends() 1461 def foo(): 1462 return 1463 """ 1464 ): 1465 self.get_config() 1466 1467 self.assertEquals(str(e.exception), "@depends needs at least one argument") 1468 1469 with self.assertRaises(ConfigureError) as e: 1470 with self.moz_configure( 1471 """ 1472 @depends('--with-foo') 1473 def foo(value): 1474 return value 1475 """ 1476 ): 1477 self.get_config() 1478 1479 self.assertEquals( 1480 str(e.exception), 1481 "'--with-foo' is not a known option. Maybe it's " "declared too late?", 1482 ) 1483 1484 with self.assertRaises(ConfigureError) as e: 1485 with self.moz_configure( 1486 """ 1487 @depends('--with-foo=42') 1488 def foo(value): 1489 return value 1490 """ 1491 ): 1492 self.get_config() 1493 1494 self.assertEquals(str(e.exception), "Option must not contain an '='") 1495 1496 with self.assertRaises(TypeError) as e: 1497 with self.moz_configure( 1498 """ 1499 @depends(42) 1500 def foo(value): 1501 return value 1502 """ 1503 ): 1504 self.get_config() 1505 1506 self.assertEquals( 1507 str(e.exception), 1508 "Cannot use object of type 'int' as argument " "to @depends", 1509 ) 1510 1511 with self.assertRaises(ConfigureError) as e: 1512 with self.moz_configure( 1513 """ 1514 @depends('--help') 1515 def foo(value): 1516 yield 1517 """ 1518 ): 1519 self.get_config() 1520 1521 self.assertEquals( 1522 str(e.exception), "Cannot decorate generator functions with @depends" 1523 ) 1524 1525 with self.assertRaises(TypeError) as e: 1526 with self.moz_configure( 1527 """ 1528 depends('--help')(42) 1529 """ 1530 ): 1531 self.get_config() 1532 1533 self.assertEquals(str(e.exception), "Unexpected type: 'int'") 1534 1535 with self.assertRaises(ConfigureError) as e: 1536 with self.moz_configure( 1537 """ 1538 option('--foo', help='foo') 1539 @depends('--foo') 1540 def foo(value): 1541 return value 1542 1543 foo() 1544 """ 1545 ): 1546 self.get_config() 1547 1548 self.assertEquals(str(e.exception), "The `foo` function may not be called") 1549 1550 with self.assertRaises(TypeError) as e: 1551 with self.moz_configure( 1552 """ 1553 @depends('--help', foo=42) 1554 def foo(_): 1555 return 1556 """ 1557 ): 1558 self.get_config() 1559 1560 self.assertEquals( 1561 str(e.exception), "depends_impl() got an unexpected keyword argument 'foo'" 1562 ) 1563 1564 def test_depends_when(self): 1565 with self.moz_configure( 1566 """ 1567 @depends(when=True) 1568 def foo(): 1569 return 'foo' 1570 1571 set_config('FOO', foo) 1572 1573 @depends(when=False) 1574 def bar(): 1575 return 'bar' 1576 1577 set_config('BAR', bar) 1578 1579 option('--with-qux', help='qux') 1580 @depends(when='--with-qux') 1581 def qux(): 1582 return 'qux' 1583 1584 set_config('QUX', qux) 1585 """ 1586 ): 1587 config = self.get_config() 1588 self.assertEquals( 1589 config, 1590 { 1591 "FOO": "foo", 1592 }, 1593 ) 1594 1595 config = self.get_config(["--with-qux"]) 1596 self.assertEquals( 1597 config, 1598 { 1599 "FOO": "foo", 1600 "QUX": "qux", 1601 }, 1602 ) 1603 1604 def test_imports_failures(self): 1605 with self.assertRaises(ConfigureError) as e: 1606 with self.moz_configure( 1607 """ 1608 @imports('os') 1609 @template 1610 def foo(value): 1611 return value 1612 """ 1613 ): 1614 self.get_config() 1615 1616 self.assertEquals(str(e.exception), "@imports must appear after @template") 1617 1618 with self.assertRaises(ConfigureError) as e: 1619 with self.moz_configure( 1620 """ 1621 option('--foo', help='foo') 1622 @imports('os') 1623 @depends('--foo') 1624 def foo(value): 1625 return value 1626 """ 1627 ): 1628 self.get_config() 1629 1630 self.assertEquals(str(e.exception), "@imports must appear after @depends") 1631 1632 for import_ in ( 1633 "42", 1634 "_from=42, _import='os'", 1635 "_from='os', _import='path', _as=42", 1636 ): 1637 with self.assertRaises(TypeError) as e: 1638 with self.moz_configure( 1639 """ 1640 @imports(%s) 1641 @template 1642 def foo(value): 1643 return value 1644 """ 1645 % import_ 1646 ): 1647 self.get_config() 1648 1649 self.assertEquals(str(e.exception), "Unexpected type: 'int'") 1650 1651 with self.assertRaises(TypeError) as e: 1652 with self.moz_configure( 1653 """ 1654 @imports('os', 42) 1655 @template 1656 def foo(value): 1657 return value 1658 """ 1659 ): 1660 self.get_config() 1661 1662 self.assertEquals(str(e.exception), "Unexpected type: 'int'") 1663 1664 with self.assertRaises(ValueError) as e: 1665 with self.moz_configure( 1666 """ 1667 @imports('os*') 1668 def foo(value): 1669 return value 1670 """ 1671 ): 1672 self.get_config() 1673 1674 self.assertEquals(str(e.exception), "Invalid argument to @imports: 'os*'") 1675 1676 def test_only_when(self): 1677 moz_configure = """ 1678 option('--enable-when', help='when') 1679 @depends('--enable-when', '--help') 1680 def when(value, _): 1681 return bool(value) 1682 1683 with only_when(when): 1684 option('--foo', nargs='*', help='foo') 1685 @depends('--foo') 1686 def foo(value): 1687 return value 1688 1689 set_config('FOO', foo) 1690 set_define('FOO', foo) 1691 1692 # It is possible to depend on a function defined in a only_when 1693 # block. It then resolves to `None`. 1694 set_config('BAR', depends(foo)(lambda x: x)) 1695 set_define('BAR', depends(foo)(lambda x: x)) 1696 """ 1697 1698 with self.moz_configure(moz_configure): 1699 config = self.get_config() 1700 self.assertEqual( 1701 config, 1702 { 1703 "DEFINES": {}, 1704 }, 1705 ) 1706 1707 config = self.get_config(["--enable-when"]) 1708 self.assertEqual( 1709 config, 1710 { 1711 "BAR": NegativeOptionValue(), 1712 "FOO": NegativeOptionValue(), 1713 "DEFINES": { 1714 "BAR": NegativeOptionValue(), 1715 "FOO": NegativeOptionValue(), 1716 }, 1717 }, 1718 ) 1719 1720 config = self.get_config(["--enable-when", "--foo=bar"]) 1721 self.assertEqual( 1722 config, 1723 { 1724 "BAR": PositiveOptionValue(["bar"]), 1725 "FOO": PositiveOptionValue(["bar"]), 1726 "DEFINES": { 1727 "BAR": PositiveOptionValue(["bar"]), 1728 "FOO": PositiveOptionValue(["bar"]), 1729 }, 1730 }, 1731 ) 1732 1733 # The --foo option doesn't exist when --enable-when is not given. 1734 with self.assertRaises(InvalidOptionError) as e: 1735 self.get_config(["--foo"]) 1736 1737 self.assertEquals( 1738 str(e.exception), "--foo is not available in this configuration" 1739 ) 1740 1741 # Cannot depend on an option defined in a only_when block, because we 1742 # don't know what OptionValue would make sense. 1743 with self.moz_configure( 1744 moz_configure 1745 + """ 1746 set_config('QUX', depends('--foo')(lambda x: x)) 1747 """ 1748 ): 1749 with self.assertRaises(ConfigureError) as e: 1750 self.get_config() 1751 1752 self.assertEquals( 1753 str(e.exception), 1754 "@depends function needs the same `when` as " "options it depends on", 1755 ) 1756 1757 with self.moz_configure( 1758 moz_configure 1759 + """ 1760 set_config('QUX', depends('--foo', when=when)(lambda x: x)) 1761 """ 1762 ): 1763 self.get_config(["--enable-when"]) 1764 1765 # Using imply_option for an option defined in a only_when block fails 1766 # similarly if the imply_option happens outside the block. 1767 with self.moz_configure( 1768 """ 1769 imply_option('--foo', True) 1770 """ 1771 + moz_configure 1772 ): 1773 with self.assertRaises(InvalidOptionError) as e: 1774 self.get_config() 1775 1776 self.assertEquals( 1777 str(e.exception), "--foo is not available in this configuration" 1778 ) 1779 1780 # And similarly doesn't fail when the condition is true. 1781 with self.moz_configure( 1782 """ 1783 imply_option('--foo', True) 1784 """ 1785 + moz_configure 1786 ): 1787 self.get_config(["--enable-when"]) 1788 1789 def test_depends_binary_ops(self): 1790 with self.moz_configure( 1791 """ 1792 option('--foo', nargs=1, help='foo') 1793 @depends('--foo') 1794 def foo(value): 1795 return value or 0 1796 1797 option('--bar', nargs=1, help='bar') 1798 @depends('--bar') 1799 def bar(value): 1800 return value or '' 1801 1802 option('--baz', nargs=1, help='baz') 1803 @depends('--baz') 1804 def baz(value): 1805 return value 1806 1807 set_config('FOOorBAR', foo | bar) 1808 set_config('FOOorBARorBAZ', foo | bar | baz) 1809 set_config('FOOandBAR', foo & bar) 1810 set_config('FOOandBARandBAZ', foo & bar & baz) 1811 """ 1812 ): 1813 for foo_opt, foo_value in ( 1814 ("", 0), 1815 ("--foo=foo", PositiveOptionValue(("foo",))), 1816 ): 1817 for bar_opt, bar_value in ( 1818 ("", ""), 1819 ("--bar=bar", PositiveOptionValue(("bar",))), 1820 ): 1821 for baz_opt, baz_value in ( 1822 ("", NegativeOptionValue()), 1823 ("--baz=baz", PositiveOptionValue(("baz",))), 1824 ): 1825 config = self.get_config( 1826 [x for x in (foo_opt, bar_opt, baz_opt) if x] 1827 ) 1828 self.assertEqual( 1829 config, 1830 { 1831 "FOOorBAR": foo_value or bar_value, 1832 "FOOorBARorBAZ": foo_value or bar_value or baz_value, 1833 "FOOandBAR": foo_value and bar_value, 1834 "FOOandBARandBAZ": foo_value 1835 and bar_value 1836 and baz_value, 1837 }, 1838 ) 1839 1840 def test_depends_getattr(self): 1841 with self.moz_configure( 1842 """ 1843 @imports(_from='mozbuild.util', _import='ReadOnlyNamespace') 1844 def namespace(**kwargs): 1845 return ReadOnlyNamespace(**kwargs) 1846 1847 option('--foo', nargs=1, help='foo') 1848 @depends('--foo') 1849 def foo(value): 1850 return value 1851 1852 option('--bar', nargs=1, help='bar') 1853 @depends('--bar') 1854 def bar(value): 1855 return value or None 1856 1857 @depends(foo, bar) 1858 def foobar(foo, bar): 1859 return namespace(foo=foo, bar=bar) 1860 1861 set_config('FOO', foobar.foo) 1862 set_config('BAR', foobar.bar) 1863 set_config('BAZ', foobar.baz) 1864 """ 1865 ): 1866 config = self.get_config() 1867 self.assertEqual( 1868 config, 1869 { 1870 "FOO": NegativeOptionValue(), 1871 }, 1872 ) 1873 1874 config = self.get_config(["--foo=foo"]) 1875 self.assertEqual( 1876 config, 1877 { 1878 "FOO": PositiveOptionValue(("foo",)), 1879 }, 1880 ) 1881 1882 config = self.get_config(["--bar=bar"]) 1883 self.assertEqual( 1884 config, 1885 { 1886 "FOO": NegativeOptionValue(), 1887 "BAR": PositiveOptionValue(("bar",)), 1888 }, 1889 ) 1890 1891 config = self.get_config(["--foo=foo", "--bar=bar"]) 1892 self.assertEqual( 1893 config, 1894 { 1895 "FOO": PositiveOptionValue(("foo",)), 1896 "BAR": PositiveOptionValue(("bar",)), 1897 }, 1898 ) 1899 1900 1901if __name__ == "__main__": 1902 main() 1903