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 StringIO import StringIO 8import os 9import sys 10import textwrap 11import unittest 12 13from mozunit import ( 14 main, 15 MockedOpen, 16) 17 18from mozbuild.configure.options import ( 19 InvalidOptionError, 20 NegativeOptionValue, 21 PositiveOptionValue, 22) 23from mozbuild.configure import ( 24 ConfigureError, 25 ConfigureSandbox, 26) 27from mozbuild.util import exec_ 28 29import mozpack.path as mozpath 30 31test_data_path = mozpath.abspath(mozpath.dirname(__file__)) 32test_data_path = mozpath.join(test_data_path, 'data') 33 34 35class TestConfigure(unittest.TestCase): 36 def get_config(self, options=[], env={}, configure='moz.configure', 37 prog='/bin/configure'): 38 config = {} 39 out = StringIO() 40 sandbox = ConfigureSandbox(config, env, [prog] + options, out, out) 41 42 sandbox.run(mozpath.join(test_data_path, configure)) 43 44 if '--help' in options: 45 return out.getvalue(), config 46 self.assertEquals('', out.getvalue()) 47 return config 48 49 def moz_configure(self, source): 50 return MockedOpen({ 51 os.path.join(test_data_path, 52 'moz.configure'): textwrap.dedent(source) 53 }) 54 55 def test_defaults(self): 56 config = self.get_config() 57 self.maxDiff = None 58 self.assertEquals({ 59 'CHOICES': NegativeOptionValue(), 60 'DEFAULTED': PositiveOptionValue(('not-simple',)), 61 'IS_GCC': NegativeOptionValue(), 62 'REMAINDER': (PositiveOptionValue(), NegativeOptionValue(), 63 NegativeOptionValue(), NegativeOptionValue()), 64 'SIMPLE': NegativeOptionValue(), 65 'VALUES': NegativeOptionValue(), 66 'VALUES2': NegativeOptionValue(), 67 'VALUES3': NegativeOptionValue(), 68 'WITH_ENV': NegativeOptionValue(), 69 }, config) 70 71 def test_help(self): 72 help, config = self.get_config(['--help'], prog='configure') 73 74 self.assertEquals({}, config) 75 self.maxDiff = None 76 self.assertEquals( 77 'Usage: configure [options]\n' 78 '\n' 79 'Options: [defaults in brackets after descriptions]\n' 80 ' --help print this message\n' 81 ' --enable-simple Enable simple\n' 82 ' --enable-with-env Enable with env\n' 83 ' --enable-values Enable values\n' 84 ' --without-thing Build without thing\n' 85 ' --with-stuff Build with stuff\n' 86 ' --option Option\n' 87 ' --with-returned-default Returned default [not-simple]\n' 88 ' --returned-choices Choices\n' 89 ' --enable-imports-in-template\n' 90 ' Imports in template\n' 91 ' --enable-include Include\n' 92 ' --with-imports Imports\n' 93 '\n' 94 'Environment variables:\n' 95 ' CC C Compiler\n', 96 help 97 ) 98 99 def test_unknown(self): 100 with self.assertRaises(InvalidOptionError): 101 self.get_config(['--unknown']) 102 103 def test_simple(self): 104 for config in ( 105 self.get_config(), 106 self.get_config(['--disable-simple']), 107 # Last option wins. 108 self.get_config(['--enable-simple', '--disable-simple']), 109 ): 110 self.assertNotIn('ENABLED_SIMPLE', config) 111 self.assertIn('SIMPLE', config) 112 self.assertEquals(NegativeOptionValue(), config['SIMPLE']) 113 114 for config in ( 115 self.get_config(['--enable-simple']), 116 self.get_config(['--disable-simple', '--enable-simple']), 117 ): 118 self.assertIn('ENABLED_SIMPLE', config) 119 self.assertIn('SIMPLE', config) 120 self.assertEquals(PositiveOptionValue(), config['SIMPLE']) 121 self.assertIs(config['SIMPLE'], config['ENABLED_SIMPLE']) 122 123 # --enable-simple doesn't take values. 124 with self.assertRaises(InvalidOptionError): 125 self.get_config(['--enable-simple=value']) 126 127 def test_with_env(self): 128 for config in ( 129 self.get_config(), 130 self.get_config(['--disable-with-env']), 131 self.get_config(['--enable-with-env', '--disable-with-env']), 132 self.get_config(env={'MOZ_WITH_ENV': ''}), 133 # Options win over environment 134 self.get_config(['--disable-with-env'], 135 env={'MOZ_WITH_ENV': '1'}), 136 ): 137 self.assertIn('WITH_ENV', config) 138 self.assertEquals(NegativeOptionValue(), config['WITH_ENV']) 139 140 for config in ( 141 self.get_config(['--enable-with-env']), 142 self.get_config(['--disable-with-env', '--enable-with-env']), 143 self.get_config(env={'MOZ_WITH_ENV': '1'}), 144 self.get_config(['--enable-with-env'], 145 env={'MOZ_WITH_ENV': ''}), 146 ): 147 self.assertIn('WITH_ENV', config) 148 self.assertEquals(PositiveOptionValue(), config['WITH_ENV']) 149 150 with self.assertRaises(InvalidOptionError): 151 self.get_config(['--enable-with-env=value']) 152 153 with self.assertRaises(InvalidOptionError): 154 self.get_config(env={'MOZ_WITH_ENV': 'value'}) 155 156 def test_values(self, name='VALUES'): 157 for config in ( 158 self.get_config(), 159 self.get_config(['--disable-values']), 160 self.get_config(['--enable-values', '--disable-values']), 161 ): 162 self.assertIn(name, config) 163 self.assertEquals(NegativeOptionValue(), config[name]) 164 165 for config in ( 166 self.get_config(['--enable-values']), 167 self.get_config(['--disable-values', '--enable-values']), 168 ): 169 self.assertIn(name, config) 170 self.assertEquals(PositiveOptionValue(), config[name]) 171 172 config = self.get_config(['--enable-values=foo']) 173 self.assertIn(name, config) 174 self.assertEquals(PositiveOptionValue(('foo',)), config[name]) 175 176 config = self.get_config(['--enable-values=foo,bar']) 177 self.assertIn(name, config) 178 self.assertTrue(config[name]) 179 self.assertEquals(PositiveOptionValue(('foo', 'bar')), config[name]) 180 181 def test_values2(self): 182 self.test_values('VALUES2') 183 184 def test_values3(self): 185 self.test_values('VALUES3') 186 187 def test_returned_default(self): 188 config = self.get_config(['--enable-simple']) 189 self.assertIn('DEFAULTED', config) 190 self.assertEquals( 191 PositiveOptionValue(('simple',)), config['DEFAULTED']) 192 193 config = self.get_config(['--disable-simple']) 194 self.assertIn('DEFAULTED', config) 195 self.assertEquals( 196 PositiveOptionValue(('not-simple',)), config['DEFAULTED']) 197 198 def test_returned_choices(self): 199 for val in ('a', 'b', 'c'): 200 config = self.get_config( 201 ['--enable-values=alpha', '--returned-choices=%s' % val]) 202 self.assertIn('CHOICES', config) 203 self.assertEquals(PositiveOptionValue((val,)), config['CHOICES']) 204 205 for val in ('0', '1', '2'): 206 config = self.get_config( 207 ['--enable-values=numeric', '--returned-choices=%s' % val]) 208 self.assertIn('CHOICES', config) 209 self.assertEquals(PositiveOptionValue((val,)), config['CHOICES']) 210 211 with self.assertRaises(InvalidOptionError): 212 self.get_config(['--enable-values=numeric', 213 '--returned-choices=a']) 214 215 with self.assertRaises(InvalidOptionError): 216 self.get_config(['--enable-values=alpha', '--returned-choices=0']) 217 218 def test_included(self): 219 config = self.get_config(env={'CC': 'gcc'}) 220 self.assertIn('IS_GCC', config) 221 self.assertEquals(config['IS_GCC'], True) 222 223 config = self.get_config( 224 ['--enable-include=extra.configure', '--extra']) 225 self.assertIn('EXTRA', config) 226 self.assertEquals(PositiveOptionValue(), config['EXTRA']) 227 228 with self.assertRaises(InvalidOptionError): 229 self.get_config(['--extra']) 230 231 def test_template(self): 232 config = self.get_config(env={'CC': 'gcc'}) 233 self.assertIn('CFLAGS', config) 234 self.assertEquals(config['CFLAGS'], ['-Werror=foobar']) 235 236 config = self.get_config(env={'CC': 'clang'}) 237 self.assertNotIn('CFLAGS', config) 238 239 def test_imports(self): 240 config = {} 241 out = StringIO() 242 sandbox = ConfigureSandbox(config, {}, ['configure'], out, out) 243 244 with self.assertRaises(ImportError): 245 exec_(textwrap.dedent(''' 246 @template 247 def foo(): 248 import sys 249 foo()'''), 250 sandbox 251 ) 252 253 exec_(textwrap.dedent(''' 254 @template 255 @imports('sys') 256 def foo(): 257 return sys'''), 258 sandbox 259 ) 260 261 self.assertIs(sandbox['foo'](), sys) 262 263 exec_(textwrap.dedent(''' 264 @template 265 @imports(_from='os', _import='path') 266 def foo(): 267 return path'''), 268 sandbox 269 ) 270 271 self.assertIs(sandbox['foo'](), os.path) 272 273 exec_(textwrap.dedent(''' 274 @template 275 @imports(_from='os', _import='path', _as='os_path') 276 def foo(): 277 return os_path'''), 278 sandbox 279 ) 280 281 self.assertIs(sandbox['foo'](), os.path) 282 283 exec_(textwrap.dedent(''' 284 @template 285 @imports('__builtin__') 286 def foo(): 287 return __builtin__'''), 288 sandbox 289 ) 290 291 import __builtin__ 292 self.assertIs(sandbox['foo'](), __builtin__) 293 294 exec_(textwrap.dedent(''' 295 @template 296 @imports(_from='__builtin__', _import='open') 297 def foo(): 298 return open('%s')''' % os.devnull), 299 sandbox 300 ) 301 302 f = sandbox['foo']() 303 self.assertEquals(f.name, os.devnull) 304 f.close() 305 306 # This unlocks the sandbox 307 exec_(textwrap.dedent(''' 308 @template 309 @imports(_import='__builtin__', _as='__builtins__') 310 def foo(): 311 import sys 312 return sys'''), 313 sandbox 314 ) 315 316 self.assertIs(sandbox['foo'](), sys) 317 318 exec_(textwrap.dedent(''' 319 @template 320 @imports('__sandbox__') 321 def foo(): 322 return __sandbox__'''), 323 sandbox 324 ) 325 326 self.assertIs(sandbox['foo'](), sandbox) 327 328 exec_(textwrap.dedent(''' 329 @template 330 @imports(_import='__sandbox__', _as='s') 331 def foo(): 332 return s'''), 333 sandbox 334 ) 335 336 self.assertIs(sandbox['foo'](), sandbox) 337 338 # Nothing leaked from the function being executed 339 self.assertEquals(sandbox.keys(), ['__builtins__', 'foo']) 340 self.assertEquals(sandbox['__builtins__'], ConfigureSandbox.BUILTINS) 341 342 exec_(textwrap.dedent(''' 343 @template 344 @imports('sys') 345 def foo(): 346 @depends(when=True) 347 def bar(): 348 return sys 349 return bar 350 bar = foo()'''), 351 sandbox 352 ) 353 354 with self.assertRaises(NameError) as e: 355 sandbox._depends[sandbox['bar']].result() 356 357 self.assertEquals(e.exception.message, 358 "global name 'sys' is not defined") 359 360 def test_apply_imports(self): 361 imports = [] 362 363 class CountApplyImportsSandbox(ConfigureSandbox): 364 def _apply_imports(self, *args, **kwargs): 365 imports.append((args, kwargs)) 366 super(CountApplyImportsSandbox, self)._apply_imports( 367 *args, **kwargs) 368 369 config = {} 370 out = StringIO() 371 sandbox = CountApplyImportsSandbox(config, {}, ['configure'], out, out) 372 373 exec_(textwrap.dedent(''' 374 @template 375 @imports('sys') 376 def foo(): 377 return sys 378 foo() 379 foo()'''), 380 sandbox 381 ) 382 383 self.assertEquals(len(imports), 1) 384 385 def test_os_path(self): 386 config = self.get_config(['--with-imports=%s' % __file__]) 387 self.assertIn('HAS_ABSPATH', config) 388 self.assertEquals(config['HAS_ABSPATH'], True) 389 self.assertIn('HAS_GETATIME', config) 390 self.assertEquals(config['HAS_GETATIME'], True) 391 self.assertIn('HAS_GETATIME2', config) 392 self.assertEquals(config['HAS_GETATIME2'], False) 393 394 def test_template_call(self): 395 config = self.get_config(env={'CC': 'gcc'}) 396 self.assertIn('TEMPLATE_VALUE', config) 397 self.assertEquals(config['TEMPLATE_VALUE'], 42) 398 self.assertIn('TEMPLATE_VALUE_2', config) 399 self.assertEquals(config['TEMPLATE_VALUE_2'], 21) 400 401 def test_template_imports(self): 402 config = self.get_config(['--enable-imports-in-template']) 403 self.assertIn('PLATFORM', config) 404 self.assertEquals(config['PLATFORM'], sys.platform) 405 406 def test_decorators(self): 407 config = {} 408 out = StringIO() 409 sandbox = ConfigureSandbox(config, {}, ['configure'], out, out) 410 411 sandbox.include_file(mozpath.join(test_data_path, 'decorators.configure')) 412 413 self.assertNotIn('FOO', sandbox) 414 self.assertNotIn('BAR', sandbox) 415 self.assertNotIn('QUX', sandbox) 416 417 def test_set_config(self): 418 def get_config(*args): 419 return self.get_config(*args, configure='set_config.configure') 420 421 help, config = get_config(['--help']) 422 self.assertEquals(config, {}) 423 424 config = get_config(['--set-foo']) 425 self.assertIn('FOO', config) 426 self.assertEquals(config['FOO'], True) 427 428 config = get_config(['--set-bar']) 429 self.assertNotIn('FOO', config) 430 self.assertIn('BAR', config) 431 self.assertEquals(config['BAR'], True) 432 433 config = get_config(['--set-value=qux']) 434 self.assertIn('VALUE', config) 435 self.assertEquals(config['VALUE'], 'qux') 436 437 config = get_config(['--set-name=hoge']) 438 self.assertIn('hoge', config) 439 self.assertEquals(config['hoge'], True) 440 441 config = get_config([]) 442 self.assertEquals(config, {'BAR': False}) 443 444 with self.assertRaises(ConfigureError): 445 # Both --set-foo and --set-name=FOO are going to try to 446 # set_config('FOO'...) 447 get_config(['--set-foo', '--set-name=FOO']) 448 449 def test_set_config_when(self): 450 with self.moz_configure(''' 451 option('--with-qux', help='qux') 452 set_config('FOO', 'foo', when=True) 453 set_config('BAR', 'bar', when=False) 454 set_config('QUX', 'qux', when='--with-qux') 455 '''): 456 config = self.get_config() 457 self.assertEquals(config, { 458 'FOO': 'foo', 459 }) 460 config = self.get_config(['--with-qux']) 461 self.assertEquals(config, { 462 'FOO': 'foo', 463 'QUX': 'qux', 464 }) 465 466 def test_set_define(self): 467 def get_config(*args): 468 return self.get_config(*args, configure='set_define.configure') 469 470 help, config = get_config(['--help']) 471 self.assertEquals(config, {'DEFINES': {}}) 472 473 config = get_config(['--set-foo']) 474 self.assertIn('FOO', config['DEFINES']) 475 self.assertEquals(config['DEFINES']['FOO'], True) 476 477 config = get_config(['--set-bar']) 478 self.assertNotIn('FOO', config['DEFINES']) 479 self.assertIn('BAR', config['DEFINES']) 480 self.assertEquals(config['DEFINES']['BAR'], True) 481 482 config = get_config(['--set-value=qux']) 483 self.assertIn('VALUE', config['DEFINES']) 484 self.assertEquals(config['DEFINES']['VALUE'], 'qux') 485 486 config = get_config(['--set-name=hoge']) 487 self.assertIn('hoge', config['DEFINES']) 488 self.assertEquals(config['DEFINES']['hoge'], True) 489 490 config = get_config([]) 491 self.assertEquals(config['DEFINES'], {'BAR': False}) 492 493 with self.assertRaises(ConfigureError): 494 # Both --set-foo and --set-name=FOO are going to try to 495 # set_define('FOO'...) 496 get_config(['--set-foo', '--set-name=FOO']) 497 498 def test_set_define_when(self): 499 with self.moz_configure(''' 500 option('--with-qux', help='qux') 501 set_define('FOO', 'foo', when=True) 502 set_define('BAR', 'bar', when=False) 503 set_define('QUX', 'qux', when='--with-qux') 504 '''): 505 config = self.get_config() 506 self.assertEquals(config['DEFINES'], { 507 'FOO': 'foo', 508 }) 509 config = self.get_config(['--with-qux']) 510 self.assertEquals(config['DEFINES'], { 511 'FOO': 'foo', 512 'QUX': 'qux', 513 }) 514 515 def test_imply_option_simple(self): 516 def get_config(*args): 517 return self.get_config( 518 *args, configure='imply_option/simple.configure') 519 520 help, config = get_config(['--help']) 521 self.assertEquals(config, {}) 522 523 config = get_config([]) 524 self.assertEquals(config, {}) 525 526 config = get_config(['--enable-foo']) 527 self.assertIn('BAR', config) 528 self.assertEquals(config['BAR'], PositiveOptionValue()) 529 530 with self.assertRaises(InvalidOptionError) as e: 531 get_config(['--enable-foo', '--disable-bar']) 532 533 self.assertEquals( 534 e.exception.message, 535 "'--enable-bar' implied by '--enable-foo' conflicts with " 536 "'--disable-bar' from the command-line") 537 538 def test_imply_option_negative(self): 539 def get_config(*args): 540 return self.get_config( 541 *args, configure='imply_option/negative.configure') 542 543 help, config = get_config(['--help']) 544 self.assertEquals(config, {}) 545 546 config = get_config([]) 547 self.assertEquals(config, {}) 548 549 config = get_config(['--enable-foo']) 550 self.assertIn('BAR', config) 551 self.assertEquals(config['BAR'], NegativeOptionValue()) 552 553 with self.assertRaises(InvalidOptionError) as e: 554 get_config(['--enable-foo', '--enable-bar']) 555 556 self.assertEquals( 557 e.exception.message, 558 "'--disable-bar' implied by '--enable-foo' conflicts with " 559 "'--enable-bar' from the command-line") 560 561 config = get_config(['--disable-hoge']) 562 self.assertIn('BAR', config) 563 self.assertEquals(config['BAR'], NegativeOptionValue()) 564 565 with self.assertRaises(InvalidOptionError) as e: 566 get_config(['--disable-hoge', '--enable-bar']) 567 568 self.assertEquals( 569 e.exception.message, 570 "'--disable-bar' implied by '--disable-hoge' conflicts with " 571 "'--enable-bar' from the command-line") 572 573 def test_imply_option_values(self): 574 def get_config(*args): 575 return self.get_config( 576 *args, configure='imply_option/values.configure') 577 578 help, config = get_config(['--help']) 579 self.assertEquals(config, {}) 580 581 config = get_config([]) 582 self.assertEquals(config, {}) 583 584 config = get_config(['--enable-foo=a']) 585 self.assertIn('BAR', config) 586 self.assertEquals(config['BAR'], PositiveOptionValue(('a',))) 587 588 config = get_config(['--enable-foo=a,b']) 589 self.assertIn('BAR', config) 590 self.assertEquals(config['BAR'], PositiveOptionValue(('a','b'))) 591 592 with self.assertRaises(InvalidOptionError) as e: 593 get_config(['--enable-foo=a,b', '--disable-bar']) 594 595 self.assertEquals( 596 e.exception.message, 597 "'--enable-bar=a,b' implied by '--enable-foo' conflicts with " 598 "'--disable-bar' from the command-line") 599 600 def test_imply_option_infer(self): 601 def get_config(*args): 602 return self.get_config( 603 *args, configure='imply_option/infer.configure') 604 605 help, config = get_config(['--help']) 606 self.assertEquals(config, {}) 607 608 config = get_config([]) 609 self.assertEquals(config, {}) 610 611 with self.assertRaises(InvalidOptionError) as e: 612 get_config(['--enable-foo', '--disable-bar']) 613 614 self.assertEquals( 615 e.exception.message, 616 "'--enable-bar' implied by '--enable-foo' conflicts with " 617 "'--disable-bar' from the command-line") 618 619 with self.assertRaises(ConfigureError) as e: 620 self.get_config([], configure='imply_option/infer_ko.configure') 621 622 self.assertEquals( 623 e.exception.message, 624 "Cannot infer what implies '--enable-bar'. Please add a `reason` " 625 "to the `imply_option` call.") 626 627 def test_imply_option_immediate_value(self): 628 def get_config(*args): 629 return self.get_config( 630 *args, configure='imply_option/imm.configure') 631 632 help, config = get_config(['--help']) 633 self.assertEquals(config, {}) 634 635 config = get_config([]) 636 self.assertEquals(config, {}) 637 638 config_path = mozpath.abspath( 639 mozpath.join(test_data_path, 'imply_option', 'imm.configure')) 640 641 with self.assertRaisesRegexp(InvalidOptionError, 642 "--enable-foo' implied by 'imply_option at %s:7' conflicts with " 643 "'--disable-foo' from the command-line" % config_path): 644 get_config(['--disable-foo']) 645 646 with self.assertRaisesRegexp(InvalidOptionError, 647 "--enable-bar=foo,bar' implied by 'imply_option at %s:16' conflicts" 648 " with '--enable-bar=a,b,c' from the command-line" % config_path): 649 get_config(['--enable-bar=a,b,c']) 650 651 with self.assertRaisesRegexp(InvalidOptionError, 652 "--enable-baz=BAZ' implied by 'imply_option at %s:25' conflicts" 653 " with '--enable-baz=QUUX' from the command-line" % config_path): 654 get_config(['--enable-baz=QUUX']) 655 656 def test_imply_option_failures(self): 657 with self.assertRaises(ConfigureError) as e: 658 with self.moz_configure(''' 659 imply_option('--with-foo', ('a',), 'bar') 660 '''): 661 self.get_config() 662 663 self.assertEquals(e.exception.message, 664 "`--with-foo`, emitted from `%s` line 2, is unknown." 665 % mozpath.join(test_data_path, 'moz.configure')) 666 667 with self.assertRaises(TypeError) as e: 668 with self.moz_configure(''' 669 imply_option('--with-foo', 42, 'bar') 670 671 option('--with-foo', help='foo') 672 @depends('--with-foo') 673 def foo(value): 674 return value 675 '''): 676 self.get_config() 677 678 self.assertEquals(e.exception.message, 679 "Unexpected type: 'int'") 680 681 def test_imply_option_when(self): 682 with self.moz_configure(''' 683 option('--with-foo', help='foo') 684 imply_option('--with-qux', True, when='--with-foo') 685 option('--with-qux', help='qux') 686 set_config('QUX', depends('--with-qux')(lambda x: x)) 687 '''): 688 config = self.get_config() 689 self.assertEquals(config, { 690 'QUX': NegativeOptionValue(), 691 }) 692 693 config = self.get_config(['--with-foo']) 694 self.assertEquals(config, { 695 'QUX': PositiveOptionValue(), 696 }) 697 698 def test_option_failures(self): 699 with self.assertRaises(ConfigureError) as e: 700 with self.moz_configure('option("--with-foo", help="foo")'): 701 self.get_config() 702 703 self.assertEquals( 704 e.exception.message, 705 'Option `--with-foo` is not handled ; reference it with a @depends' 706 ) 707 708 with self.assertRaises(ConfigureError) as e: 709 with self.moz_configure(''' 710 option("--with-foo", help="foo") 711 option("--with-foo", help="foo") 712 '''): 713 self.get_config() 714 715 self.assertEquals( 716 e.exception.message, 717 'Option `--with-foo` already defined' 718 ) 719 720 with self.assertRaises(ConfigureError) as e: 721 with self.moz_configure(''' 722 option(env="MOZ_FOO", help="foo") 723 option(env="MOZ_FOO", help="foo") 724 '''): 725 self.get_config() 726 727 self.assertEquals( 728 e.exception.message, 729 'Option `MOZ_FOO` already defined' 730 ) 731 732 with self.assertRaises(ConfigureError) as e: 733 with self.moz_configure(''' 734 option('--with-foo', env="MOZ_FOO", help="foo") 735 option(env="MOZ_FOO", help="foo") 736 '''): 737 self.get_config() 738 739 self.assertEquals( 740 e.exception.message, 741 'Option `MOZ_FOO` already defined' 742 ) 743 744 with self.assertRaises(ConfigureError) as e: 745 with self.moz_configure(''' 746 option(env="MOZ_FOO", help="foo") 747 option('--with-foo', env="MOZ_FOO", help="foo") 748 '''): 749 self.get_config() 750 751 self.assertEquals( 752 e.exception.message, 753 'Option `MOZ_FOO` already defined' 754 ) 755 756 with self.assertRaises(ConfigureError) as e: 757 with self.moz_configure(''' 758 option('--with-foo', env="MOZ_FOO", help="foo") 759 option('--with-foo', help="foo") 760 '''): 761 self.get_config() 762 763 self.assertEquals( 764 e.exception.message, 765 'Option `--with-foo` already defined' 766 ) 767 768 def test_option_when(self): 769 with self.moz_configure(''' 770 option('--with-foo', help='foo', when=True) 771 option('--with-bar', help='bar', when=False) 772 option('--with-qux', env="QUX", help='qux', when='--with-foo') 773 774 set_config('FOO', depends('--with-foo', when=True)(lambda x: x)) 775 set_config('BAR', depends('--with-bar', when=False)(lambda x: x)) 776 set_config('QUX', depends('--with-qux', when='--with-foo')(lambda x: x)) 777 '''): 778 config = self.get_config() 779 self.assertEquals(config, { 780 'FOO': NegativeOptionValue(), 781 }) 782 783 config = self.get_config(['--with-foo']) 784 self.assertEquals(config, { 785 'FOO': PositiveOptionValue(), 786 'QUX': NegativeOptionValue(), 787 }) 788 789 config = self.get_config(['--with-foo', '--with-qux']) 790 self.assertEquals(config, { 791 'FOO': PositiveOptionValue(), 792 'QUX': PositiveOptionValue(), 793 }) 794 795 with self.assertRaises(InvalidOptionError) as e: 796 self.get_config(['--with-bar']) 797 798 self.assertEquals( 799 e.exception.message, 800 '--with-bar is not available in this configuration' 801 ) 802 803 with self.assertRaises(InvalidOptionError) as e: 804 self.get_config(['--with-qux']) 805 806 self.assertEquals( 807 e.exception.message, 808 '--with-qux is not available in this configuration' 809 ) 810 811 with self.assertRaises(InvalidOptionError) as e: 812 self.get_config(['QUX=1']) 813 814 self.assertEquals( 815 e.exception.message, 816 'QUX is not available in this configuration' 817 ) 818 819 config = self.get_config(env={'QUX': '1'}) 820 self.assertEquals(config, { 821 'FOO': NegativeOptionValue(), 822 }) 823 824 help, config = self.get_config(['--help']) 825 self.assertEquals(help, textwrap.dedent('''\ 826 Usage: configure [options] 827 828 Options: [defaults in brackets after descriptions] 829 --help print this message 830 --with-foo foo 831 832 Environment variables: 833 ''')) 834 835 help, config = self.get_config(['--help', '--with-foo']) 836 self.assertEquals(help, textwrap.dedent('''\ 837 Usage: configure [options] 838 839 Options: [defaults in brackets after descriptions] 840 --help print this message 841 --with-foo foo 842 --with-qux qux 843 844 Environment variables: 845 ''')) 846 847 with self.moz_configure(''' 848 option('--with-foo', help='foo', when=True) 849 set_config('FOO', depends('--with-foo')(lambda x: x)) 850 '''): 851 with self.assertRaises(ConfigureError) as e: 852 self.get_config() 853 854 self.assertEquals(e.exception.message, 855 '@depends function needs the same `when` as ' 856 'options it depends on') 857 858 with self.moz_configure(''' 859 @depends(when=True) 860 def always(): 861 return True 862 @depends(when=True) 863 def always2(): 864 return True 865 option('--with-foo', help='foo', when=always) 866 set_config('FOO', depends('--with-foo', when=always2)(lambda x: x)) 867 '''): 868 with self.assertRaises(ConfigureError) as e: 869 self.get_config() 870 871 self.assertEquals(e.exception.message, 872 '@depends function needs the same `when` as ' 873 'options it depends on') 874 875 with self.moz_configure(''' 876 @depends(when=True) 877 def always(): 878 return True 879 @depends(when=True) 880 def always2(): 881 return True 882 with only_when(always2): 883 option('--with-foo', help='foo', when=always) 884 # include() triggers resolution of its dependencies, and their 885 # side effects. 886 include(depends('--with-foo', when=always)(lambda x: x)) 887 # The sandbox should figure that the `when` here is 888 # appropriate. Bad behavior in CombinedDependsFunction.__eq__ 889 # made this fail in the past. 890 set_config('FOO', depends('--with-foo', when=always)(lambda x: x)) 891 '''): 892 self.get_config() 893 894 def test_include_failures(self): 895 with self.assertRaises(ConfigureError) as e: 896 with self.moz_configure('include("../foo.configure")'): 897 self.get_config() 898 899 self.assertEquals( 900 e.exception.message, 901 'Cannot include `%s` because it is not in a subdirectory of `%s`' 902 % (mozpath.normpath(mozpath.join(test_data_path, '..', 903 'foo.configure')), 904 mozpath.normsep(test_data_path)) 905 ) 906 907 with self.assertRaises(ConfigureError) as e: 908 with self.moz_configure(''' 909 include('extra.configure') 910 include('extra.configure') 911 '''): 912 self.get_config() 913 914 self.assertEquals( 915 e.exception.message, 916 'Cannot include `%s` because it was included already.' 917 % mozpath.normpath(mozpath.join(test_data_path, 918 'extra.configure')) 919 ) 920 921 with self.assertRaises(TypeError) as e: 922 with self.moz_configure(''' 923 include(42) 924 '''): 925 self.get_config() 926 927 self.assertEquals(e.exception.message, "Unexpected type: 'int'") 928 929 def test_include_when(self): 930 with MockedOpen({ 931 os.path.join(test_data_path, 'moz.configure'): textwrap.dedent(''' 932 option('--with-foo', help='foo') 933 934 include('always.configure', when=True) 935 include('never.configure', when=False) 936 include('foo.configure', when='--with-foo') 937 938 set_config('FOO', foo) 939 set_config('BAR', bar) 940 set_config('QUX', qux) 941 '''), 942 os.path.join(test_data_path, 'always.configure'): textwrap.dedent(''' 943 option('--with-bar', help='bar') 944 @depends('--with-bar') 945 def bar(x): 946 if x: 947 return 'bar' 948 '''), 949 os.path.join(test_data_path, 'never.configure'): textwrap.dedent(''' 950 option('--with-qux', help='qux') 951 @depends('--with-qux') 952 def qux(x): 953 if x: 954 return 'qux' 955 '''), 956 os.path.join(test_data_path, 'foo.configure'): textwrap.dedent(''' 957 option('--with-foo-really', help='really foo') 958 @depends('--with-foo-really') 959 def foo(x): 960 if x: 961 return 'foo' 962 963 include('foo2.configure', when='--with-foo-really') 964 '''), 965 os.path.join(test_data_path, 'foo2.configure'): textwrap.dedent(''' 966 set_config('FOO2', True) 967 '''), 968 }): 969 config = self.get_config() 970 self.assertEquals(config, {}) 971 972 config = self.get_config(['--with-foo']) 973 self.assertEquals(config, {}) 974 975 config = self.get_config(['--with-bar']) 976 self.assertEquals(config, { 977 'BAR': 'bar', 978 }) 979 980 with self.assertRaises(InvalidOptionError) as e: 981 self.get_config(['--with-qux']) 982 983 self.assertEquals( 984 e.exception.message, 985 '--with-qux is not available in this configuration' 986 ) 987 988 config = self.get_config(['--with-foo', '--with-foo-really']) 989 self.assertEquals(config, { 990 'FOO': 'foo', 991 'FOO2': True, 992 }) 993 994 def test_sandbox_failures(self): 995 with self.assertRaises(KeyError) as e: 996 with self.moz_configure(''' 997 include = 42 998 '''): 999 self.get_config() 1000 1001 self.assertEquals(e.exception.message, 'Cannot reassign builtins') 1002 1003 with self.assertRaises(KeyError) as e: 1004 with self.moz_configure(''' 1005 foo = 42 1006 '''): 1007 self.get_config() 1008 1009 self.assertEquals(e.exception.message, 1010 'Cannot assign `foo` because it is neither a ' 1011 '@depends nor a @template') 1012 1013 def test_depends_failures(self): 1014 with self.assertRaises(ConfigureError) as e: 1015 with self.moz_configure(''' 1016 @depends() 1017 def foo(): 1018 return 1019 '''): 1020 self.get_config() 1021 1022 self.assertEquals(e.exception.message, 1023 "@depends needs at least one argument") 1024 1025 with self.assertRaises(ConfigureError) as e: 1026 with self.moz_configure(''' 1027 @depends('--with-foo') 1028 def foo(value): 1029 return value 1030 '''): 1031 self.get_config() 1032 1033 self.assertEquals(e.exception.message, 1034 "'--with-foo' is not a known option. Maybe it's " 1035 "declared too late?") 1036 1037 with self.assertRaises(ConfigureError) as e: 1038 with self.moz_configure(''' 1039 @depends('--with-foo=42') 1040 def foo(value): 1041 return value 1042 '''): 1043 self.get_config() 1044 1045 self.assertEquals(e.exception.message, 1046 "Option must not contain an '='") 1047 1048 with self.assertRaises(TypeError) as e: 1049 with self.moz_configure(''' 1050 @depends(42) 1051 def foo(value): 1052 return value 1053 '''): 1054 self.get_config() 1055 1056 self.assertEquals(e.exception.message, 1057 "Cannot use object of type 'int' as argument " 1058 "to @depends") 1059 1060 with self.assertRaises(ConfigureError) as e: 1061 with self.moz_configure(''' 1062 @depends('--help') 1063 def foo(value): 1064 yield 1065 '''): 1066 self.get_config() 1067 1068 self.assertEquals(e.exception.message, 1069 "Cannot decorate generator functions with @depends") 1070 1071 with self.assertRaises(TypeError) as e: 1072 with self.moz_configure(''' 1073 depends('--help')(42) 1074 '''): 1075 self.get_config() 1076 1077 self.assertEquals(e.exception.message, 1078 "Unexpected type: 'int'") 1079 1080 with self.assertRaises(ConfigureError) as e: 1081 with self.moz_configure(''' 1082 option('--foo', help='foo') 1083 @depends('--foo') 1084 def foo(value): 1085 return value 1086 1087 foo() 1088 '''): 1089 self.get_config() 1090 1091 self.assertEquals(e.exception.message, 1092 "The `foo` function may not be called") 1093 1094 with self.assertRaises(TypeError) as e: 1095 with self.moz_configure(''' 1096 @depends('--help', foo=42) 1097 def foo(_): 1098 return 1099 '''): 1100 self.get_config() 1101 1102 self.assertEquals(e.exception.message, 1103 "depends_impl() got an unexpected keyword argument 'foo'") 1104 1105 def test_depends_when(self): 1106 with self.moz_configure(''' 1107 @depends(when=True) 1108 def foo(): 1109 return 'foo' 1110 1111 set_config('FOO', foo) 1112 1113 @depends(when=False) 1114 def bar(): 1115 return 'bar' 1116 1117 set_config('BAR', bar) 1118 1119 option('--with-qux', help='qux') 1120 @depends(when='--with-qux') 1121 def qux(): 1122 return 'qux' 1123 1124 set_config('QUX', qux) 1125 '''): 1126 config = self.get_config() 1127 self.assertEquals(config, { 1128 'FOO': 'foo', 1129 }) 1130 1131 config = self.get_config(['--with-qux']) 1132 self.assertEquals(config, { 1133 'FOO': 'foo', 1134 'QUX': 'qux', 1135 }) 1136 1137 def test_imports_failures(self): 1138 with self.assertRaises(ConfigureError) as e: 1139 with self.moz_configure(''' 1140 @imports('os') 1141 @template 1142 def foo(value): 1143 return value 1144 '''): 1145 self.get_config() 1146 1147 self.assertEquals(e.exception.message, 1148 '@imports must appear after @template') 1149 1150 with self.assertRaises(ConfigureError) as e: 1151 with self.moz_configure(''' 1152 option('--foo', help='foo') 1153 @imports('os') 1154 @depends('--foo') 1155 def foo(value): 1156 return value 1157 '''): 1158 self.get_config() 1159 1160 self.assertEquals(e.exception.message, 1161 '@imports must appear after @depends') 1162 1163 for import_ in ( 1164 "42", 1165 "_from=42, _import='os'", 1166 "_from='os', _import='path', _as=42", 1167 ): 1168 with self.assertRaises(TypeError) as e: 1169 with self.moz_configure(''' 1170 @imports(%s) 1171 @template 1172 def foo(value): 1173 return value 1174 ''' % import_): 1175 self.get_config() 1176 1177 self.assertEquals(e.exception.message, "Unexpected type: 'int'") 1178 1179 with self.assertRaises(TypeError) as e: 1180 with self.moz_configure(''' 1181 @imports('os', 42) 1182 @template 1183 def foo(value): 1184 return value 1185 '''): 1186 self.get_config() 1187 1188 self.assertEquals(e.exception.message, "Unexpected type: 'int'") 1189 1190 with self.assertRaises(ValueError) as e: 1191 with self.moz_configure(''' 1192 @imports('os*') 1193 def foo(value): 1194 return value 1195 '''): 1196 self.get_config() 1197 1198 self.assertEquals(e.exception.message, 1199 "Invalid argument to @imports: 'os*'") 1200 1201 def test_only_when(self): 1202 moz_configure = ''' 1203 option('--enable-when', help='when') 1204 @depends('--enable-when', '--help') 1205 def when(value, _): 1206 return bool(value) 1207 1208 with only_when(when): 1209 option('--foo', nargs='*', help='foo') 1210 @depends('--foo') 1211 def foo(value): 1212 return value 1213 1214 set_config('FOO', foo) 1215 set_define('FOO', foo) 1216 1217 # It is possible to depend on a function defined in a only_when 1218 # block. It then resolves to `None`. 1219 set_config('BAR', depends(foo)(lambda x: x)) 1220 set_define('BAR', depends(foo)(lambda x: x)) 1221 ''' 1222 1223 with self.moz_configure(moz_configure): 1224 config = self.get_config() 1225 self.assertEqual(config, { 1226 'DEFINES': {}, 1227 }) 1228 1229 config = self.get_config(['--enable-when']) 1230 self.assertEqual(config, { 1231 'BAR': NegativeOptionValue(), 1232 'FOO': NegativeOptionValue(), 1233 'DEFINES': { 1234 'BAR': NegativeOptionValue(), 1235 'FOO': NegativeOptionValue(), 1236 }, 1237 }) 1238 1239 config = self.get_config(['--enable-when', '--foo=bar']) 1240 self.assertEqual(config, { 1241 'BAR': PositiveOptionValue(['bar']), 1242 'FOO': PositiveOptionValue(['bar']), 1243 'DEFINES': { 1244 'BAR': PositiveOptionValue(['bar']), 1245 'FOO': PositiveOptionValue(['bar']), 1246 }, 1247 }) 1248 1249 # The --foo option doesn't exist when --enable-when is not given. 1250 with self.assertRaises(InvalidOptionError) as e: 1251 self.get_config(['--foo']) 1252 1253 self.assertEquals(e.exception.message, 1254 '--foo is not available in this configuration') 1255 1256 # Cannot depend on an option defined in a only_when block, because we 1257 # don't know what OptionValue would make sense. 1258 with self.moz_configure(moz_configure + ''' 1259 set_config('QUX', depends('--foo')(lambda x: x)) 1260 '''): 1261 with self.assertRaises(ConfigureError) as e: 1262 self.get_config() 1263 1264 self.assertEquals(e.exception.message, 1265 '@depends function needs the same `when` as ' 1266 'options it depends on') 1267 1268 with self.moz_configure(moz_configure + ''' 1269 set_config('QUX', depends('--foo', when=when)(lambda x: x)) 1270 '''): 1271 self.get_config(['--enable-when']) 1272 1273 # Using imply_option for an option defined in a only_when block fails 1274 # similarly if the imply_option happens outside the block. 1275 with self.moz_configure(''' 1276 imply_option('--foo', True) 1277 ''' + moz_configure): 1278 with self.assertRaises(InvalidOptionError) as e: 1279 self.get_config() 1280 1281 self.assertEquals(e.exception.message, 1282 '--foo is not available in this configuration') 1283 1284 # And similarly doesn't fail when the condition is true. 1285 with self.moz_configure(''' 1286 imply_option('--foo', True) 1287 ''' + moz_configure): 1288 self.get_config(['--enable-when']) 1289 1290 def test_depends_binary_ops(self): 1291 with self.moz_configure(''' 1292 option('--foo', nargs=1, help='foo') 1293 @depends('--foo') 1294 def foo(value): 1295 return value or 0 1296 1297 option('--bar', nargs=1, help='bar') 1298 @depends('--bar') 1299 def bar(value): 1300 return value or '' 1301 1302 option('--baz', nargs=1, help='baz') 1303 @depends('--baz') 1304 def baz(value): 1305 return value 1306 1307 set_config('FOOorBAR', foo | bar) 1308 set_config('FOOorBARorBAZ', foo | bar | baz) 1309 set_config('FOOandBAR', foo & bar) 1310 set_config('FOOandBARandBAZ', foo & bar & baz) 1311 '''): 1312 for foo_opt, foo_value in ( 1313 ('', 0), 1314 ('--foo=foo', PositiveOptionValue(('foo',))) 1315 ): 1316 for bar_opt, bar_value in ( 1317 ('', ''), 1318 ('--bar=bar', PositiveOptionValue(('bar',))) 1319 ): 1320 for baz_opt, baz_value in ( 1321 ('', NegativeOptionValue()), 1322 ('--baz=baz', PositiveOptionValue(('baz',))) 1323 ): 1324 config = self.get_config( 1325 [x for x in (foo_opt, bar_opt, baz_opt) if x]) 1326 self.assertEqual(config, { 1327 'FOOorBAR': foo_value or bar_value, 1328 'FOOorBARorBAZ': foo_value or bar_value or baz_value, 1329 'FOOandBAR': foo_value and bar_value, 1330 'FOOandBARandBAZ': foo_value and bar_value and baz_value, 1331 }) 1332 1333 def test_depends_getattr(self): 1334 with self.moz_configure(''' 1335 @imports(_from='mozbuild.util', _import='ReadOnlyNamespace') 1336 def namespace(**kwargs): 1337 return ReadOnlyNamespace(**kwargs) 1338 1339 option('--foo', nargs=1, help='foo') 1340 @depends('--foo') 1341 def foo(value): 1342 return value 1343 1344 option('--bar', nargs=1, help='bar') 1345 @depends('--bar') 1346 def bar(value): 1347 return value or None 1348 1349 @depends(foo, bar) 1350 def foobar(foo, bar): 1351 return namespace(foo=foo, bar=bar) 1352 1353 set_config('FOO', foobar.foo) 1354 set_config('BAR', foobar.bar) 1355 set_config('BAZ', foobar.baz) 1356 '''): 1357 config = self.get_config() 1358 self.assertEqual(config, { 1359 'FOO': NegativeOptionValue(), 1360 }) 1361 1362 config = self.get_config(['--foo=foo']) 1363 self.assertEqual(config, { 1364 'FOO': PositiveOptionValue(('foo',)), 1365 }) 1366 1367 config = self.get_config(['--bar=bar']) 1368 self.assertEqual(config, { 1369 'FOO': NegativeOptionValue(), 1370 'BAR': PositiveOptionValue(('bar',)), 1371 }) 1372 1373 config = self.get_config(['--foo=foo', '--bar=bar']) 1374 self.assertEqual(config, { 1375 'FOO': PositiveOptionValue(('foo',)), 1376 'BAR': PositiveOptionValue(('bar',)), 1377 }) 1378 1379 1380if __name__ == '__main__': 1381 main() 1382