test_checks_configure.py (41995B)
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 5 import os 6 import sys 7 import textwrap 8 import unittest 9 from io import StringIO 10 11 from buildconfig import topsrcdir 12 from mozpack import path as mozpath 13 from mozshellutil import quote as shell_quote 14 from mozunit import MockedOpen, main 15 16 from common import ConfigureTestSandbox, ensure_exe_extension, fake_short_path 17 from mozbuild.configure import ConfigureError, ConfigureSandbox 18 19 20 class TestChecksConfigure(unittest.TestCase): 21 def test_checking(self): 22 def make_test(to_exec): 23 def test(val, msg): 24 out = StringIO() 25 sandbox = ConfigureSandbox({}, stdout=out, stderr=out) 26 base_dir = os.path.join(topsrcdir, "build", "moz.configure") 27 sandbox.include_file(os.path.join(base_dir, "checks.configure")) 28 exec(to_exec, sandbox) 29 sandbox["foo"](val) 30 self.assertEqual(out.getvalue(), msg) 31 32 return test 33 34 test = make_test( 35 textwrap.dedent( 36 """ 37 @checking('for a thing') 38 def foo(value): 39 return value 40 """ 41 ) 42 ) 43 test(True, "checking for a thing... yes\n") 44 test(False, "checking for a thing... no\n") 45 test(42, "checking for a thing... 42\n") 46 test("foo", "checking for a thing... foo\n") 47 data = ["foo", "bar"] 48 test(data, "checking for a thing... %r\n" % data) 49 50 # When the function given to checking does nothing interesting, the 51 # behavior is not altered 52 test = make_test( 53 textwrap.dedent( 54 """ 55 @checking('for a thing', lambda x: x) 56 def foo(value): 57 return value 58 """ 59 ) 60 ) 61 test(True, "checking for a thing... yes\n") 62 test(False, "checking for a thing... no\n") 63 test(42, "checking for a thing... 42\n") 64 test("foo", "checking for a thing... foo\n") 65 data = ["foo", "bar"] 66 test(data, "checking for a thing... %r\n" % data) 67 68 test = make_test( 69 textwrap.dedent( 70 """ 71 def munge(x): 72 if not x: 73 return 'not found' 74 if isinstance(x, (str, bool, int)): 75 return x 76 return ' '.join(x) 77 78 @checking('for a thing', munge) 79 def foo(value): 80 return value 81 """ 82 ) 83 ) 84 test(True, "checking for a thing... yes\n") 85 test(False, "checking for a thing... not found\n") 86 test(42, "checking for a thing... 42\n") 87 test("foo", "checking for a thing... foo\n") 88 data = ["foo", "bar"] 89 test(data, "checking for a thing... foo bar\n") 90 91 KNOWN_A = ensure_exe_extension(mozpath.abspath("/usr/bin/known-a")) 92 KNOWN_B = ensure_exe_extension(mozpath.abspath("/usr/local/bin/known-b")) 93 KNOWN_C = ensure_exe_extension(mozpath.abspath("/home/user/bin/known c")) 94 OTHER_A = ensure_exe_extension(mozpath.abspath("/lib/other/known-a")) 95 96 def get_result( 97 self, 98 command="", 99 args=[], 100 environ={}, 101 prog="/bin/configure", 102 extra_paths=None, 103 includes=("util.configure", "checks.configure"), 104 ): 105 config = {} 106 out = StringIO() 107 paths = {self.KNOWN_A: None, self.KNOWN_B: None, self.KNOWN_C: None} 108 if extra_paths: 109 paths.update(extra_paths) 110 environ = dict(environ) 111 if "PATH" not in environ: 112 environ["PATH"] = os.pathsep.join(os.path.dirname(p) for p in paths) 113 paths[self.OTHER_A] = None 114 sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args, out, out) 115 base_dir = os.path.join(topsrcdir, "build", "moz.configure") 116 for f in includes: 117 sandbox.include_file(os.path.join(base_dir, f)) 118 119 status = 0 120 try: 121 exec(command, sandbox) 122 sandbox.run() 123 except SystemExit as e: 124 status = e.code 125 126 return config, out.getvalue(), status 127 128 def test_check_prog(self): 129 config, out, status = self.get_result('check_prog("FOO", ("known-a",))') 130 self.assertEqual(status, 0) 131 self.assertEqual(config, {"FOO": self.KNOWN_A}) 132 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A) 133 134 config, out, status = self.get_result( 135 'check_prog("FOO", ("unknown", "known-b", "known c"))' 136 ) 137 self.assertEqual(status, 0) 138 self.assertEqual(config, {"FOO": self.KNOWN_B}) 139 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_B) 140 141 config, out, status = self.get_result( 142 'check_prog("FOO", ("unknown", "unknown-2", "known c"))' 143 ) 144 self.assertEqual(status, 0) 145 self.assertEqual(config, {"FOO": fake_short_path(self.KNOWN_C)}) 146 self.assertEqual( 147 out, "checking for foo... %s\n" % shell_quote(fake_short_path(self.KNOWN_C)) 148 ) 149 150 config, out, status = self.get_result('check_prog("FOO", ("unknown",))') 151 self.assertEqual(status, 1) 152 self.assertEqual(config, {}) 153 self.assertEqual( 154 out, 155 textwrap.dedent( 156 """\ 157 checking for foo... not found 158 DEBUG: foo: Looking for unknown 159 ERROR: Cannot find foo 160 """ 161 ), 162 ) 163 164 config, out, status = self.get_result( 165 'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"))' 166 ) 167 self.assertEqual(status, 1) 168 self.assertEqual(config, {}) 169 self.assertEqual( 170 out, 171 textwrap.dedent( 172 """\ 173 checking for foo... not found 174 DEBUG: foo: Looking for unknown 175 DEBUG: foo: Looking for unknown-2 176 DEBUG: foo: Looking for 'unknown 3' 177 ERROR: Cannot find foo 178 """ 179 ), 180 ) 181 182 config, out, status = self.get_result( 183 'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), ' 184 "allow_missing=True)" 185 ) 186 self.assertEqual(status, 0) 187 self.assertEqual(config, {}) 188 self.assertEqual(out, "checking for foo... not found\n") 189 190 @unittest.skipIf(not sys.platform.startswith("win"), "Windows-only test") 191 def test_check_prog_exe(self): 192 config, out, status = self.get_result( 193 'check_prog("FOO", ("unknown", "known-b", "known c"))', ["FOO=known-a.exe"] 194 ) 195 self.assertEqual(status, 0) 196 self.assertEqual(config, {"FOO": self.KNOWN_A}) 197 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A) 198 199 config, out, status = self.get_result( 200 'check_prog("FOO", ("unknown", "known-b", "known c"))', 201 ["FOO=%s" % os.path.splitext(self.KNOWN_A)[0]], 202 ) 203 self.assertEqual(status, 0) 204 self.assertEqual(config, {"FOO": self.KNOWN_A}) 205 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A) 206 207 def test_check_prog_with_args(self): 208 config, out, status = self.get_result( 209 'check_prog("FOO", ("unknown", "known-b", "known c"))', ["FOO=known-a"] 210 ) 211 self.assertEqual(status, 0) 212 self.assertEqual(config, {"FOO": self.KNOWN_A}) 213 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A) 214 215 config, out, status = self.get_result( 216 'check_prog("FOO", ("unknown", "known-b", "known c"))', 217 ["FOO=%s" % self.KNOWN_A], 218 ) 219 self.assertEqual(status, 0) 220 self.assertEqual(config, {"FOO": self.KNOWN_A}) 221 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A) 222 223 path = self.KNOWN_B.replace("known-b", "known-a") 224 config, out, status = self.get_result( 225 'check_prog("FOO", ("unknown", "known-b", "known c"))', ["FOO=%s" % path] 226 ) 227 self.assertEqual(status, 1) 228 self.assertEqual(config, {}) 229 self.assertEqual( 230 out, 231 textwrap.dedent( 232 """\ 233 checking for foo... not found 234 DEBUG: foo: Looking for %s 235 ERROR: Cannot find foo 236 """ 237 ) 238 % path, 239 ) 240 241 config, out, status = self.get_result( 242 'check_prog("FOO", ("unknown",))', ["FOO=known c"] 243 ) 244 self.assertEqual(status, 0) 245 self.assertEqual(config, {"FOO": fake_short_path(self.KNOWN_C)}) 246 self.assertEqual( 247 out, "checking for foo... %s\n" % shell_quote(fake_short_path(self.KNOWN_C)) 248 ) 249 250 config, out, status = self.get_result( 251 'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), ' 252 "allow_missing=True)", 253 ["FOO=unknown"], 254 ) 255 self.assertEqual(status, 1) 256 self.assertEqual(config, {}) 257 self.assertEqual( 258 out, 259 textwrap.dedent( 260 """\ 261 checking for foo... not found 262 DEBUG: foo: Looking for unknown 263 ERROR: Cannot find foo 264 """ 265 ), 266 ) 267 268 def test_check_prog_what(self): 269 config, out, status = self.get_result( 270 'check_prog("CC", ("known-a",), what="the target C compiler")' 271 ) 272 self.assertEqual(status, 0) 273 self.assertEqual(config, {"CC": self.KNOWN_A}) 274 self.assertEqual( 275 out, "checking for the target C compiler... %s\n" % self.KNOWN_A 276 ) 277 278 config, out, status = self.get_result( 279 'check_prog("CC", ("unknown", "unknown-2", "unknown 3"),' 280 ' what="the target C compiler")' 281 ) 282 self.assertEqual(status, 1) 283 self.assertEqual(config, {}) 284 self.assertEqual( 285 out, 286 textwrap.dedent( 287 """\ 288 checking for the target C compiler... not found 289 DEBUG: cc: Looking for unknown 290 DEBUG: cc: Looking for unknown-2 291 DEBUG: cc: Looking for 'unknown 3' 292 ERROR: Cannot find the target C compiler 293 """ 294 ), 295 ) 296 297 def test_check_prog_input(self): 298 config, out, status = self.get_result( 299 textwrap.dedent( 300 """ 301 option("--with-ccache", nargs=1, help="Ccache") 302 check_prog("CCACHE", ("known-a",), input="--with-ccache") 303 """ 304 ), 305 ["--with-ccache=known-b"], 306 ) 307 self.assertEqual(status, 0) 308 self.assertEqual(config, {"CCACHE": self.KNOWN_B}) 309 self.assertEqual(out, "checking for ccache... %s\n" % self.KNOWN_B) 310 311 script = textwrap.dedent( 312 """ 313 option(env="CC", nargs=1, help="Compiler") 314 @depends("CC") 315 def compiler(value): 316 return value[0].split()[0] if value else None 317 check_prog("CC", ("known-a",), input=compiler) 318 """ 319 ) 320 config, out, status = self.get_result(script) 321 self.assertEqual(status, 0) 322 self.assertEqual(config, {"CC": self.KNOWN_A}) 323 self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A) 324 325 config, out, status = self.get_result(script, ["CC=known-b"]) 326 self.assertEqual(status, 0) 327 self.assertEqual(config, {"CC": self.KNOWN_B}) 328 self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_B) 329 330 config, out, status = self.get_result(script, ["CC=known-b -m32"]) 331 self.assertEqual(status, 0) 332 self.assertEqual(config, {"CC": self.KNOWN_B}) 333 self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_B) 334 335 def test_check_prog_progs(self): 336 config, out, status = self.get_result('check_prog("FOO", ())') 337 self.assertEqual(status, 0) 338 self.assertEqual(config, {}) 339 self.assertEqual(out, "") 340 341 config, out, status = self.get_result('check_prog("FOO", ())', ["FOO=known-a"]) 342 self.assertEqual(status, 0) 343 self.assertEqual(config, {"FOO": self.KNOWN_A}) 344 self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A) 345 346 script = textwrap.dedent( 347 """ 348 option(env="TARGET", nargs=1, default="linux", help="Target") 349 @depends("TARGET") 350 def compiler(value): 351 if value: 352 if value[0] == "linux": 353 return ("gcc", "clang") 354 if value[0] == "winnt": 355 return ("cl", "clang-cl") 356 check_prog("CC", compiler) 357 """ 358 ) 359 config, out, status = self.get_result(script) 360 self.assertEqual(status, 1) 361 self.assertEqual(config, {}) 362 self.assertEqual( 363 out, 364 textwrap.dedent( 365 """\ 366 checking for cc... not found 367 DEBUG: cc: Looking for gcc 368 DEBUG: cc: Looking for clang 369 ERROR: Cannot find cc 370 """ 371 ), 372 ) 373 374 config, out, status = self.get_result(script, ["TARGET=linux"]) 375 self.assertEqual(status, 1) 376 self.assertEqual(config, {}) 377 self.assertEqual( 378 out, 379 textwrap.dedent( 380 """\ 381 checking for cc... not found 382 DEBUG: cc: Looking for gcc 383 DEBUG: cc: Looking for clang 384 ERROR: Cannot find cc 385 """ 386 ), 387 ) 388 389 config, out, status = self.get_result(script, ["TARGET=winnt"]) 390 self.assertEqual(status, 1) 391 self.assertEqual(config, {}) 392 self.assertEqual( 393 out, 394 textwrap.dedent( 395 """\ 396 checking for cc... not found 397 DEBUG: cc: Looking for cl 398 DEBUG: cc: Looking for clang-cl 399 ERROR: Cannot find cc 400 """ 401 ), 402 ) 403 404 config, out, status = self.get_result(script, ["TARGET=none"]) 405 self.assertEqual(status, 0) 406 self.assertEqual(config, {}) 407 self.assertEqual(out, "") 408 409 config, out, status = self.get_result(script, ["TARGET=winnt", "CC=known-a"]) 410 self.assertEqual(status, 0) 411 self.assertEqual(config, {"CC": self.KNOWN_A}) 412 self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A) 413 414 config, out, status = self.get_result(script, ["TARGET=none", "CC=known-a"]) 415 self.assertEqual(status, 0) 416 self.assertEqual(config, {"CC": self.KNOWN_A}) 417 self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A) 418 419 def test_check_prog_configure_error(self): 420 with self.assertRaises(ConfigureError) as e: 421 self.get_result('check_prog("FOO", "foo")') 422 423 self.assertEqual(str(e.exception), "progs must resolve to a list or tuple!") 424 425 with self.assertRaises(ConfigureError) as e: 426 self.get_result( 427 'foo = depends(when=True)(lambda: ("a", "b"))\n' 428 'check_prog("FOO", ("known-a",), input=foo)' 429 ) 430 431 self.assertEqual( 432 str(e.exception), 433 "input must resolve to a tuple or a list with a " 434 "single element, or a string", 435 ) 436 437 with self.assertRaises(ConfigureError) as e: 438 self.get_result( 439 'foo = depends(when=True)(lambda: {"a": "b"})\n' 440 'check_prog("FOO", ("known-a",), input=foo)' 441 ) 442 443 self.assertEqual( 444 str(e.exception), 445 "input must resolve to a tuple or a list with a " 446 "single element, or a string", 447 ) 448 449 def test_check_prog_with_path(self): 450 config, out, status = self.get_result( 451 'check_prog("A", ("known-a",), paths=["/some/path"])' 452 ) 453 self.assertEqual(status, 1) 454 self.assertEqual(config, {}) 455 self.assertEqual( 456 out, 457 textwrap.dedent( 458 """\ 459 checking for a... not found 460 DEBUG: a: Looking for known-a 461 ERROR: Cannot find a 462 """ 463 ), 464 ) 465 466 config, out, status = self.get_result( 467 'check_prog("A", ("known-a",), paths=["%s"])' 468 % os.path.dirname(self.OTHER_A) 469 ) 470 self.assertEqual(status, 0) 471 self.assertEqual(config, {"A": self.OTHER_A}) 472 self.assertEqual( 473 out, 474 textwrap.dedent( 475 """\ 476 checking for a... %s 477 """ 478 % self.OTHER_A 479 ), 480 ) 481 482 dirs = map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A)) 483 config, out, status = self.get_result( 484 textwrap.dedent( 485 """\ 486 check_prog("A", ("known-a",), paths=["%s"]) 487 """ 488 % os.pathsep.join(dirs) 489 ) 490 ) 491 self.assertEqual(status, 0) 492 self.assertEqual(config, {"A": self.OTHER_A}) 493 self.assertEqual( 494 out, 495 textwrap.dedent( 496 """\ 497 checking for a... %s 498 """ 499 % self.OTHER_A 500 ), 501 ) 502 503 dirs = map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B)) 504 config, out, status = self.get_result( 505 textwrap.dedent( 506 """\ 507 check_prog("A", ("known-a",), paths=["%s", "%s"]) 508 """ 509 % (os.pathsep.join(dirs), self.OTHER_A) 510 ) 511 ) 512 self.assertEqual(status, 0) 513 self.assertEqual(config, {"A": self.KNOWN_A}) 514 self.assertEqual( 515 out, 516 textwrap.dedent( 517 """\ 518 checking for a... %s 519 """ 520 % self.KNOWN_A 521 ), 522 ) 523 524 config, out, status = self.get_result( 525 'check_prog("A", ("known-a",), paths="%s")' % os.path.dirname(self.OTHER_A) 526 ) 527 528 self.assertEqual(status, 1) 529 self.assertEqual(config, {}) 530 self.assertEqual( 531 out, 532 textwrap.dedent( 533 """\ 534 checking for a... """ # noqa # trailing whitespace... 535 """ 536 DEBUG: a: Looking for known-a 537 ERROR: Paths provided to find_program must be a list of strings, not %r 538 """ % mozpath.dirname(self.OTHER_A) 539 ), 540 ) 541 542 @unittest.skipIf( 543 not sys.platform.startswith("linux"), 544 "Linux-only test, assumes Java is located from a $PATH", 545 ) 546 def test_java_tool_checks_linux(self): 547 def run_configure_java( 548 mock_fs_paths, mock_java_home=None, mock_path=None, args=[] 549 ): 550 script = textwrap.dedent( 551 """\ 552 @depends('--help') 553 def host(_): 554 return namespace(os='unknown', kernel='unknown') 555 toolchains_base_dir = depends(when=True)(lambda: '/mozbuild') 556 want_bootstrap = dependable(lambda: lambda _: False) 557 include('%(topsrcdir)s/build/moz.configure/java.configure') 558 """ 559 % {"topsrcdir": topsrcdir} 560 ) 561 562 # Don't let system JAVA_HOME influence the test 563 original_java_home = os.environ.pop("JAVA_HOME", None) 564 configure_environ = {} 565 566 if mock_java_home: 567 os.environ["JAVA_HOME"] = mock_java_home 568 configure_environ["JAVA_HOME"] = mock_java_home 569 570 if mock_path: 571 configure_environ["PATH"] = mock_path 572 573 # * Even if the real file sysphabtem has a symlink at the mocked path, don't let 574 # realpath follow it, as it may influence the test. 575 # * When finding a binary, check the mock paths rather than the real filesystem. 576 # Note: Python doesn't allow the different "with" bits to be put in parenthesis, 577 # because then it thinks it's an un-with-able tuple. Additionally, if this is cleanly 578 # lined up with "\", black removes them and autoformats them to the block that is 579 # below. 580 result = self.get_result( 581 args=args, 582 command=script, 583 extra_paths=paths, 584 environ=configure_environ, 585 ) 586 587 if original_java_home: 588 os.environ["JAVA_HOME"] = original_java_home 589 return result 590 591 java = mozpath.abspath("/usr/bin/java") 592 javac = mozpath.abspath("/usr/bin/javac") 593 paths = {java: None, javac: None} 594 expected_error_message = ( 595 "ERROR: Could not locate Java at /mozbuild/jdk/jdk-17.0.17+10/bin, " 596 "please run ./mach bootstrap --no-system-changes\n" 597 ) 598 599 config, out, status = run_configure_java(paths) 600 self.assertEqual(status, 1) 601 self.assertEqual(config, {}) 602 self.assertEqual(out, expected_error_message) 603 604 # An alternative valid set of tools referred to by JAVA_HOME. 605 alt_java = mozpath.abspath("/usr/local/bin/java") 606 alt_javac = mozpath.abspath("/usr/local/bin/javac") 607 alt_java_home = mozpath.dirname(mozpath.dirname(alt_java)) 608 paths = {alt_java: None, alt_javac: None, java: None, javac: None} 609 610 alt_path = mozpath.dirname(java) 611 config, out, status = run_configure_java(paths, alt_java_home, alt_path) 612 self.assertEqual(status, 1) 613 self.assertEqual(config, {}) 614 self.assertEqual(out, expected_error_message) 615 616 # We can use --with-java-bin-path instead of JAVA_HOME to similar 617 # effect. 618 config, out, status = run_configure_java( 619 paths, 620 mock_path=mozpath.dirname(java), 621 args=["--with-java-bin-path=%s" % mozpath.dirname(alt_java)], 622 ) 623 self.assertEqual(status, 0) 624 self.assertEqual(config, {"JAVA": alt_java, "MOZ_JAVA_CODE_COVERAGE": False}) 625 self.assertEqual( 626 out, 627 textwrap.dedent( 628 """\ 629 checking for java... %s 630 """ 631 % alt_java 632 ), 633 ) 634 635 # If --with-java-bin-path and JAVA_HOME are both set, 636 # --with-java-bin-path takes precedence. 637 config, out, status = run_configure_java( 638 paths, 639 mock_java_home=mozpath.dirname(mozpath.dirname(java)), 640 mock_path=mozpath.dirname(java), 641 args=["--with-java-bin-path=%s" % mozpath.dirname(alt_java)], 642 ) 643 self.assertEqual(status, 0) 644 self.assertEqual(config, {"JAVA": alt_java, "MOZ_JAVA_CODE_COVERAGE": False}) 645 self.assertEqual( 646 out, 647 textwrap.dedent( 648 """\ 649 checking for java... %s 650 """ 651 % alt_java 652 ), 653 ) 654 655 # --enable-java-coverage should set MOZ_JAVA_CODE_COVERAGE. 656 alt_java_home = mozpath.dirname(mozpath.dirname(java)) 657 config, out, status = run_configure_java( 658 paths, 659 mock_java_home=alt_java_home, 660 mock_path=mozpath.dirname(java), 661 args=["--enable-java-coverage"], 662 ) 663 self.assertEqual(status, 1) 664 self.assertEqual(config, {}) 665 666 # Any missing tool is fatal when these checks run. 667 paths = {} 668 config, out, status = run_configure_java( 669 mock_fs_paths={}, 670 mock_path=mozpath.dirname(java), 671 args=["--enable-java-coverage"], 672 ) 673 self.assertEqual(status, 1) 674 self.assertEqual(config, {}) 675 self.assertEqual(out, expected_error_message) 676 677 def test_pkg_check_modules(self): 678 mock_pkg_config_version = "0.10.0" 679 mock_pkg_config_path = mozpath.abspath("/usr/bin/pkg-config") 680 681 seen_flags = set() 682 683 def mock_pkg_config(_, args): 684 if "--dont-define-prefix" in args: 685 args = list(args) 686 seen_flags.add(args.pop(args.index("--dont-define-prefix"))) 687 args = tuple(args) 688 if args[0:2] == ("--errors-to-stdout", "--print-errors"): 689 assert len(args) == 3 690 package = args[2] 691 if package == "unknown": 692 return ( 693 1, 694 "Package unknown was not found in the pkg-config search path.\n" 695 "Perhaps you should add the directory containing `unknown.pc'\n" 696 "to the PKG_CONFIG_PATH environment variable\n" 697 "No package 'unknown' found", 698 "", 699 ) 700 if package == "valid": 701 return 0, "", "" 702 if package == "new > 1.1": 703 return 1, "Requested 'new > 1.1' but version of new is 1.1", "" 704 if args[0] == "--cflags": 705 assert len(args) == 2 706 return 0, "-I/usr/include/%s" % args[1], "" 707 if args[0] == "--libs": 708 assert len(args) == 2 709 return 0, "-l%s" % args[1], "" 710 if args[0] == "--version": 711 return 0, mock_pkg_config_version, "" 712 if args[0] == "--about": 713 return 1, "Unknown option --about", "" 714 self.fail("Unexpected arguments to mock_pkg_config: %s" % (args,)) 715 716 def mock_pkgconf(_, args): 717 if args[0] == "--shared": 718 seen_flags.add(args[0]) 719 args = args[1:] 720 if args[0] == "--about": 721 return 0, f"pkgconf {mock_pkg_config_version}", "" 722 return mock_pkg_config(_, args) 723 724 def get_result(cmd, args=[], bootstrapped_sysroot=False, extra_paths=None): 725 return self.get_result( 726 textwrap.dedent( 727 """\ 728 option('--disable-compile-environment', help='Compile env') 729 compile_environment = depends(when='--enable-compile-environment')(lambda: True) 730 toolchain_prefix = depends(when=True)(lambda: None) 731 target_multiarch_dir = depends(when=True)(lambda: None) 732 target_sysroot = depends(when=True)(lambda: %(sysroot)s) 733 target = depends(when=True)(lambda: namespace(os="unknown")) 734 include('%(topsrcdir)s/build/moz.configure/util.configure') 735 include('%(topsrcdir)s/build/moz.configure/checks.configure') 736 # Skip bootstrapping. 737 @template 738 def check_prog(*args, **kwargs): 739 del kwargs["bootstrap"] 740 return check_prog(*args, **kwargs) 741 include('%(topsrcdir)s/build/moz.configure/pkg.configure') 742 """ 743 % { 744 "topsrcdir": topsrcdir, 745 "sysroot": ( 746 "namespace(bootstrapped=True)" 747 if bootstrapped_sysroot 748 else "None" 749 ), 750 } 751 ) 752 + cmd, 753 args=args, 754 extra_paths=extra_paths, 755 includes=(), 756 ) 757 758 extra_paths = {mock_pkg_config_path: mock_pkg_config} 759 760 config, output, status = get_result("pkg_check_modules('MOZ_VALID', 'valid')") 761 self.assertEqual(status, 1) 762 self.assertEqual( 763 output, 764 textwrap.dedent( 765 """\ 766 checking for pkg_config... not found 767 ERROR: *** The pkg-config script could not be found. Make sure it is 768 *** in your path, or set the PKG_CONFIG environment variable 769 *** to the full path to pkg-config. 770 """ 771 ), 772 ) 773 774 for pkg_config, version, bootstrapped_sysroot, is_pkgconf in ( 775 (mock_pkg_config, "0.10.0", False, False), 776 (mock_pkg_config, "0.30.0", False, False), 777 (mock_pkg_config, "0.30.0", True, False), 778 (mock_pkgconf, "1.1.0", True, True), 779 (mock_pkgconf, "1.6.0", False, True), 780 (mock_pkgconf, "1.8.0", False, True), 781 (mock_pkgconf, "1.8.0", True, True), 782 ): 783 seen_flags = set() 784 mock_pkg_config_version = version 785 config, output, status = get_result( 786 "pkg_check_modules('MOZ_VALID', 'valid')", 787 bootstrapped_sysroot=bootstrapped_sysroot, 788 extra_paths={mock_pkg_config_path: pkg_config}, 789 ) 790 self.assertEqual(status, 0) 791 self.assertEqual( 792 output, 793 textwrap.dedent( 794 """\ 795 checking for pkg_config... %s 796 checking for pkg-config version... %s 797 checking whether pkg-config is pkgconf... %s 798 checking for valid... yes 799 checking MOZ_VALID_CFLAGS... -I/usr/include/valid 800 checking MOZ_VALID_LIBS... -lvalid 801 """ 802 % ( 803 mock_pkg_config_path, 804 mock_pkg_config_version, 805 "yes" if is_pkgconf else "no", 806 ) 807 ), 808 ) 809 self.assertEqual( 810 config, 811 { 812 "PKG_CONFIG": mock_pkg_config_path, 813 "MOZ_VALID_CFLAGS": ("-I/usr/include/valid",), 814 "MOZ_VALID_LIBS": ("-lvalid",), 815 }, 816 ) 817 if version == "1.8.0" and bootstrapped_sysroot: 818 self.assertEqual(seen_flags, set(["--shared", "--dont-define-prefix"])) 819 elif version == "1.8.0": 820 self.assertEqual(seen_flags, set(["--shared"])) 821 elif version in ("1.6.0", "0.30.0") and bootstrapped_sysroot: 822 self.assertEqual(seen_flags, set(["--dont-define-prefix"])) 823 else: 824 self.assertEqual(seen_flags, set()) 825 826 config, output, status = get_result( 827 "pkg_check_modules('MOZ_UKNOWN', 'unknown')", extra_paths=extra_paths 828 ) 829 self.assertEqual(status, 1) 830 self.assertEqual( 831 output, 832 textwrap.dedent( 833 """\ 834 checking for pkg_config... %s 835 checking for pkg-config version... %s 836 checking whether pkg-config is pkgconf... no 837 checking for unknown... no 838 ERROR: Package unknown was not found in the pkg-config search path. 839 ERROR: Perhaps you should add the directory containing `unknown.pc' 840 ERROR: to the PKG_CONFIG_PATH environment variable 841 ERROR: No package 'unknown' found 842 """ 843 % (mock_pkg_config_path, mock_pkg_config_version) 844 ), 845 ) 846 self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path}) 847 848 config, output, status = get_result( 849 "pkg_check_modules('MOZ_NEW', 'new > 1.1')", extra_paths=extra_paths 850 ) 851 self.assertEqual(status, 1) 852 self.assertEqual( 853 output, 854 textwrap.dedent( 855 """\ 856 checking for pkg_config... %s 857 checking for pkg-config version... %s 858 checking whether pkg-config is pkgconf... no 859 checking for new > 1.1... no 860 ERROR: Requested 'new > 1.1' but version of new is 1.1 861 """ 862 % (mock_pkg_config_path, mock_pkg_config_version) 863 ), 864 ) 865 self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path}) 866 867 # allow_missing makes missing packages non-fatal. 868 cmd = textwrap.dedent( 869 """\ 870 have_new_module = pkg_check_modules('MOZ_NEW', 'new > 1.1', allow_missing=True) 871 @depends(have_new_module) 872 def log_new_module_error(mod): 873 if mod is not True: 874 log.info('Module not found.') 875 """ 876 ) 877 878 config, output, status = get_result(cmd, extra_paths=extra_paths) 879 self.assertEqual(status, 0) 880 self.assertEqual( 881 output, 882 textwrap.dedent( 883 """\ 884 checking for pkg_config... %s 885 checking for pkg-config version... %s 886 checking whether pkg-config is pkgconf... no 887 checking for new > 1.1... no 888 WARNING: Requested 'new > 1.1' but version of new is 1.1 889 Module not found. 890 """ 891 % (mock_pkg_config_path, mock_pkg_config_version) 892 ), 893 ) 894 self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path}) 895 896 config, output, status = get_result( 897 cmd, args=["--disable-compile-environment"], extra_paths=extra_paths 898 ) 899 self.assertEqual(status, 0) 900 self.assertEqual(output, "Module not found.\n") 901 self.assertEqual(config, {}) 902 903 def mock_old_pkg_config(_, args): 904 if args[0] == "--version": 905 return 0, "0.8.10", "" 906 if args[0] == "--about": 907 return 1, "Unknown option --about", "" 908 self.fail("Unexpected arguments to mock_old_pkg_config: %s" % args) 909 910 extra_paths = {mock_pkg_config_path: mock_old_pkg_config} 911 912 config, output, status = get_result( 913 "pkg_check_modules('MOZ_VALID', 'valid')", extra_paths=extra_paths 914 ) 915 self.assertEqual(status, 1) 916 self.assertEqual( 917 output, 918 textwrap.dedent( 919 """\ 920 checking for pkg_config... %s 921 checking for pkg-config version... 0.8.10 922 checking whether pkg-config is pkgconf... no 923 ERROR: *** Your version of pkg-config is too old. You need version 0.9.0 or newer. 924 """ 925 % mock_pkg_config_path 926 ), 927 ) 928 929 def test_simple_keyfile(self): 930 includes = ("util.configure", "checks.configure", "keyfiles.configure") 931 932 config, output, status = self.get_result( 933 "simple_keyfile('Mozilla API')", includes=includes 934 ) 935 self.assertEqual(status, 0) 936 self.assertEqual( 937 output, 938 textwrap.dedent( 939 """\ 940 checking for the Mozilla API key... no 941 """ 942 ), 943 ) 944 self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "no-mozilla-api-key"}) 945 946 config, output, status = self.get_result( 947 "simple_keyfile('Mozilla API')", 948 args=["--with-mozilla-api-keyfile=/foo/bar/does/not/exist"], 949 includes=includes, 950 ) 951 self.assertEqual(status, 1) 952 self.assertEqual( 953 output, 954 textwrap.dedent( 955 """\ 956 checking for the Mozilla API key... no 957 ERROR: '/foo/bar/does/not/exist': No such file or directory. 958 """ 959 ), 960 ) 961 self.assertEqual(config, {}) 962 963 with MockedOpen({"key": ""}): 964 config, output, status = self.get_result( 965 "simple_keyfile('Mozilla API')", 966 args=["--with-mozilla-api-keyfile=key"], 967 includes=includes, 968 ) 969 self.assertEqual(status, 1) 970 self.assertEqual( 971 output, 972 textwrap.dedent( 973 """\ 974 checking for the Mozilla API key... no 975 ERROR: 'key' is empty. 976 """ 977 ), 978 ) 979 self.assertEqual(config, {}) 980 981 with MockedOpen({"key": "fake-key\n"}): 982 config, output, status = self.get_result( 983 "simple_keyfile('Mozilla API')", 984 args=["--with-mozilla-api-keyfile=key"], 985 includes=includes, 986 ) 987 self.assertEqual(status, 0) 988 self.assertEqual( 989 output, 990 textwrap.dedent( 991 """\ 992 checking for the Mozilla API key... yes 993 """ 994 ), 995 ) 996 self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "fake-key"}) 997 998 with MockedOpen({"default": "default-key\n"}): 999 config, output, status = self.get_result( 1000 "simple_keyfile('Mozilla API', default='default')", includes=includes 1001 ) 1002 self.assertEqual(status, 0) 1003 self.assertEqual( 1004 output, 1005 textwrap.dedent( 1006 """\ 1007 checking for the Mozilla API key... yes 1008 """ 1009 ), 1010 ) 1011 self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "default-key"}) 1012 1013 with MockedOpen({"default": "default-key\n", "key": "fake-key\n"}): 1014 config, output, status = self.get_result( 1015 "simple_keyfile('Mozilla API', default='key')", includes=includes 1016 ) 1017 self.assertEqual(status, 0) 1018 self.assertEqual( 1019 output, 1020 textwrap.dedent( 1021 """\ 1022 checking for the Mozilla API key... yes 1023 """ 1024 ), 1025 ) 1026 self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "fake-key"}) 1027 1028 def test_id_and_secret_keyfile(self): 1029 includes = ("util.configure", "checks.configure", "keyfiles.configure") 1030 1031 config, output, status = self.get_result( 1032 "id_and_secret_keyfile('Bing API')", includes=includes 1033 ) 1034 self.assertEqual(status, 0) 1035 self.assertEqual( 1036 output, 1037 textwrap.dedent( 1038 """\ 1039 checking for the Bing API key... no 1040 """ 1041 ), 1042 ) 1043 self.assertEqual( 1044 config, 1045 { 1046 "MOZ_BING_API_CLIENTID": "no-bing-api-clientid", 1047 "MOZ_BING_API_KEY": "no-bing-api-key", 1048 }, 1049 ) 1050 1051 config, output, status = self.get_result( 1052 "id_and_secret_keyfile('Bing API')", 1053 args=["--with-bing-api-keyfile=/foo/bar/does/not/exist"], 1054 includes=includes, 1055 ) 1056 self.assertEqual(status, 1) 1057 self.assertEqual( 1058 output, 1059 textwrap.dedent( 1060 """\ 1061 checking for the Bing API key... no 1062 ERROR: '/foo/bar/does/not/exist': No such file or directory. 1063 """ 1064 ), 1065 ) 1066 self.assertEqual(config, {}) 1067 1068 with MockedOpen({"key": ""}): 1069 config, output, status = self.get_result( 1070 "id_and_secret_keyfile('Bing API')", 1071 args=["--with-bing-api-keyfile=key"], 1072 includes=includes, 1073 ) 1074 self.assertEqual(status, 1) 1075 self.assertEqual( 1076 output, 1077 textwrap.dedent( 1078 """\ 1079 checking for the Bing API key... no 1080 ERROR: 'key' is empty. 1081 """ 1082 ), 1083 ) 1084 self.assertEqual(config, {}) 1085 1086 with MockedOpen({"key": "fake-id fake-key\n"}): 1087 config, output, status = self.get_result( 1088 "id_and_secret_keyfile('Bing API')", 1089 args=["--with-bing-api-keyfile=key"], 1090 includes=includes, 1091 ) 1092 self.assertEqual(status, 0) 1093 self.assertEqual( 1094 output, 1095 textwrap.dedent( 1096 """\ 1097 checking for the Bing API key... yes 1098 """ 1099 ), 1100 ) 1101 self.assertEqual( 1102 config, 1103 {"MOZ_BING_API_CLIENTID": "fake-id", "MOZ_BING_API_KEY": "fake-key"}, 1104 ) 1105 1106 with MockedOpen({"key": "fake-key\n"}): 1107 config, output, status = self.get_result( 1108 "id_and_secret_keyfile('Bing API')", 1109 args=["--with-bing-api-keyfile=key"], 1110 includes=includes, 1111 ) 1112 self.assertEqual(status, 1) 1113 self.assertEqual( 1114 output, 1115 textwrap.dedent( 1116 """\ 1117 checking for the Bing API key... no 1118 ERROR: Bing API key file has an invalid format. 1119 """ 1120 ), 1121 ) 1122 self.assertEqual(config, {}) 1123 1124 with MockedOpen({"default-key": "default-id default-key\n"}): 1125 config, output, status = self.get_result( 1126 "id_and_secret_keyfile('Bing API', default='default-key')", 1127 includes=includes, 1128 ) 1129 self.assertEqual(status, 0) 1130 self.assertEqual( 1131 output, 1132 textwrap.dedent( 1133 """\ 1134 checking for the Bing API key... yes 1135 """ 1136 ), 1137 ) 1138 self.assertEqual( 1139 config, 1140 { 1141 "MOZ_BING_API_CLIENTID": "default-id", 1142 "MOZ_BING_API_KEY": "default-key", 1143 }, 1144 ) 1145 1146 with MockedOpen({ 1147 "default-key": "default-id default-key\n", 1148 "key": "fake-id fake-key\n", 1149 }): 1150 config, output, status = self.get_result( 1151 "id_and_secret_keyfile('Bing API', default='default-key')", 1152 args=["--with-bing-api-keyfile=key"], 1153 includes=includes, 1154 ) 1155 self.assertEqual(status, 0) 1156 self.assertEqual( 1157 output, 1158 textwrap.dedent( 1159 """\ 1160 checking for the Bing API key... yes 1161 """ 1162 ), 1163 ) 1164 self.assertEqual( 1165 config, 1166 {"MOZ_BING_API_CLIENTID": "fake-id", "MOZ_BING_API_KEY": "fake-key"}, 1167 ) 1168 1169 1170 if __name__ == "__main__": 1171 main()