예제 #1
0
    def __call__(self, args):
        super(ScreenshotCommand, self).__call__(args)
        screenshot = Screenshot(self.pebble)
        screenshot.register_handler("progress", self._handle_progress)

        self.progress_bar.start()
        try:
            image = screenshot.grab_image()
        except ScreenshotError as e:
            if self.pebble.firmware_version.major == 3 and self.pebble.firmware_version.minor == 2:
                # PBL-21154: Screenshots failing with error code 2 (out of memory)
                raise ToolError(
                    str(e) +
                    " (screenshots are known to be broken using firmware 3.2; try the emulator.)"
                )
            else:
                raise ToolError(str(e) + " (try rebooting the watch)")
        if not args.no_correction:
            image = self._correct_colours(image)
        image = self._roundify(image)
        self.progress_bar.finish()

        filename = self._generate_filename(
        ) if args.filename is None else args.filename
        png.from_array(image, mode='RGBA;8').save(filename)
        print("Saved screenshot to {}".format(filename))
        if not args.no_open:
            self._open(os.path.abspath(filename))
예제 #2
0
def check_npm():
    try:
        npm_version = subprocess.check_output(["npm", "--version"]).strip()
        if version_to_key(npm_version)[0] < 3:
            raise ToolError(
                "We require npm3; you are using version {}.".format(
                    npm_version))
    except subprocess.CalledProcessError:
        raise ToolError(u"You must have npm ≥ 3.0.0 available on your path.")
예제 #3
0
 def _authenticate(self):
     oauth = get_default_account().bearer_token
     self.send_packet(WebSocketProxyAuthenticationRequest(token=oauth), target=MessageTargetPhone())
     target, packet = self.read_packet()
     if isinstance(packet, WebSocketProxyAuthenticationResponse):
         if packet.status != WebSocketProxyAuthenticationResponse.StatusCode.Success:
             raise ToolError("Failed to authenticate to the CloudPebble proxy.")
     else:
         logger.info("Got unexpected message from proxy: %s", packet)
         raise ToolError("Unexpected message from CloudPebble proxy.")
예제 #4
0
파일: create.py 프로젝트: mddub/pebble-tool
def _copy_template(name, directory_list, appinfo_list, file_list,
                   create_dir_list):
    try:
        project_path = name
        project_name = os.path.split(project_path)[1]
        project_root = os.path.join(os.getcwd(), project_path)
        os.mkdir(project_path)
    except OSError as e:
        if e.errno == errno.EEXIST:
            raise ToolError(
                "A directory called '{}' already exists.".format(project_name))
        raise

    for directory in directory_list:
        if os.path.exists(directory):
            template_path = directory
            break
    else:
        raise ToolError(
            "Can't create that sort of project with the current SDK.")

    for appinfo_path in appinfo_list:
        appinfo_path = os.path.join(template_path, appinfo_path)
        if os.path.exists(appinfo_path):
            file_list.append((appinfo_path,
                              os.path.join(project_root,
                                           os.path.basename(appinfo_path))))
            break
    else:
        raise ToolError("Couldn't find an appinfo-like file.")

    for file_path in file_list:
        if isinstance(file_path, basestring):
            origin_path = os.path.join(template_path, file_path)
            target_path = os.path.join(project_root, file_path)
        else:
            origin_path = os.path.join(template_path, file_path[0])
            target_path = os.path.join(project_root, file_path[1])

        if os.path.exists(origin_path):
            try:
                os.makedirs(os.path.dirname(target_path))
            except OSError as e:
                if e.errno != errno.EEXIST:
                    raise
            with open(origin_path) as f:
                template = Template(f.read())
            with open(target_path, 'w') as f:
                f.write(
                    template.substitute(uuid=str(uuid4()),
                                        project_name=project_name,
                                        display_name=project_name,
                                        sdk_version=SDK_VERSION))
