コード例 #1
0
    def test_build(self):
        """
        Initialize a new project and try to build it
        """
        project = stm32pio.core.project.Stm32pio(
            STAGE_PATH, parameters={'project': {
                'board': PROJECT_BOARD
            }})
        project.generate_code()
        project.pio_init()
        project.patch()

        self.assertEqual(project.build(), 0, msg="Build failed")
コード例 #2
0
    def test_regenerate_code(self):
        """
        Simulate a new project creation, its changing and CubeMX code re-generation (for example, after adding new
        hardware features and some new files by a user)
        """
        project = stm32pio.core.project.Stm32pio(
            STAGE_PATH, parameters={'project': {
                'board': PROJECT_BOARD
            }})

        # Generate a new project ...
        project.generate_code()
        project.pio_init()
        project.patch()

        # ... change it:
        test_file_1 = STAGE_PATH.joinpath('Src', 'main.c')
        test_content_1 = "*** TEST STRING 1 ***\n"
        test_file_2 = STAGE_PATH.joinpath('Inc', 'my_header.h')
        test_content_2 = "*** TEST STRING 2 ***\n"
        #   - add some sample string inside CubeMX' /* BEGIN - END */ block
        main_c_content = test_file_1.read_text()
        pos = main_c_content.index("while (1)")
        main_c_new_content = main_c_content[:
                                            pos] + test_content_1 + main_c_content[
                                                pos:]
        test_file_1.write_text(main_c_new_content)
        #  - add new file inside the project
        test_file_2.write_text(test_content_2)

        # Re-generate CubeMX project
        project.generate_code()

        # Check if added information has been preserved
        for test_content, after_regenerate_content in [
            (test_content_1, test_file_1.read_text()),
            (test_content_2, test_file_2.read_text())
        ]:
            with self.subTest(
                    msg=
                    f"User content hasn't been preserved in {after_regenerate_content}"
            ):
                self.assertIn(test_content, after_regenerate_content)
コード例 #3
0
ファイル: app.py プロジェクト: bluejazzCHN/stm32pio
def main(sys_argv: List[str] = None, should_setup_logging: bool = True) -> int:
    """
    Can be used as a high-level wrapper to perform independent tasks.

    Example:
        ret_code = stm32pio.app.main(sys_argv=['new', '-d', '~/path/to/project', '-b', 'nucleo_f031k6', '--with-build'])

    Args:
        sys_argv: list of strings CLI arguments
        should_setup_logging: if this is true, the preferable default logging schema would be applied, otherwise it is a
            caller responsibility to provide (or do not) some logging configuration. The latter can be useful when the
            outer code makes sequential calls to this API so it is unwanted to append the logging handlers every time
            (e.g. when unit-testing)

    Returns:
        0 on success, -1 otherwise
    """

    if sys_argv is None:
        sys_argv = sys.argv[1:]

    args = parse_args(sys_argv)

    if args is not None and args.subcommand == 'gui':
        gui_args = [arg for arg in sys_argv if arg != 'gui']
        import stm32pio.gui.app as gui_app
        return gui_app.main(sys_argv=gui_args).exec_()
    elif args is not None and args.subcommand is not None:
        logger = setup_logging(verbose=args.verbose,
                               dummy=not should_setup_logging)
    else:
        print("\nNo arguments were given, exiting...")
        return 0

    project = None

    # Main routine
    try:
        if args.subcommand == 'init':
            project = stm32pio.core.project.Stm32pio(
                args.path,
                parameters={'project': {
                    'board': args.board
                }},
                instance_options={'save_on_destruction': True})
            if args.store_content:
                project.config.save_content_as_ignore_list()
            if project.config.get('project', 'board') == '':
                logger.warning(
                    "PlatformIO board identifier is not specified, it will be needed on PlatformIO project "
                    "creation. Type 'pio boards' or go to https://platformio.org to find an appropriate "
                    "identifier")
            logger.info(
                f"project has been initialized. You can now edit {stm32pio.core.settings.config_file_name} "
                "config file")
            if args.editor:
                project.start_editor(args.editor)

        elif args.subcommand == 'generate':
            project = stm32pio.core.project.Stm32pio(args.path)
            project.generate_code()
            if args.with_build:
                project.build()
            if args.editor:
                project.start_editor(args.editor)

        elif args.subcommand == 'pio_init':
            project = stm32pio.core.project.Stm32pio(
                args.path,
                parameters={'project': {
                    'board': args.board
                }},
                instance_options={'save_on_destruction': True})
            project.pio_init()

        elif args.subcommand == 'patch':
            project = stm32pio.core.project.Stm32pio(args.path)
            project.patch()

        elif args.subcommand == 'new':
            project = stm32pio.core.project.Stm32pio(
                args.path,
                parameters={'project': {
                    'board': args.board
                }},
                instance_options={'save_on_destruction': True})
            if args.store_content:
                project.config.save_content_as_ignore_list()
            if project.config.get('project', 'board') == '':
                logger.info(
                    f"project has been initialized. You can now edit {stm32pio.core.settings.config_file_name} "
                    "config file")
                raise Exception(
                    "PlatformIO board identifier is not specified, it is needed for PlatformIO project "
                    "creation. Type 'pio boards' or go to https://platformio.org to find an appropriate "
                    "identifier")
            project.generate_code()
            project.pio_init()
            project.patch()
            if args.with_build:
                project.build()
            if args.editor:
                project.start_editor(args.editor)

        elif args.subcommand == 'status':
            project = stm32pio.core.project.Stm32pio(args.path)
            print(project.state)

        elif args.subcommand == 'validate':
            project = stm32pio.core.project.Stm32pio(args.path)
            print(project.validate_environment())

        elif args.subcommand == 'clean':
            project = stm32pio.core.project.Stm32pio(args.path)
            if args.store_content:
                project.config.save_content_as_ignore_list()
            else:
                project.clean(quiet_on_cli=args.quiet)

    # Global errors catching. Core library is designed to throw the exception in cases when there is no sense to
    # proceed. Of course this also suppose to handle any unexpected behavior, too
    except Exception:
        stm32pio.core.logging.log_current_exception(
            logger,
            config=project.config if
            (project is not None and hasattr(project, 'config')) else None)
        return -1

    return 0
