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