예제 #5
0
    def __call__(self, args):
        super(GdbCommand, self).__call__(args)
        # We poke around in the ManagedEmulatorTransport, so it's important that we actually have one.
        # Just asserting is okay because this should already be enforced by valid_connections.
        assert isinstance(self.pebble.transport, ManagedEmulatorTransport)

        platform = self.pebble.transport.platform
        sdk_version = self.pebble.transport.version
        gdb_port = self.pebble.transport.qemu_gdb_port
        if gdb_port is None:
            raise ToolError(
                "The emulator does not have gdb support. Try killing and re-running it."
            )

        sdk_root = sdk_manager.path_for_sdk(sdk_version)
        fw_elf = os.path.join(sdk_root, 'pebble', platform, 'qemu',
                              '{}_sdk_debug.elf'.format(platform))
        if not os.path.exists(fw_elf):
            raise ToolError(
                "SDK {} does not support app debugging. You need at least SDK 3.10."
                .format(sdk_version))

        base_address = self._find_app_load_offset(fw_elf)

        app_elf_path = os.path.join(os.getcwd(), 'build', platform,
                                    'pebble-app.elf')
        if not os.path.exists(app_elf_path):
            raise ToolError(
                "No app debugging information available. "
                "You must be in a project directory and have built the app.")

        offsets = self._find_real_app_section_offsets(base_address,
                                                      app_elf_path)

        gdb_commands = [
            "target remote :{}".format(gdb_port),
            "set confirm off",
            'add-symbol-file "{elf}" {text} -s .data {data} -s .bss {bss}'.
            format(elf=app_elf_path, **offsets),
            "set confirm on",
            "break app_crashed",  # app crashes (as of FW 3.10) go through this symbol for our convenience.
            'echo \nPress ctrl-D or type \'quit\' to exit.\n',
            'echo Try `pebble gdb --help` for a short cheat sheet.\n'
        ]

        gdb_args = ['arm-none-eabi-gdb', fw_elf, '-q'
                    ] + ['--ex={}'.format(x) for x in gdb_commands]

        # Ignore SIGINT, or we'll die every time the user tries to pause execution.
        signal.signal(signal.SIGINT, signal.SIG_IGN)

        subprocess.call(gdb_args)
예제 #6
0
    def __call__(self, args):
        super(NewProjectCommand, self).__call__(args)

        template_paths = [
            os.path.join(self.get_sdk_path(), 'pebble', 'common', 'templates'),
            os.path.join(os.path.dirname(__file__), '..', '..', 'sdk',
                         'templates')
        ]

        sdk = self.sdk or sdk_version()
        sdk2 = (sdk == "2.9")

        if args.rocky:
            if sdk2:
                raise ToolError("--rocky is not compatible with SDK 2.9")
            if args.simple or args.worker:
                raise ToolError(
                    "--rocky is incompatible with --simple and --worker")
            options = ['rocky']
        else:
            options = ['app']
            if args.javascript:
                options.append('javascript')
            if args.simple:
                options.append('simple')
            if args.worker:
                options.append('worker')

        # Hack for old SDKs that need an appinfo, because the declarative system can't
        # handle "this, but only if not that." For "tintin" SDKs and unparseble
        # versions, assume this hack is not needed.
        version_number = version_to_key(sdk)
        if version_number[:5] != (0, 0, 0, 0, 0) and \
           version_number < (3, 13, 0):
            options.append('appinfo')

        with open(
                extant_path(
                    os.path.join(x, "templates.json")
                    for x in template_paths)) as f:
            template_layout = json.load(f)

        _copy_from_template(template_layout, extant_path(template_paths),
                            args.name, options)

        post_event("sdk_create_project",
                   javascript=args.javascript or args.rocky,
                   worker=args.worker,
                   rocky=args.rocky)
        print("Created new project {}".format(args.name))