コード例 #4
0
ファイル: test_unit.py プロジェクト: ussserrr/stm32pio
    def test_patch(self):
        """
        Check that the new parameters have been added, modified ones have been updated and existing parameters didn't
        gone. Also, check for unnecessary folders deletion
        """
        project = stm32pio.core.project.Stm32pio(STAGE_PATH)

        header = inspect.cleandoc('''
            ; This is a test config .ini file
            ; with a comment. It emulates a real
            ; platformio.ini file
        ''') + '\n'
        test_content = header + inspect.cleandoc('''
            [platformio]
            include_dir = this s;789hould be replaced
                let's add some tricky content
            ; there should appear a new parameter
            test_key3 = this should be preserved
                alright?

            [test_section]
            test_key1 = test_value1
            test_key2 = 123
        ''') + '\n'
        STAGE_PATH.joinpath('platformio.ini').write_text(test_content)
        STAGE_PATH.joinpath('include').mkdir()

        project.patch()

        with self.subTest():
            self.assertFalse(STAGE_PATH.joinpath('include').is_dir(),
                             msg="'include' has not been deleted")

        original_test_config = configparser.ConfigParser(interpolation=None)
        original_test_config.read_string(test_content)

        patched_config = configparser.ConfigParser(interpolation=None)
        patch_config = configparser.ConfigParser(interpolation=None)
        patch_config.read_string(
            project.config.get('project', 'platformio_ini_patch_content'))

        patched_content = STAGE_PATH.joinpath('platformio.ini').read_text()
        patched_config.read_string(patched_content)
        self.assertGreater(len(patched_content), 0)

        for patch_section in patch_config.sections():
            self.assertTrue(patched_config.has_section(patch_section),
                            msg=f"{patch_section} is missing")
            for patch_key, patch_value in patch_config.items(patch_section):
                self.assertEqual(
                    patched_config.get(patch_section, patch_key,
                                       fallback=None),
                    patch_value,
                    msg=
                    f"{patch_section}: {patch_key}={patch_value} is missing or incorrect in the "
                    "patched config")

        for original_section in original_test_config.sections():
            self.assertTrue(
                patched_config.has_section(original_section),
                msg=f"{original_section} from the original config is missing")
            for original_key, original_value in original_test_config.items(
                    original_section):
                # We've already checked patch parameters so skip them
                if not patch_config.has_option(original_section, original_key):
                    self.assertEqual(
                        patched_config.get(original_section, original_key),
                        original_value,
                        msg=
                        f"{original_section}: {original_key}={original_value} is corrupted"
                    )

        self.assertIn(header,
                      patched_content,
                      msg='Header should be preserved')
