Exemplo n.º 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")
Exemplo n.º 2
0
    def test_generate_code(self):
        """
        Check whether files and folders have been created (by STM32CubeMX)
        """
        project = stm32pio.core.project.Stm32pio(
            STAGE_PATH, parameters={'project': {
                'board': PROJECT_BOARD
            }})
        project.generate_code()

        # Assuming that the presence of these files indicating a success
        files_should_be_present = ['Src/main.c', 'Inc/main.h']
        for file in files_should_be_present:
            with self.subTest(msg=f"{file} hasn't been created"):
                self.assertEqual(STAGE_PATH.joinpath(file).is_file(), True)
Exemplo n.º 3
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)
Exemplo n.º 4
0
#     [*]  .ioc file is present
#     [*]  stm32pio initialized
#     [ ]  CubeMX code generated
#     [ ]  PlatformIO project initialized
#     [ ]  PlatformIO project patched
#     [ ]  PlatformIO project built
# or ...
from stm32pio.core.state import ProjectStage
print(project.state[ProjectStage.INITIALIZED] is True)
#     True
# or ...
print(project.state.current_stage)
#     stm32pio initialized

# If we haven't set up logging, messages from the inner logging.Logger instance are not allowed to propagate by default
project.generate_code()  # we do not see any output here

# But we can help it by configuring some logging schema
import logging
logger = logging.getLogger('stm32pio')  # you can also provide a logger to the project instance itself
logger.setLevel(logging.INFO)  # use logging.DEBUG for the verbose output
handler = logging.StreamHandler()  # default STDERR stream
handler.setFormatter(logging.Formatter('%(levelname)s %(message)s'))  # some pretty simple output format
logger.addHandler(handler)

# Or you can just use a built-in logging schema which is basically doing the same stuff for you. Note though, that only
# a single option should be either picked at a time, otherwise records duplication will occur
import stm32pio.cli.app
# logger = stm32pio.cli.app.setup_logging()  # comment section above and uncomment me

# There are multiple ways of logging configuration, it as an essential feature opening doors to many possible library
Exemplo n.º 5
0
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
Exemplo n.º 6
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