예제 #7
0
파일: debug.py 프로젝트: mddub/pebble-tool
    def __call__(self, args):
        super(GdbCommand, self).__call__(args)
        # We poke around in the ManagedEmulatorTransport, so it's important that we actually have one.
        # Just asserting is okay because this should already be enforced by valid_connections.
        assert isinstance(self.pebble.transport, ManagedEmulatorTransport)
        add_tools_to_path()

        platform = self.pebble.transport.platform
        sdk_version = self.pebble.transport.version
        gdb_port = self.pebble.transport.qemu_gdb_port
        if gdb_port is None:
            raise ToolError("The emulator does not have gdb support. Try killing and re-running it.")

        sdk_root = sdk_manager.path_for_sdk(sdk_version)
        self._fw_elf = os.path.join(sdk_root, 'pebble', platform, 'qemu', '{}_sdk_debug.elf'.format(platform))

        if not os.path.exists(self._fw_elf):
            raise ToolError("SDK {} does not support app debugging. You need at least SDK 3.10.".format(sdk_version))

        app_elf_path = os.path.join(os.getcwd(), 'build', platform, 'pebble-app.elf')
        if not os.path.exists(app_elf_path):
            raise ToolError("No app debugging information available. "
                            "You must be in a project directory and have built the app.")

        gdb_commands = [
            "set charset US-ASCII",  # Avoid a bug in the ancient version of libiconv apple ships.
            "target remote :{}".format(gdb_port),
            "set confirm off",
            self._get_symbol_command(app_elf_path, 'app')
        ]

        # Optionally add the worker symbols, if any exist.
        worker_elf_path = os.path.join(os.getcwd(), 'build', platform, 'pebble-worker.elf')
        if os.path.exists(worker_elf_path):
            gdb_commands.append(self._get_symbol_command(worker_elf_path, 'worker'))

        gdb_commands.extend([
            "set confirm on",
            "break app_crashed",  # app crashes (as of FW 3.10) go through this symbol for our convenience.
            'echo \nPress ctrl-D or type \'quit\' to exit.\n',
            'echo Try `pebble gdb --help` for a short cheat sheet.\n',
        ])

        gdb_args = ['arm-none-eabi-gdb', self._fw_elf, '-q'] + ['--ex={}'.format(x) for x in gdb_commands]

        # Ignore SIGINT, or we'll die every time the user tries to pause execution.
        signal.signal(signal.SIGINT, signal.SIG_IGN)

        subprocess.call(gdb_args)
예제 #8
0
 def _wait_for_qemu(self):
     logger.info("Waiting for the firmware to boot.")
     for i in range(20):
         time.sleep(0.2)
         try:
             s = socket.create_connection(
                 ('localhost', self.qemu_serial_port))
         except socket.error:
             logger.debug("QEMU not ready yet.")
             pass
         else:
             break
     else:
         post_event("qemu_launched",
                    success=False,
                    reason="qemu_launch_timeout")
         raise ToolError("Emulator launch timed out.")
     received = b''
     while True:
         try:
             received += s.recv(256)
         except socket.error as e:
             # Ignore "Interrupted system call"
             if e.errno != errno.EINTR:
                 raise
         if b"<SDK Home>" in received or b"<Launcher>" in received or b"Ready for communication" in received:
             break
     s.close()
     post_event("qemu_launched", success=True)
     logger.info("Firmware booted.")
예제 #9
0
    def copy_group(group, must_succeed=True):
        """
        Copies the files described by a subgroup of the main template definition.
        :param group: The group to copy
        :type group: dict[str, str | list[str] | dict[str, str | list[str]]]
        :param must_succeed: If nothing is copied, and this is True, throw a ToolError.
        :type must_succeed: bool
        """
        copied_files = 0

        for dest, origins in iteritems(group):
            target_path = os.path.join(substitute(project_root), dest)
            if origins is None:
                _mkdirs(target_path)
                continue

            if isinstance(origins, string_types):
                origins = [origins]

            origin_path = extant_path(
                os.path.join(template_root, x) for x in origins)
            if origin_path is not None:
                copied_files += 1
                _mkdirs(target_path)
                with open(origin_path) as f:
                    template_content = f.read()
                with open(substitute(target_path), 'w') as f:
                    f.write(substitute(template_content))

        if must_succeed and copied_files == 0:
            raise ToolError(
                "Can't create that sort of project with the current SDK.")
예제 #10
0
def get_emulator_info(platform, version=None):
    info = get_all_emulator_info().get(platform, None)

    # If we have nothing for the platform, it's None
    if info is None:
        return None

    # If a specific version was requested, return that directly.
    if version is not None:
        return info.get(version, None)

    # If a version wasn't requested, look for one that's alive.
    # If exactly one is alive, return that.
    alive = []
    for sdk_version, sdk_info in iteritems(info):
        if ManagedEmulatorTransport.is_emulator_alive(platform, sdk_version):
            alive.append(sdk_version)
        else:
            # Clean up dead entries that are left hanging around.
            update_emulator_info(platform, sdk_version, None)
    if len(alive) > 1:
        raise ToolError(
            "There are multiple {} emulators (versions {}) running. You must specify a version."
            .format(platform, ', '.join(alive)))
    elif len(alive) == 0:
        return None
    else:
        return info[alive[0]]
