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))
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.")
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.")
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))
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)
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))
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)
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.")
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.")
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]]
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)))
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)
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)
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
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()
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.")
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'])
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))
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.")
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]))
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)
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()
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))
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()
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
def __call__(self, args): super(PebbleCommand, self).__call__(args) try: self.pebble = self._connect(args) except ConnectionError as e: raise ToolError(str(e))
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)
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))
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