コード例 #5
0
def main(sys_argv: List[str] = None, should_setup_logging: bool = True) -> int:
    """
    Entry point to the CLI edition of application. Since this is a highest-order wrapper, it can be used to
    programmatically the application (for testing, embedding, etc.). Example:

        ret_code = stm32pio.app.main(sys_argv=['new', '-d', '~/path/to/project', '-b', 'nucleo_f031k6', '--with-build'])

    :param sys_argv: list of CLI arguments
    :param should_setup_logging: if True, a reasonable default logging schema would be applied, otherwise it is on
    caller to resolve (or not) some logging configuration. The latter can be useful when an outer code makes sequential
    calls to this API so it is unwanted to append logging handlers every time (e.g. when unit-testing)
    :return: 0 on success, -1 otherwise
    """

    if sys_argv is None:
        sys_argv = sys.argv[1:]

    args = parse_args(sys_argv)

    if args is not None and args.command == 'gui':
        gui_args = [arg for arg in sys_argv if arg != 'gui']
        import stm32pio.gui.app as gui
        app = gui.create_app(sys_argv=gui_args)
        return app.exec_()
    elif args is not None and args.command is not None:
        logger = setup_logging(verbose=args.verbose,
                               dummy=not should_setup_logging)
    else:
        print("\nNo arguments were given, exiting...")
        return 0

    project = None

    # Wrap the main routine into try...except to gently handle possible error (API is designed to throw in certain
    # situations when it doesn't make much sense to continue with the met conditions)
    try:
        if args.command == 'init':
            project = stm32pio.core.project.Stm32pio(
                args.path,
                parameters={'project': {
                    'board': args.board
                }},
                save_on_destruction=True)
            if args.store_content:
                project.config.set_content_as_ignore_list()
            if project.config.get('project', 'board') == '':
                logger.warning(no_board_message)
            project.inspect_ioc_config()
            logger.info(init_message)
            if args.editor:
                project.start_editor(args.editor)

        elif args.command == 'generate':
            project = stm32pio.core.project.Stm32pio(args.path)
            if project.config.get('project', 'inspect_ioc',
                                  fallback='0').lower(
                                  ) in stm32pio.core.settings.yes_options:
                project.inspect_ioc_config()
            project.generate_code()
            if args.with_build:
                project.build()
            if args.editor:
                project.start_editor(args.editor)

        elif args.command == 'pio_init':
            project = stm32pio.core.project.Stm32pio(
                args.path,
                parameters={'project': {
                    'board': args.board
                }},
                save_on_destruction=True)
            if project.config.get('project', 'inspect_ioc',
                                  fallback='0').lower(
                                  ) in stm32pio.core.settings.yes_options:
                project.inspect_ioc_config()
            project.pio_init()

        elif args.command == 'patch':
            project = stm32pio.core.project.Stm32pio(args.path)
            if project.config.get('project', 'inspect_ioc',
                                  fallback='0').lower(
                                  ) in stm32pio.core.settings.yes_options:
                project.inspect_ioc_config()
            project.patch()

        elif args.command == 'new':
            project = stm32pio.core.project.Stm32pio(
                args.path,
                parameters={'project': {
                    'board': args.board
                }},
                save_on_destruction=True)
            if args.store_content:
                project.config.set_content_as_ignore_list()
            if project.config.get('project', 'board') == '':
                logger.info(init_message)
                raise Exception(no_board_message)
            if project.config.get('project', 'inspect_ioc',
                                  fallback='0').lower(
                                  ) in stm32pio.core.settings.yes_options:
                project.inspect_ioc_config()
            project.generate_code()
            project.pio_init()
            project.patch()
            if args.with_build:
                project.build()
            if args.editor:
                project.start_editor(args.editor)

        elif args.command == 'status':
            project = stm32pio.core.project.Stm32pio(args.path)
            print(project.state)

        elif args.command == 'validate':
            project = stm32pio.core.project.Stm32pio(args.path)
            print(project.validate_environment())

        elif args.command == 'clean':
            project = stm32pio.core.project.Stm32pio(args.path)
            if args.store_content:
                project.config.set_content_as_ignore_list()
                project.config.save()
            else:
                project.clean(quiet=args.quiet)

    except (Exception, ):
        stm32pio.core.log.log_current_exception(
            logger, config=project.config if project is not None else None)
        return -1

    return 0