예제 #11
0
 def ensure_satisfied(self):
     unsatisfied = self.unsatisfied_requirements()
     if len(unsatisfied) > 0:
         raise ToolError(
             "This SDK has the following unmet requirements: {}\n"
             "Try updating the pebble tool.".format(", ".join(
                 str(x) for x in unsatisfied)))
예제 #12
0
 def __call__(self, args):
     super(PackageManager, self).__call__(args)
     if not isinstance(self.project, NpmProject):
         raise ToolError(
             "Package management is only available on projects using package.json. "
             "Try pebble convert-project.")
     args.sub_func(args)
예제 #13
0
    def __call__(self, args):
        BaseCommand.__call__(self, args)
        self.add_arm_tools_to_path()
        sys.path.append(os.path.join(sdk_path(), 'pebble', 'common', 'tools'))

        paths = []
        if args.elf_path is None:
            try:
                project = PebbleProject()
                paths = ['build/{}/pebble-app.elf'.format(x) for x in project.target_platforms]
            except PebbleProjectException:
                raise ToolError("This is not a valid Pebble project. Please instead specify a valid elf path.")
            except Exception as e:
                print(e)
        else:
            paths.append(args.elf_path)

        # This is Super Special Magic of some form that comes from the SDK.
        import binutils

        for path in paths:
            print("\n======{}======".format(path))
            sections = binutils.analyze_elf(path, 'bdt', use_fast_nm=True)

            for s in sections.itervalues():
                s.pprint(args.summary, args.verbose)
예제 #14
0
 def connect(self):
     account = get_default_account()
     if not account.is_logged_in:
         raise ToolError("You must be logged in ('pebble login') to use the CloudPebble connection.")
     self.ws = websocket.create_connection(CP_TRANSPORT_HOST)
     self._authenticate()
     self._wait_for_phone()
     self._phone_connected = True
예제 #15
0
 def do_install(cls, args):
     try:
         npm.invoke_npm(
             ["install", "--save", "--ignore-scripts", args.package])
         npm.invoke_npm(["dedupe"])
         npm.sanity_check()
     except subprocess.CalledProcessError:
         raise ToolError()
예제 #16
0
 def _wait_for_phone(self):
     print("Waiting for phone to connect...")
     target, packet = self.read_packet()
     if isinstance(packet, WebSocketProxyConnectionStatusUpdate):
         if packet.status == WebSocketProxyConnectionStatusUpdate.StatusCode.Connected:
             print("Connected.")
             return
     raise ToolError("Unexpected message when waiting for phone connection.")
예제 #17
0
    def _fix_python(self):
        # First figure out what 'python' means:
        try:
            version = int(subprocess.check_output(["python", "-c", "import sys; print(sys.version_info[0])"]).strip())
        except (subprocess.CalledProcessError, ValueError):
            raise ToolError("'python' doesn't mean anything on this system.")

        if version != 2:
            try:
                python2_version = int(subprocess.check_output(["python2", "-c",
                                                                "import sys; print(sys.version_info[1])"]).strip())
            except (subprocess.CalledProcessError, ValueError):
                raise ToolError("Can't find a python2 interpreter.")
            if python2_version < 6:
                raise ToolError("Require python 2.6 or 2.7 to run the build tools; got 2.{}".format(python2_version))
            # We have a viable python2. Use our hack to stick 'python' into the path.
            os.environ['PATH'] = '{}:{}'.format(os.path.normpath(os.path.dirname(__file__)), os.environ['PATH'])
예제 #18
0
파일: debug.py 프로젝트: mddub/pebble-tool
    def _find_app_load_offset(cls, fw_elf, kind):
        elf_sections = subprocess.check_output(["arm-none-eabi-readelf", "-W", "-s", fw_elf])

        # Figure out where we load the app into firmware memory
        for line in elf_sections.split(b'\n'):
            if b'__{}_flash_load_start__'.format(kind) in line:
                return int(line.split()[1], 16)
        else:
            raise ToolError("Couldn't find the {} address offset.".format(kind))
