diff options
| -rw-r--r-- | meta/lib/oeqa/selftest/cases/devtool.py | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index dd3a5e9b5c..d76b974fbb 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py | |||
| @@ -12,6 +12,7 @@ import tempfile | |||
| 12 | import glob | 12 | import glob |
| 13 | import fnmatch | 13 | import fnmatch |
| 14 | import unittest | 14 | import unittest |
| 15 | import json | ||
| 15 | 16 | ||
| 16 | from oeqa.selftest.case import OESelftestTestCase | 17 | from oeqa.selftest.case import OESelftestTestCase |
| 17 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer | 18 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer |
| @@ -2362,3 +2363,494 @@ class DevtoolUpgradeTests(DevtoolBase): | |||
| 2362 | 2363 | ||
| 2363 | #Step 4.5 | 2364 | #Step 4.5 |
| 2364 | runCmd("grep %s %s" % (modconfopt, codeconfigfile)) | 2365 | runCmd("grep %s %s" % (modconfopt, codeconfigfile)) |
| 2366 | |||
| 2367 | |||
| 2368 | class DevtoolIdeSdkTests(DevtoolBase): | ||
| 2369 | def _write_bb_config(self, recipe_names): | ||
| 2370 | """Helper to write the bitbake local.conf file""" | ||
| 2371 | conf_lines = [ | ||
| 2372 | 'IMAGE_CLASSES += "image-combined-dbg"', | ||
| 2373 | 'IMAGE_GEN_DEBUGFS = "1"', | ||
| 2374 | 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join( | ||
| 2375 | [r + '-ptest' for r in recipe_names]) | ||
| 2376 | ] | ||
| 2377 | self.write_config("\n".join(conf_lines)) | ||
| 2378 | |||
| 2379 | def _check_workspace(self): | ||
| 2380 | """Check if a workspace directory is available and setup the cleanup""" | ||
| 2381 | self.assertTrue(not os.path.exists(self.workspacedir), | ||
| 2382 | 'This test cannot be run with a workspace directory under the build directory') | ||
| 2383 | self.track_for_cleanup(self.workspacedir) | ||
| 2384 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') | ||
| 2385 | |||
| 2386 | def _workspace_scripts_dir(self, recipe_name): | ||
| 2387 | return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts')) | ||
| 2388 | |||
| 2389 | def _sources_scripts_dir(self, src_dir): | ||
| 2390 | return os.path.realpath(os.path.join(src_dir, 'oe-scripts')) | ||
| 2391 | |||
| 2392 | def _workspace_gdbinit_dir(self, recipe_name): | ||
| 2393 | return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit')) | ||
| 2394 | |||
| 2395 | def _sources_gdbinit_dir(self, src_dir): | ||
| 2396 | return os.path.realpath(os.path.join(src_dir, 'oe-gdbinit')) | ||
| 2397 | |||
| 2398 | def _devtool_ide_sdk_recipe(self, recipe_name, build_file, testimage): | ||
| 2399 | """Setup a recipe for working with devtool ide-sdk | ||
| 2400 | |||
| 2401 | Basically devtool modify -x followed by some tests | ||
| 2402 | """ | ||
| 2403 | tempdir = tempfile.mkdtemp(prefix='devtoolqa') | ||
| 2404 | self.track_for_cleanup(tempdir) | ||
| 2405 | self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name) | ||
| 2406 | |||
| 2407 | result = runCmd('devtool modify %s -x %s' % (recipe_name, tempdir)) | ||
| 2408 | self.assertExists(os.path.join(tempdir, build_file), | ||
| 2409 | 'Extracted source could not be found') | ||
| 2410 | self.assertExists(os.path.join(self.workspacedir, 'conf', | ||
| 2411 | 'layer.conf'), 'Workspace directory not created') | ||
| 2412 | matches = glob.glob(os.path.join(self.workspacedir, | ||
| 2413 | 'appends', recipe_name + '.bbappend')) | ||
| 2414 | self.assertTrue(matches, 'bbappend not created %s' % result.output) | ||
| 2415 | |||
| 2416 | # Test devtool status | ||
| 2417 | result = runCmd('devtool status') | ||
| 2418 | self.assertIn(recipe_name, result.output) | ||
| 2419 | self.assertIn(tempdir, result.output) | ||
| 2420 | self._check_src_repo(tempdir) | ||
| 2421 | |||
| 2422 | # Usually devtool ide-sdk would initiate the build of the SDK. | ||
| 2423 | # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide-sdk. | ||
| 2424 | if testimage: | ||
| 2425 | bitbake("%s qemu-native qemu-helper-native" % testimage) | ||
| 2426 | deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') | ||
| 2427 | self.add_command_to_tearDown('bitbake -c clean %s' % testimage) | ||
| 2428 | self.add_command_to_tearDown( | ||
| 2429 | 'rm -f %s/%s*' % (deploy_dir_image, testimage)) | ||
| 2430 | |||
| 2431 | return tempdir | ||
| 2432 | |||
| 2433 | def _get_recipe_ids(self, recipe_name): | ||
| 2434 | """IDs needed to write recipe specific config entries into IDE config files""" | ||
| 2435 | package_arch = get_bb_var('PACKAGE_ARCH', recipe_name) | ||
| 2436 | recipe_id = recipe_name + "-" + package_arch | ||
| 2437 | recipe_id_pretty = recipe_name + ": " + package_arch | ||
| 2438 | return (recipe_id, recipe_id_pretty) | ||
| 2439 | |||
| 2440 | def _verify_install_script_code(self, tempdir, recipe_name): | ||
| 2441 | """Verify the scripts referred by the tasks.json file are fine. | ||
| 2442 | |||
| 2443 | This function does not depend on Qemu. Therefore it verifies the scripts | ||
| 2444 | exists and the delete step works as expected. But it does not try to | ||
| 2445 | deploy to Qemu. | ||
| 2446 | """ | ||
| 2447 | recipe_id, recipe_id_pretty = self._get_recipe_ids(recipe_name) | ||
| 2448 | with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j: | ||
| 2449 | tasks_d = json.load(tasks_j) | ||
| 2450 | tasks = tasks_d["tasks"] | ||
| 2451 | task_install = next( | ||
| 2452 | (task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None) | ||
| 2453 | self.assertIsNot(task_install, None) | ||
| 2454 | # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running. | ||
| 2455 | i_and_d_script = "install_and_deploy_" + recipe_id | ||
| 2456 | i_and_d_script_path = os.path.join( | ||
| 2457 | self._workspace_scripts_dir(recipe_name), i_and_d_script) | ||
| 2458 | self.assertExists(i_and_d_script_path) | ||
| 2459 | del_script = "delete_package_dirs_" + recipe_id | ||
| 2460 | del_script_path = os.path.join( | ||
| 2461 | self._workspace_scripts_dir(recipe_name), del_script) | ||
| 2462 | self.assertExists(del_script_path) | ||
| 2463 | runCmd(del_script_path, cwd=tempdir) | ||
| 2464 | |||
| 2465 | def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe): | ||
| 2466 | """Verify deployment and execution in Qemu system work for one recipe. | ||
| 2467 | |||
| 2468 | This function checks the entire SDK workflow: changing the code, recompiling | ||
| 2469 | it and deploying it back to Qemu, and checking that the changes have been | ||
| 2470 | incorporated into the provided binaries. It also runs the tests of the recipe. | ||
| 2471 | """ | ||
| 2472 | recipe_id, _ = self._get_recipe_ids(recipe_name) | ||
| 2473 | i_and_d_script = "install_and_deploy_" + recipe_id | ||
| 2474 | install_deploy_cmd = os.path.join( | ||
| 2475 | self._workspace_scripts_dir(recipe_name), i_and_d_script) | ||
| 2476 | self.assertExists(install_deploy_cmd, | ||
| 2477 | '%s script not found' % install_deploy_cmd) | ||
| 2478 | runCmd(install_deploy_cmd) | ||
| 2479 | |||
| 2480 | MAGIC_STRING_ORIG = "Magic: 123456789" | ||
| 2481 | MAGIC_STRING_NEW = "Magic: 987654321" | ||
| 2482 | ptest_cmd = "ptest-runner " + recipe_name | ||
| 2483 | |||
| 2484 | # validate that SSH is working | ||
| 2485 | status, _ = qemu.run("uname") | ||
| 2486 | self.assertEqual( | ||
| 2487 | status, 0, msg="Failed to connect to the SSH server on Qemu") | ||
| 2488 | |||
| 2489 | # Verify the unmodified example prints the magic string | ||
| 2490 | status, output = qemu.run(example_exe) | ||
| 2491 | self.assertEqual(status, 0, msg="%s failed: %s" % | ||
| 2492 | (example_exe, output)) | ||
| 2493 | self.assertIn(MAGIC_STRING_ORIG, output) | ||
| 2494 | |||
| 2495 | # Verify the unmodified ptests work | ||
| 2496 | status, output = qemu.run(ptest_cmd) | ||
| 2497 | self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output)) | ||
| 2498 | self.assertIn("PASS: cpp-example-lib", output) | ||
| 2499 | |||
| 2500 | # Replace the Magic String in the code, compile and deploy to Qemu | ||
| 2501 | cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp') | ||
| 2502 | with open(cpp_example_lib_hpp, 'r') as file: | ||
| 2503 | cpp_code = file.read() | ||
| 2504 | cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW) | ||
| 2505 | with open(cpp_example_lib_hpp, 'w') as file: | ||
| 2506 | file.write(cpp_code) | ||
| 2507 | runCmd(install_deploy_cmd, cwd=tempdir) | ||
| 2508 | |||
| 2509 | # Verify the modified example prints the modified magic string | ||
| 2510 | status, output = qemu.run(example_exe) | ||
| 2511 | self.assertEqual(status, 0, msg="%s failed: %s" % | ||
| 2512 | (example_exe, output)) | ||
| 2513 | self.assertNotIn(MAGIC_STRING_ORIG, output) | ||
| 2514 | self.assertIn(MAGIC_STRING_NEW, output) | ||
| 2515 | |||
| 2516 | # Verify the modified example ptests work | ||
| 2517 | status, output = qemu.run(ptest_cmd) | ||
| 2518 | self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output)) | ||
| 2519 | self.assertIn("PASS: cpp-example-lib", output) | ||
| 2520 | |||
| 2521 | def _gdb_cross(self): | ||
| 2522 | """Verify gdb-cross is provided by devtool ide-sdk""" | ||
| 2523 | target_arch = self.td["TARGET_ARCH"] | ||
| 2524 | target_sys = self.td["TARGET_SYS"] | ||
| 2525 | gdb_recipe = "gdb-cross-" + target_arch | ||
| 2526 | gdb_binary = target_sys + "-gdb" | ||
| 2527 | |||
| 2528 | native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe) | ||
| 2529 | r = runCmd("%s --version" % gdb_binary, | ||
| 2530 | native_sysroot=native_sysroot, target_sys=target_sys) | ||
| 2531 | self.assertEqual(r.status, 0) | ||
| 2532 | self.assertIn("GNU gdb", r.output) | ||
| 2533 | |||
| 2534 | def _gdb_cross_debugging(self, qemu, recipe_name, example_exe): | ||
| 2535 | """Verify gdb-cross is working | ||
| 2536 | |||
| 2537 | Test remote debugging: | ||
| 2538 | break main | ||
| 2539 | run | ||
| 2540 | continue | ||
| 2541 | """ | ||
| 2542 | sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' | ||
| 2543 | gdbserver_script = os.path.join(self._workspace_scripts_dir( | ||
| 2544 | recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_m') | ||
| 2545 | gdb_script = os.path.join(self._workspace_scripts_dir( | ||
| 2546 | recipe_name), 'gdb_1234_usr-bin-' + example_exe) | ||
| 2547 | |||
| 2548 | # Start a gdbserver | ||
| 2549 | r = runCmd(gdbserver_script) | ||
| 2550 | self.assertEqual(r.status, 0) | ||
| 2551 | |||
| 2552 | # Check there is a gdbserver running | ||
| 2553 | r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps')) | ||
| 2554 | self.assertEqual(r.status, 0) | ||
| 2555 | self.assertIn("gdbserver ", r.output) | ||
| 2556 | |||
| 2557 | # Check the pid file is correct | ||
| 2558 | test_cmd = "cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \ | ||
| 2559 | example_exe + "/pid)/cmdline" | ||
| 2560 | r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd)) | ||
| 2561 | self.assertEqual(r.status, 0) | ||
| 2562 | self.assertIn("gdbserver", r.output) | ||
| 2563 | |||
| 2564 | # Test remote debugging works | ||
| 2565 | r = runCmd( | ||
| 2566 | gdb_script + " --batch -ex 'break main' --ex 'run' -ex 'continue'") | ||
| 2567 | self.assertEqual(r.status, 0) | ||
| 2568 | self.assertIn("Breakpoint 1, main", r.output) | ||
| 2569 | self.assertIn("exited normally", r.output) | ||
| 2570 | |||
| 2571 | # Stop the gdbserver | ||
| 2572 | r = runCmd(gdbserver_script + ' stop') | ||
| 2573 | self.assertEqual(r.status, 0) | ||
| 2574 | |||
| 2575 | # Check there is no gdbserver running | ||
| 2576 | r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps')) | ||
| 2577 | self.assertEqual(r.status, 0) | ||
| 2578 | self.assertNotIn("gdbserver ", r.output) | ||
| 2579 | |||
| 2580 | def _verify_cmake_preset(self, tempdir): | ||
| 2581 | """Verify the generated cmake preset works as expected | ||
| 2582 | |||
| 2583 | Check if compiling works | ||
| 2584 | Check if unit tests can be executed in qemu (not qemu-system) | ||
| 2585 | """ | ||
| 2586 | with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j: | ||
| 2587 | cmake_preset_d = json.load(cmake_preset_j) | ||
| 2588 | config_presets = cmake_preset_d["configurePresets"] | ||
| 2589 | self.assertEqual(len(config_presets), 1) | ||
| 2590 | cmake_exe = config_presets[0]["cmakeExecutable"] | ||
| 2591 | preset_name = config_presets[0]["name"] | ||
| 2592 | |||
| 2593 | # Verify the wrapper for cmake native is available | ||
| 2594 | self.assertExists(cmake_exe) | ||
| 2595 | |||
| 2596 | # Verify the cmake preset generated by devtool ide-sdk is available | ||
| 2597 | result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir) | ||
| 2598 | self.assertIn(preset_name, result.output) | ||
| 2599 | |||
| 2600 | # Verify cmake re-uses the o files compiled by bitbake | ||
| 2601 | result = runCmd('%s --build --preset %s' % | ||
| 2602 | (cmake_exe, preset_name), cwd=tempdir) | ||
| 2603 | self.assertIn("ninja: no work to do.", result.output) | ||
| 2604 | |||
| 2605 | # Verify the unit tests work (in Qemu user mode) | ||
| 2606 | result = runCmd('%s --build --preset %s --target test' % | ||
| 2607 | (cmake_exe, preset_name), cwd=tempdir) | ||
| 2608 | self.assertIn("100% tests passed", result.output) | ||
| 2609 | |||
| 2610 | # Verify re-building and testing works again | ||
| 2611 | result = runCmd('%s --build --preset %s --target clean' % | ||
| 2612 | (cmake_exe, preset_name), cwd=tempdir) | ||
| 2613 | self.assertIn("Cleaning", result.output) | ||
| 2614 | result = runCmd('%s --build --preset %s' % | ||
| 2615 | (cmake_exe, preset_name), cwd=tempdir) | ||
| 2616 | self.assertIn("Building", result.output) | ||
| 2617 | self.assertIn("Linking", result.output) | ||
| 2618 | result = runCmd('%s --build --preset %s --target test' % | ||
| 2619 | (cmake_exe, preset_name), cwd=tempdir) | ||
| 2620 | self.assertIn("Running tests...", result.output) | ||
| 2621 | self.assertIn("100% tests passed", result.output) | ||
| 2622 | |||
| 2623 | @OETestTag("runqemu") | ||
| 2624 | def test_devtool_ide_sdk_none_qemu(self): | ||
| 2625 | """Start qemu-system and run tests for multiple recipes. ide=none is used.""" | ||
| 2626 | recipe_names = ["cmake-example", "meson-example"] | ||
| 2627 | testimage = "oe-selftest-image" | ||
| 2628 | |||
| 2629 | self._check_workspace() | ||
| 2630 | self._write_bb_config(recipe_names) | ||
| 2631 | self._check_runqemu_prerequisites() | ||
| 2632 | |||
| 2633 | # Verify deployment to Qemu (system mode) works | ||
| 2634 | bitbake(testimage) | ||
| 2635 | with runqemu(testimage, runqemuparams="nographic") as qemu: | ||
| 2636 | # cmake-example recipe | ||
| 2637 | recipe_name = "cmake-example" | ||
| 2638 | example_exe = "cmake-example" | ||
| 2639 | build_file = "CMakeLists.txt" | ||
| 2640 | tempdir = self._devtool_ide_sdk_recipe( | ||
| 2641 | recipe_name, build_file, testimage) | ||
| 2642 | bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % ( | ||
| 2643 | recipe_name, testimage, qemu.ip) | ||
| 2644 | runCmd(bitbake_sdk_cmd) | ||
| 2645 | self._verify_cmake_preset(tempdir) | ||
| 2646 | self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe) | ||
| 2647 | # Verify the oe-scripts sym-link is valid | ||
| 2648 | self.assertEqual(self._workspace_scripts_dir( | ||
| 2649 | recipe_name), self._sources_scripts_dir(tempdir)) | ||
| 2650 | # Verify GDB is working after devtool ide-sdk | ||
| 2651 | self._gdb_cross() | ||
| 2652 | self._gdb_cross_debugging(qemu, recipe_name, example_exe) | ||
| 2653 | |||
| 2654 | # meson-example recipe | ||
| 2655 | recipe_name = "meson-example" | ||
| 2656 | example_exe = "mesonex" | ||
| 2657 | build_file = "meson.build" | ||
| 2658 | tempdir = self._devtool_ide_sdk_recipe( | ||
| 2659 | recipe_name, build_file, testimage) | ||
| 2660 | bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % ( | ||
| 2661 | recipe_name, testimage, qemu.ip) | ||
| 2662 | runCmd(bitbake_sdk_cmd) | ||
| 2663 | self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe) | ||
| 2664 | # Verify the oe-scripts sym-link is valid | ||
| 2665 | self.assertEqual(self._workspace_scripts_dir( | ||
| 2666 | recipe_name), self._sources_scripts_dir(tempdir)) | ||
| 2667 | # Verify GDB is working after devtool ide-sdk | ||
| 2668 | self._gdb_cross() | ||
| 2669 | self._gdb_cross_debugging(qemu, recipe_name, example_exe) | ||
| 2670 | |||
| 2671 | def test_devtool_ide_sdk_code_cmake(self): | ||
| 2672 | """Verify a cmake recipe works with ide=code mode""" | ||
| 2673 | recipe_name = "cmake-example" | ||
| 2674 | build_file = "CMakeLists.txt" | ||
| 2675 | testimage = "oe-selftest-image" | ||
| 2676 | |||
| 2677 | self._check_workspace() | ||
| 2678 | self._write_bb_config([recipe_name]) | ||
| 2679 | tempdir = self._devtool_ide_sdk_recipe( | ||
| 2680 | recipe_name, build_file, testimage) | ||
| 2681 | bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % ( | ||
| 2682 | recipe_name, testimage) | ||
| 2683 | runCmd(bitbake_sdk_cmd) | ||
| 2684 | self._verify_cmake_preset(tempdir) | ||
| 2685 | self._verify_install_script_code(tempdir, recipe_name) | ||
| 2686 | self._gdb_cross() | ||
| 2687 | |||
| 2688 | def test_devtool_ide_sdk_code_meson(self): | ||
| 2689 | """Verify a meson recipe works with ide=code mode""" | ||
| 2690 | recipe_name = "meson-example" | ||
| 2691 | build_file = "meson.build" | ||
| 2692 | testimage = "oe-selftest-image" | ||
| 2693 | |||
| 2694 | self._check_workspace() | ||
| 2695 | self._write_bb_config([recipe_name]) | ||
| 2696 | tempdir = self._devtool_ide_sdk_recipe( | ||
| 2697 | recipe_name, build_file, testimage) | ||
| 2698 | bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % ( | ||
| 2699 | recipe_name, testimage) | ||
| 2700 | runCmd(bitbake_sdk_cmd) | ||
| 2701 | |||
| 2702 | with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j: | ||
| 2703 | settings_d = json.load(settings_j) | ||
| 2704 | meson_exe = settings_d["mesonbuild.mesonPath"] | ||
| 2705 | meson_build_folder = settings_d["mesonbuild.buildFolder"] | ||
| 2706 | |||
| 2707 | # Verify the wrapper for meson native is available | ||
| 2708 | self.assertExists(meson_exe) | ||
| 2709 | |||
| 2710 | # Verify meson re-uses the o files compiled by bitbake | ||
| 2711 | result = runCmd('%s compile -C %s' % | ||
| 2712 | (meson_exe, meson_build_folder), cwd=tempdir) | ||
| 2713 | self.assertIn("ninja: no work to do.", result.output) | ||
| 2714 | |||
| 2715 | # Verify the unit tests work (in Qemu) | ||
| 2716 | runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) | ||
| 2717 | |||
| 2718 | # Verify re-building and testing works again | ||
| 2719 | result = runCmd('%s compile -C %s --clean' % | ||
| 2720 | (meson_exe, meson_build_folder), cwd=tempdir) | ||
| 2721 | self.assertIn("Cleaning...", result.output) | ||
| 2722 | result = runCmd('%s compile -C %s' % | ||
| 2723 | (meson_exe, meson_build_folder), cwd=tempdir) | ||
| 2724 | self.assertIn("Linking target", result.output) | ||
| 2725 | runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) | ||
| 2726 | |||
| 2727 | self._verify_install_script_code(tempdir, recipe_name) | ||
| 2728 | self._gdb_cross() | ||
| 2729 | |||
| 2730 | def test_devtool_ide_sdk_shared_sysroots(self): | ||
| 2731 | """Verify the shared sysroot SDK""" | ||
| 2732 | |||
| 2733 | # Handle the workspace (which is not needed by this test case) | ||
| 2734 | self._check_workspace() | ||
| 2735 | |||
| 2736 | result_init = runCmd( | ||
| 2737 | 'devtool ide-sdk -m shared oe-selftest-image cmake-example meson-example --ide=code') | ||
| 2738 | bb_vars = get_bb_vars( | ||
| 2739 | ['REAL_MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'], "meta-ide-support") | ||
| 2740 | environment_script = 'environment-setup-%s' % bb_vars['REAL_MULTIMACH_TARGET_SYS'] | ||
| 2741 | deploydir = bb_vars['DEPLOY_DIR_IMAGE'] | ||
| 2742 | environment_script_path = os.path.join(deploydir, environment_script) | ||
| 2743 | cpp_example_src = os.path.join( | ||
| 2744 | bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files') | ||
| 2745 | |||
| 2746 | # Verify the cross environment script is available | ||
| 2747 | self.assertExists(environment_script_path) | ||
| 2748 | |||
| 2749 | def runCmdEnv(cmd, cwd): | ||
| 2750 | cmd = '/bin/sh -c ". %s > /dev/null && %s"' % ( | ||
| 2751 | environment_script_path, cmd) | ||
| 2752 | return runCmd(cmd, cwd) | ||
| 2753 | |||
| 2754 | # Verify building the C++ example works with CMake | ||
| 2755 | tempdir_cmake = tempfile.mkdtemp(prefix='devtoolqa') | ||
| 2756 | self.track_for_cleanup(tempdir_cmake) | ||
| 2757 | |||
| 2758 | result_cmake = runCmdEnv("which cmake", cwd=tempdir_cmake) | ||
| 2759 | cmake_native = os.path.normpath(result_cmake.output.strip()) | ||
| 2760 | self.assertExists(cmake_native) | ||
| 2761 | |||
| 2762 | runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake) | ||
| 2763 | runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake) | ||
| 2764 | |||
| 2765 | # Verify the printed note really referres to a cmake executable | ||
| 2766 | cmake_native_code = "" | ||
| 2767 | for line in result_init.output.splitlines(): | ||
| 2768 | m = re.search(r'"cmake.cmakePath": "(.*)"', line) | ||
| 2769 | if m: | ||
| 2770 | cmake_native_code = m.group(1) | ||
| 2771 | break | ||
| 2772 | self.assertExists(cmake_native_code) | ||
| 2773 | self.assertEqual(cmake_native, cmake_native_code) | ||
| 2774 | |||
| 2775 | # Verify building the C++ example works with Meson | ||
| 2776 | tempdir_meson = tempfile.mkdtemp(prefix='devtoolqa') | ||
| 2777 | self.track_for_cleanup(tempdir_meson) | ||
| 2778 | |||
| 2779 | result_cmake = runCmdEnv("which meson", cwd=tempdir_meson) | ||
| 2780 | meson_native = os.path.normpath(result_cmake.output.strip()) | ||
| 2781 | self.assertExists(meson_native) | ||
| 2782 | |||
| 2783 | runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src) | ||
| 2784 | runCmdEnv('meson compile', cwd=tempdir_meson) | ||
| 2785 | |||
| 2786 | def test_devtool_ide_sdk_plugins(self): | ||
| 2787 | """Test that devtool ide-sdk can use plugins from other layers.""" | ||
| 2788 | |||
| 2789 | # We need a workspace layer and a modified recipe (but no image) | ||
| 2790 | modified_recipe_name = "meson-example" | ||
| 2791 | modified_build_file = "meson.build" | ||
| 2792 | testimage = "oe-selftest-image" | ||
| 2793 | shared_recipe_name = "cmake-example" | ||
| 2794 | |||
| 2795 | self._check_workspace() | ||
| 2796 | self._write_bb_config([modified_recipe_name]) | ||
| 2797 | tempdir = self._devtool_ide_sdk_recipe( | ||
| 2798 | modified_recipe_name, modified_build_file, None) | ||
| 2799 | |||
| 2800 | IDE_RE = re.compile(r'.*--ide \{(.*)\}.*') | ||
| 2801 | |||
| 2802 | def get_ides_from_help(help_str): | ||
| 2803 | m = IDE_RE.search(help_str) | ||
| 2804 | return m.group(1).split(',') | ||
| 2805 | |||
| 2806 | # verify the default plugins are available but the foo plugin is not | ||
| 2807 | result = runCmd('devtool ide-sdk -h') | ||
| 2808 | found_ides = get_ides_from_help(result.output) | ||
| 2809 | self.assertIn('code', found_ides) | ||
| 2810 | self.assertIn('none', found_ides) | ||
| 2811 | self.assertNotIn('foo', found_ides) | ||
| 2812 | |||
| 2813 | shared_config_file = os.path.join(tempdir, 'shared-config.txt') | ||
| 2814 | shared_config_str = 'Dummy shared IDE config' | ||
| 2815 | modified_config_file = os.path.join(tempdir, 'modified-config.txt') | ||
| 2816 | modified_config_str = 'Dummy modified IDE config' | ||
| 2817 | |||
| 2818 | # Generate a foo plugin in the workspace layer | ||
| 2819 | plugin_dir = os.path.join( | ||
| 2820 | self.workspacedir, 'lib', 'devtool', 'ide_plugins') | ||
| 2821 | os.makedirs(plugin_dir) | ||
| 2822 | plugin_code = 'from devtool.ide_plugins import IdeBase\n\n' | ||
| 2823 | plugin_code += 'class IdeFoo(IdeBase):\n' | ||
| 2824 | plugin_code += ' def setup_shared_sysroots(self, shared_env):\n' | ||
| 2825 | plugin_code += ' with open("%s", "w") as config_file:\n' % shared_config_file | ||
| 2826 | plugin_code += ' config_file.write("%s")\n\n' % shared_config_str | ||
| 2827 | plugin_code += ' def setup_modified_recipe(self, args, image_recipe, modified_recipe):\n' | ||
| 2828 | plugin_code += ' with open("%s", "w") as config_file:\n' % modified_config_file | ||
| 2829 | plugin_code += ' config_file.write("%s")\n\n' % modified_config_str | ||
| 2830 | plugin_code += 'def register_ide_plugin(ide_plugins):\n' | ||
| 2831 | plugin_code += ' ide_plugins["foo"] = IdeFoo\n' | ||
| 2832 | |||
| 2833 | plugin_py = os.path.join(plugin_dir, 'ide_foo.py') | ||
| 2834 | with open(plugin_py, 'w') as plugin_file: | ||
| 2835 | plugin_file.write(plugin_code) | ||
| 2836 | |||
| 2837 | # Verify the foo plugin is available as well | ||
| 2838 | result = runCmd('devtool ide-sdk -h') | ||
| 2839 | found_ides = get_ides_from_help(result.output) | ||
| 2840 | self.assertIn('code', found_ides) | ||
| 2841 | self.assertIn('none', found_ides) | ||
| 2842 | self.assertIn('foo', found_ides) | ||
| 2843 | |||
| 2844 | # Verify the foo plugin generates a shared config | ||
| 2845 | result = runCmd( | ||
| 2846 | 'devtool ide-sdk -m shared --skip-bitbake --ide foo %s' % shared_recipe_name) | ||
| 2847 | with open(shared_config_file) as shared_config: | ||
| 2848 | shared_config_new = shared_config.read() | ||
| 2849 | self.assertEqual(shared_config_str, shared_config_new) | ||
| 2850 | |||
| 2851 | # Verify the foo plugin generates a modified config | ||
| 2852 | result = runCmd('devtool ide-sdk --skip-bitbake --ide foo %s %s' % | ||
| 2853 | (modified_recipe_name, testimage)) | ||
| 2854 | with open(modified_config_file) as modified_config: | ||
| 2855 | modified_config_new = modified_config.read() | ||
| 2856 | self.assertEqual(modified_config_str, modified_config_new) | ||