예제 #19
0
 def _connect(self, args):
     self._set_debugging(args.v)
     if getattr(args, 'phone', None):
         return self._connect_phone(args.phone)
     elif getattr(args, 'qemu', None):
         return self._connect_qemu(args.qemu)
     elif getattr(args, 'emulator', None):
         return self._connect_emulator(args.emulator, args.sdk)
     elif getattr(args, 'cloudpebble', None):
         return self._connect_cloudpebble()
     elif getattr(args, 'serial', None):
         return self._connect_serial(args.serial)
     else:
         if 'phone' in self.valid_connections and 'PEBBLE_PHONE' in os.environ:
             return self._connect_phone(os.environ['PEBBLE_PHONE'])
         elif 'qemu' in self.valid_connections and 'PEBBLE_QEMU' in os.environ:
             return self._connect_qemu(os.environ['PEBBLE_QEMU'])
         elif 'cloudpebble' in self.valid_connections and os.environ.get('PEBBLE_CLOUDPEBBLE', False):
             return self._connect_cloudpebble()
         elif 'serial' in self.valid_connections and 'PEBBLE_BT_SERIAL' in os.environ:
             return self._connect_serial(os.environ['PEBBLE_BT_SERIAL'])
         elif 'emulator' in self.valid_connections:
             running = []
             emulator_platform = None
             emulator_sdk = None
             if 'PEBBLE_EMULATOR' in os.environ:
                 emulator_platform = os.environ['PEBBLE_EMULATOR']
                 if emulator_platform not in pebble_platforms:
                     raise ToolError("PEBBLE_EMULATOR is set to '{}', which is not a valid platform "
                                     "(pick from {})".format(emulator_platform, ', '.join(pebble_platforms)))
                 emulator_sdk = os.environ.get('PEBBLE_EMULATOR_VERSION', sdk_version())
             else:
                 for platform, sdks in get_all_emulator_info().items():
                     for sdk in sdks:
                         if ManagedEmulatorTransport.is_emulator_alive(platform, sdk):
                             running.append((platform, sdk))
                 if len(running) == 1:
                     emulator_platform, emulator_sdk = running[0]
                 elif len(running) > 1:
                     raise ToolError("Multiple emulators are running; you must specify which to use.")
             if emulator_platform is not None:
                 return self._connect_emulator(emulator_platform, emulator_sdk)
     raise ToolError("No pebble connection specified.")
예제 #20
0
def sanity_check():
    if not os.path.exists('node_modules'):
        return
    for d in os.listdir('node_modules'):
        if 'node_modules' in os.listdir(os.path.join('node_modules', d)):
            raise ToolError(
                "Conflicting npm dependency in {}: {}. Please resolve before continuing."
                .format(
                    d,
                    os.listdir(os.path.join('node_modules', d,
                                            'node_modules'))[0]))
예제 #21
0
파일: base.py 프로젝트: tulth/pebble-tool
    def _connect_args(cls, args):
        emulator_platform = getattr(args, 'emulator', None)
        emulator_sdk = getattr(args, 'sdk', None)
        if emulator_platform:
            return emulator_platform, emulator_sdk
        elif 'PEBBLE_EMULATOR' in os.environ:
            emulator_platform = os.environ['PEBBLE_EMULATOR']
            if emulator_platform not in pebble_platforms:
                raise ToolError(
                    "PEBBLE_EMULATOR is set to '{}', which is not a valid platform "
                    "(pick from {})".format(emulator_platform,
                                            ', '.join(pebble_platforms)))
            emulator_sdk = os.environ.get('PEBBLE_EMULATOR_VERSION',
                                          sdk_version())
        else:
            running = cls.get_running_emulators()
            if len(running) == 1:
                emulator_platform, emulator_sdk = running[0]
            elif len(running) > 1:
                raise ToolError(
                    "Multiple emulators are running; you must specify which to use."
                )

        return (emulator_platform, emulator_sdk)
예제 #22
0
파일: debug.py 프로젝트: tulth/pebble-tool
    def _ensure_correct_app(self, try_install=True):
        project = PebbleProject()
        if project.project_type != 'native':
            raise ToolError("Only native apps can be debugged using gdb.")

        current_app_uuid = self.pebble.send_and_read(
            AppRunState(data=AppRunStateRequest()), AppRunState).data.uuid
        if current_app_uuid != project.uuid:
            print("Launching {}...".format(project.long_name))
            # Try launching the app we want. This just does nothing if the app doesn't exist.
            # Edge case: the app exists in blobdb but isn't installed. This shouldn't come up with the pebble tool.
            queue = self.pebble.get_endpoint_queue(AppRunState)
            try:
                self.pebble.send_packet(
                    AppRunState(data=AppRunStateStart(uuid=project.uuid)))
                while True:
                    packet = queue.get(timeout=0.5)
                    if isinstance(packet.data, AppRunStateStart
                                  ) and packet.data.uuid == project.uuid:
                        break
            except TimeoutError:
                if try_install:
                    print("App did not launch. Trying to install it...")
                    try:
                        ToolAppInstaller(self.pebble).install()
                    except IOError:
                        raise ToolError(
                            "The app to debug must be built and installed on the watch."
                        )
                    self._ensure_correct_app(try_install=False)
                else:
                    raise ToolError(
                        "The app to debug must be running on the watch to start gdb."
                    )
            finally:
                queue.close()
예제 #23
0
파일: debug.py 프로젝트: tulth/pebble-tool
    def _find_legacy_app_load_offset(fw_elf, kind):
        """Use readelf to find the app/worker load offset in a legacy 3.x
        firmware debugging symbols ELF where GDB is unable to read the symbols
        itself.
        """
        elf_sections = subprocess.check_output(
            ["arm-none-eabi-readelf", "-W", "-s", fw_elf])

        # Figure out where we load the app into firmware memory
        for line in elf_sections.split(b'\n'):
            if b'__{}_flash_load_start__'.format(kind) in line:
                return int(line.split()[1], 16)
        else:
            raise ToolError(
                "Couldn't find the {} address offset.".format(kind))
예제 #24
0
    def _convert_to_npm(self):
        new_info = {
            'name': self.project.short_name,
            'author': self.project.company_name,
            'version': self.project.version + '.0',
            'private': True,
            'keywords': ['pebble-app'],
            'dependencies': {},
            'pebble': {
                'sdkVersion': self.project.sdk_version,
                'targetPlatforms': self.project.target_platforms,
                'enableMultiJS': self.project.enable_multi_js,
                'capabilities': self.project.capabilities,
                'projectType': self.project.project_type,
                'displayName': self.project.long_name,
                'uuid': str(self.project.uuid),
                'watchapp': {
                    'watchface':
                    self.project.is_watchface,
                    'hiddenApp':
                    self.project.is_hidden,
                    'onlyShownOnCommunication':
                    self.project.is_shown_only_on_communication,
                },
                'resources': self.project.resources,
                'messageKeys': self.project.message_keys,
            }
        }
        if os.path.exists('package.json'):
            with open('package.json') as f:
                try:
                    new_info.update(json.load(f))
                except ValueError:
                    raise ToolError(
                        "An invalid package.json already exists; conversion aborted."
                    )
            copy2('package.json', 'package.json~')
            print(
                "A package.json already exists. It has been backed up to package.json~."
            )
        with open('package.json', 'w') as f:
            json.dump(new_info, f, indent=2, separators=(',', ': '))
        os.unlink('appinfo.json')

        self._ignore_npm()
예제 #25
0
파일: base.py 프로젝트: tulth/pebble-tool
    def _connect(self, args):
        self._set_debugging(args.v)
        for handler_impl in self.valid_connection_handlers():
            if handler_impl.is_selected(args):
                break
        else:
            # No selected transport, fallback to a running emulator if available
            if PebbleTransportEmulator.get_running_emulators():
                handler_impl = PebbleTransportEmulator
            else:
                raise ToolError("No pebble connection specified.")

        transport = handler_impl.get_transport(args)
        connection = PebbleConnection(transport, **self._get_debug_args())
        connection.connect()
        connection.run_async()
        handler_impl.post_connect(connection)
        return connection
예제 #26
0
파일: base.py 프로젝트: tulth/pebble-tool
 def __call__(self, args):
     super(PebbleCommand, self).__call__(args)
     try:
         self.pebble = self._connect(args)
     except ConnectionError as e:
         raise ToolError(str(e))
예제 #27
0
파일: debug.py 프로젝트: tulth/pebble-tool
    def __call__(self, args):
        super(GdbCommand, self).__call__(args)
        # We poke around in the ManagedEmulatorTransport, so it's important that we actually have one.
        # Just asserting is okay because this should already be enforced by valid_connections.
        assert isinstance(self.pebble.transport, ManagedEmulatorTransport)
        self._ensure_correct_app()
        add_tools_to_path()

        platform = self.pebble.transport.platform
        sdk_version = self.pebble.transport.version
        gdb_port = self.pebble.transport.qemu_gdb_port
        if gdb_port is None:
            raise ToolError(
                "The emulator does not have gdb support. Try killing and re-running it."
            )

        sdk_root = sdk_manager.path_for_sdk(sdk_version)
        self._fw_elf = os.path.join(sdk_root, 'pebble', platform, 'qemu',
                                    '{}_sdk_debug.elf'.format(platform))

        if not os.path.exists(self._fw_elf):
            raise ToolError(
                "SDK {} does not support app debugging. You need at least SDK 3.10."
                .format(sdk_version))

        app_elf_path = os.path.join(os.getcwd(), 'build', platform,
                                    'pebble-app.elf')
        if not os.path.exists(app_elf_path):
            raise ToolError(
                "No app debugging information available. "
                "You must be in a project directory and have built the app.")

        if self.pebble.firmware_version.major >= 4:
            # Type information for symbols is not currently written into the
            # debugging symbols generated by fw_elf_obfuscate.py. We must
            # explicitly tell GDB what type the symbols are so that their values
            # can be read.
            app_load_address = '*(void**)&g_app_load_address'
            worker_load_address = '*(void**)&g_worker_load_address'
        else:
            # The version of fw_elf_obfuscate.py which generated the debugging
            # symbol files for 3.x SDKs wrote out the symbol information for
            # variables in a way that caused them to be unavailable to GDB.
            # We have to use readelf to work around that and get the symbol
            # addresses.
            app_load_address = '(void*){:#x}'.format(
                self._find_legacy_app_load_offset(self._fw_elf, 'app'))
            worker_load_address = '(void*){:#x}'.format(
                self._find_legacy_app_load_offset(self._fw_elf, 'worker'))

        gdb_commands = [
            "set charset US-ASCII",  # Avoid a bug in the ancient version of libiconv apple ships.
            "target remote :{}".format(gdb_port),
            "set confirm off",
            self._get_symbol_command(app_elf_path, app_load_address)
        ]

        # Optionally add the worker symbols, if any exist.
        worker_elf_path = os.path.join(os.getcwd(), 'build', platform,
                                       'pebble-worker.elf')
        if os.path.exists(worker_elf_path):
            gdb_commands.append(
                self._get_symbol_command(worker_elf_path, worker_load_address))

        gdb_commands.extend([
            "set confirm on",
            "break app_crashed",  # app crashes (as of FW 3.10) go through this symbol for our convenience.
            'echo \nPress ctrl-D or type \'quit\' to exit.\n',
            'echo Try `pebble gdb --help` for a short cheat sheet.\n',
        ])

        gdb_args = ['arm-none-eabi-gdb', self._fw_elf, '-q'
                    ] + ['--ex={}'.format(x) for x in gdb_commands]

        # Ignore SIGINT, or we'll die every time the user tries to pause execution.
        signal.signal(signal.SIGINT, signal.SIG_IGN)

        subprocess.call(gdb_args)
예제 #28
0
    def __call__(self, args):
        super(NewProjectCommand, self).__call__(args)

        # User can give a path to a new project dir
        project_path = args.name
        project_name = os.path.split(project_path)[1]
        project_root = os.path.join(os.getcwd(), project_path)

        project_src = os.path.join(project_root, "src")

        # Create directories
        try:
            os.makedirs(project_root)
            os.makedirs(os.path.join(project_root, "resources"))
            os.makedirs(project_src)
        except OSError as e:
            if e.errno == errno.EEXIST:
                raise ToolError(
                    "A directory called '{}' already exists.".format(
                        args.name))
            raise

        project_template_path = os.path.join(self.get_sdk_path(), 'pebble',
                                             'common', 'templates')
        if not os.path.exists(project_template_path):
            project_template_path = os.path.join(os.path.dirname(__file__),
                                                 '..', '..', 'sdk',
                                                 'templates')

        # Create main .c file
        if args.simple:
            default_main = os.path.join(project_template_path, 'simple.c')
        else:
            default_main = os.path.join(project_template_path, 'main.c')
        copy2(default_main,
              os.path.join(project_src, "{}.c".format(project_name)))

        # Add appinfo.json file
        with open(os.path.join(project_template_path, 'appinfo.json')) as f:
            appinfo = Template(f.read())

        with open(os.path.join(project_root, "appinfo.json"), "w") as f:
            f.write(
                appinfo.substitute(uuid=str(uuid4()),
                                   project_name=project_name,
                                   sdk_version=SDK_VERSION))

        # Add .gitignore file
        copy2(os.path.join(project_template_path, 'gitignore'),
              os.path.join(project_root, '.gitignore'))

        # Add javascript files if applicable
        if args.javascript:
            project_js_src = os.path.join(project_src, "js")
            os.makedirs(project_js_src)

            try:
                copy2(os.path.join(project_template_path, 'app.js'),
                      os.path.join(project_js_src, 'app.js'))
            except IOError as e:
                if e.errno != errno.ENOENT:
                    raise e
                copy2(os.path.join(project_template_path, 'pebble-js-app.js'),
                      os.path.join(project_js_src, 'pebble-js-app.js'))

        # Add background worker files if applicable
        if args.worker:
            project_worker_src = os.path.join(project_root, "worker_src")
            os.makedirs(project_worker_src)
            # Add simple source file
            copy2(
                os.path.join(project_template_path, 'worker.c'),
                os.path.join(project_worker_src,
                             "{}_worker.c".format(project_name)))

        # Add wscript file
        if self.sdk == "2.9" or (self.sdk is None and sdk_version() == "2.9"):
            copy2(os.path.join(project_template_path, 'wscript_sdk2'),
                  os.path.join(project_root, "wscript"))
        else:
            copy2(os.path.join(project_template_path, 'wscript'),
                  os.path.join(project_root, "wscript"))

        post_event("sdk_create_project",
                   javascript=args.javascript,
                   worker=args.worker)
        print("Created new project {}".format(args.name))
예제 #29
0
def _copy_from_template(template, template_root, path, options):
    """
    Given a description of a template and a pointer to the root that description uses,
    instantiates a template at the given path, using the given options
    :param template: The dictionary describing a template
    :type template: dict[str, dict[str, str | list[str] | dict[str, dict[str, str | list[str]]]]]
    :param template_root: The path to the root of the source files described by template
    :type template_root: str
    :param path: The path to the directory to create for the template.
    :type path: str
    :param options: The type of template to create
    :type options: list[str]
    """
    project_path = path
    project_name = os.path.split(project_path)[1]
    project_root = os.path.join(os.getcwd(), project_path)
    uuid = uuid4()

    try:
        os.mkdir(project_path)
    except OSError as e:
        if e.errno == errno.EEXIST:
            raise ToolError(
                "A directory called '{}' already exists.".format(project_name))
        raise

    def substitute(template_content):
        return Template(template_content).substitute(uuid=str(uuid),
                                                     project_name=project_name,
                                                     display_name=project_name,
                                                     project_name_c=re.sub(
                                                         r'[^a-zA-Z0-9_]+',
                                                         '_', project_name),
                                                     sdk_version=SDK_VERSION)

    def copy_group(group, must_succeed=True):
        """
        Copies the files described by a subgroup of the main template definition.
        :param group: The group to copy
        :type group: dict[str, str | list[str] | dict[str, str | list[str]]]
        :param must_succeed: If nothing is copied, and this is True, throw a ToolError.
        :type must_succeed: bool
        """
        copied_files = 0

        for dest, origins in iteritems(group):
            target_path = os.path.join(substitute(project_root), dest)
            if origins is None:
                _mkdirs(target_path)
                continue

            if isinstance(origins, string_types):
                origins = [origins]

            origin_path = extant_path(
                os.path.join(template_root, x) for x in origins)
            if origin_path is not None:
                copied_files += 1
                _mkdirs(target_path)
                with open(origin_path) as f:
                    template_content = f.read()
                with open(substitute(target_path), 'w') as f:
                    f.write(substitute(template_content))

        if must_succeed and copied_files == 0:
            raise ToolError(
                "Can't create that sort of project with the current SDK.")

    try:
        copy_group(template.get('default', {}), must_succeed=False)
        copy_group(template.get(options[0], {}).get('default', {}))
        for option in options[1:]:
            copy_group(template.get(options[0], {}).get(option, {}))
    except Exception:
        shutil.rmtree(project_root)
        raise