Skip to content

Commit e600aee

Browse files
committed
Merge branch 'pebble-packages'
2 parents 9878624 + 39d5e1c commit e600aee

File tree

8 files changed

+380
-81
lines changed

8 files changed

+380
-81
lines changed

pebble_tool/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from .commands import (install, logs, screenshot, timeline, emucontrol, ping, account, repl,
1717
transcription_server, data_logging)
1818
from .commands.sdk import create, emulator
19-
from .commands.sdk.project import analyse_size, convert, debug
19+
from .commands.sdk.project import package, analyse_size, convert, debug
2020
from .exceptions import ToolError
2121
from .sdk import sdk_version
2222
from .util.analytics import wait_for_analytics, analytics_prompt

pebble_tool/commands/sdk/create.py

Lines changed: 100 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,83 +13,85 @@
1313
from pebble_tool.util.analytics import post_event
1414

1515

16+
def _copy_template(name, directory_list, appinfo_list, file_list, create_dir_list):
17+
try:
18+
project_path = name
19+
project_name = os.path.split(project_path)[1]
20+
project_root = os.path.join(os.getcwd(), project_path)
21+
except OSError as e:
22+
if e.errno == errno.EEXIST:
23+
raise ToolError("A directory called '{}' already exists.".format(project_name))
24+
raise
25+
26+
for directory in directory_list:
27+
if os.path.exists(directory):
28+
template_path = directory
29+
break
30+
else:
31+
raise ToolError("Can't create that sort of project with the current SDK.")
32+
33+
for appinfo_path in appinfo_list:
34+
appinfo_path = os.path.join(template_path, appinfo_path)
35+
if os.path.exists(appinfo_path):
36+
file_list.append((appinfo_path, os.path.join(project_root, os.path.basename(appinfo_path))))
37+
break
38+
else:
39+
raise ToolError("Couldn't find an appinfo-like file.")
40+
41+
for file_path in file_list:
42+
if isinstance(file_path, basestring):
43+
origin_path = os.path.join(template_path, file_path)
44+
target_path = os.path.join(project_root, file_path)
45+
else:
46+
origin_path = os.path.join(template_path, file_path[0])
47+
target_path = os.path.join(project_root, file_path[1])
48+
49+
if os.path.exists(origin_path):
50+
try:
51+
os.makedirs(os.path.dirname(target_path))
52+
except OSError as e:
53+
if e.errno != errno.EEXIST:
54+
raise
55+
with open(origin_path) as f:
56+
template = Template(f.read())
57+
with open(target_path, 'w') as f:
58+
f.write(template.substitute(uuid=str(uuid4()),
59+
project_name=project_name,
60+
display_name=project_name,
61+
sdk_version=SDK_VERSION))
62+
63+
1664
class NewProjectCommand(SDKCommand):
1765
"""Creates a new pebble project with the given name in a new directory."""
1866
command = 'new-project'
1967

2068
def __call__(self, args):
2169
super(NewProjectCommand, self).__call__(args)
2270

23-
# User can give a path to a new project dir
2471
project_path = args.name
2572
project_name = os.path.split(project_path)[1]
26-
project_root = os.path.join(os.getcwd(), project_path)
27-
28-
project_src = os.path.join(project_root, "src")
29-
30-
# Create directories
31-
try:
32-
os.makedirs(project_root)
33-
os.makedirs(os.path.join(project_root, "resources"))
34-
os.makedirs(project_src)
35-
except OSError as e:
36-
if e.errno == errno.EEXIST:
37-
raise ToolError("A directory called '{}' already exists.".format(args.name))
38-
raise
39-
40-
project_template_path = os.path.join(self.get_sdk_path(), 'pebble', 'common', 'templates')
41-
if not os.path.exists(project_template_path):
42-
project_template_path = os.path.join(os.path.dirname(__file__), '..', '..', 'sdk', 'templates')
43-
44-
# Create main .c file
45-
if args.simple:
46-
default_main = os.path.join(project_template_path, 'simple.c')
47-
else:
48-
default_main = os.path.join(project_template_path, 'main.c')
49-
copy2(default_main, os.path.join(project_src, "{}.c".format(project_name)))
50-
51-
# Add appinfo.json file
52-
with open(os.path.join(project_template_path, 'appinfo.json')) as f:
53-
appinfo = Template(f.read())
54-
55-
with open(os.path.join(project_root, "appinfo.json"), "w") as f:
56-
f.write(appinfo.substitute(uuid=str(uuid4()), project_name=project_name, sdk_version=SDK_VERSION))
57-
58-
# Add .gitignore file
59-
copy2(os.path.join(project_template_path, 'gitignore'), os.path.join(project_root, '.gitignore'))
60-
61-
# Add javascript files if applicable
73+
sdk2 = self.sdk == "2.9" or (self.sdk is None and sdk_version() == "2.9")
74+
75+
template_paths = [
76+
os.path.join(self.get_sdk_path(), 'pebble', 'common', 'templates', 'app'),
77+
os.path.join(self.get_sdk_path(), 'pebble', 'common', 'templates'),
78+
os.path.join(os.path.dirname(__file__), '..', '..', 'sdk', 'templates'),
79+
]
80+
file_list = [
81+
('gitignore', '.gitignore'),
82+
('simple.c' if args.simple else 'main.c', 'src/{}.c'.format(project_name)),
83+
('wscript_sdk2' if sdk2 else 'wscript', 'wscript')
84+
]
6285
if args.javascript:
63-
project_js_src = os.path.join(project_src, "js")
64-
os.makedirs(project_js_src)
65-
66-
try:
67-
copy2(os.path.join(project_template_path, 'app.js'),
68-
os.path.join(project_js_src, 'app.js'))
69-
except IOError as e:
70-
if e.errno != errno.ENOENT:
71-
raise e
72-
copy2(os.path.join(project_template_path, 'pebble-js-app.js'),
73-
os.path.join(project_js_src, 'pebble-js-app.js'))
74-
75-
# Add background worker files if applicable
86+
file_list.extend([('app.js', 'src/js/app.js'), ('pebble-js-app.js', 'src/js/pebble-js-app.js')])
7687
if args.worker:
77-
project_worker_src = os.path.join(project_root, "worker_src")
78-
os.makedirs(project_worker_src)
79-
# Add simple source file
80-
copy2(os.path.join(project_template_path, 'worker.c'),
81-
os.path.join(project_worker_src, "{}_worker.c".format(project_name)))
82-
83-
# Add wscript file
84-
if self.sdk == "2.9" or (self.sdk is None and sdk_version() == "2.9"):
85-
copy2(os.path.join(project_template_path, 'wscript_sdk2'), os.path.join(project_root, "wscript"))
86-
else:
87-
copy2(os.path.join(project_template_path, 'wscript'), os.path.join(project_root, "wscript"))
88+
file_list.append(('worker.c', 'worker/{}_worker.c'.format(project_name)))
89+
90+
_copy_template(args.name, template_paths, ['package.json', 'appinfo.json'], file_list, ['resources'])
8891

8992
post_event("sdk_create_project", javascript=args.javascript, worker=args.worker)
9093
print("Created new project {}".format(args.name))
9194

92-
9395
@classmethod
9496
def add_parser(cls, parser):
9597
parser = super(NewProjectCommand, cls).add_parser(parser)
@@ -98,3 +100,39 @@ def add_parser(cls, parser):
98100
parser.add_argument("--javascript", action="store_true", help="Generate a JavaScript file.")
99101
parser.add_argument("--worker", action="store_true", help="Generate a background worker.")
100102
return parser
103+
104+
105+
class NewPackageCommand(SDKCommand):
106+
"""Creates a new pebble package (not app or watchface) with the given name in a new directory."""
107+
command = 'new-package'
108+
109+
def __call__(self, args):
110+
super(NewPackageCommand, self).__call__(args)
111+
112+
package_path = args.name
113+
package_name = os.path.split(package_path)[1]
114+
115+
template_paths = [
116+
os.path.join(self.get_sdk_path(), 'pebble', 'common', 'templates', 'lib'),
117+
]
118+
file_list = [
119+
('gitignore', '.gitignore'),
120+
('lib.c', 'src/c/{}.c'.format(package_name)),
121+
('lib.h', 'include/{}.h'.format(package_name)),
122+
'wscript',
123+
]
124+
dir_list = ['src/resources']
125+
if args.javascript:
126+
file_list.append(('lib.js', 'src/js/index.js'))
127+
128+
_copy_template(args.name, template_paths, ['package.json'], file_list, dir_list)
129+
130+
post_event("sdk_create_package", javascript=args.javascript)
131+
print("Created new package {}".format(args.name))
132+
133+
@classmethod
134+
def add_parser(cls, parser):
135+
parser = super(NewPackageCommand, cls).add_parser(parser)
136+
parser.add_argument("name", help="Name of the package you want to create")
137+
parser.add_argument("--javascript", action="store_true", help="Include a js directory.")
138+
return parser

pebble_tool/commands/sdk/project/build.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from pebble_tool.exceptions import BuildError
1010
from pebble_tool.util.analytics import post_event
11+
import pebble_tool.util.npm as npm
1112
from pebble_tool.commands.sdk.project import SDKProjectCommand
1213

1314

@@ -18,6 +19,13 @@ class BuildCommand(SDKProjectCommand):
1819
def __call__(self, args):
1920
super(BuildCommand, self).__call__(args)
2021
start_time = time.time()
22+
if len(self.project.dependencies) > 0:
23+
post_event('app_build_with_npm_deps')
24+
try:
25+
npm.invoke_npm(["install"])
26+
except subprocess.CalledProcessError:
27+
post_event("app_build_failed_npm")
28+
raise BuildError("npm failed.")
2129
try:
2230
waf = list(args.args)
2331
try:

pebble_tool/commands/sdk/project/convert.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,74 @@
88
from shutil import copy2
99

1010
from pebble_tool.commands.sdk.project import SDKProjectCommand
11-
from pebble_tool.sdk.project import PebbleProject, OutdatedProjectException
11+
from pebble_tool.exceptions import OutdatedProjectException, ToolError
12+
from pebble_tool.sdk.project import NpmProject
1213
from pebble_tool.sdk import pebble_platforms
1314

1415

1516
class PblProjectConverter(SDKProjectCommand):
16-
"""Structurally converts an SDK 2 project to an SDK 3 project. Code changes may still be required."""
17+
"""Converts an appinfo project from SDK 2 or SDK 3 to a modern package.json project."""
1718
command = 'convert-project'
1819

1920
def __call__(self, args):
2021
try:
2122
super(PblProjectConverter, self).__call__(args)
22-
print("No conversion required")
23+
if not isinstance(self.project, NpmProject):
24+
self._convert_to_npm()
25+
print("Converted to package.json format.")
26+
else:
27+
print("No conversion required")
2328
except OutdatedProjectException:
2429
self._convert_project()
30+
self._convert_to_npm()
2531
print("Project successfully converted!")
2632

33+
def _convert_to_npm(self):
34+
new_info = {
35+
'name': self.project.short_name,
36+
'author': self.project.company_name,
37+
'version': self.project.version + '.0',
38+
'pebble': {
39+
'sdkVersion': self.project.sdk_version,
40+
'targetPlatforms': self.project.target_platforms,
41+
'enableMultiJS': self.project.enable_multi_js,
42+
'capabilities': self.project.capabilities,
43+
'projectType': self.project.project_type,
44+
'displayName': self.project.long_name,
45+
'uuid': str(self.project.uuid),
46+
'watchapp': {
47+
'watchface': self.project.is_watchface,
48+
'hiddenApp': self.project.is_hidden,
49+
'onlyShownOnCommunication': self.project.is_shown_only_on_communication,
50+
},
51+
'resources': self.project.resources,
52+
'messageKeys': self.project.message_keys,
53+
}
54+
}
55+
if os.path.exists('package.json'):
56+
with open('package.json') as f:
57+
try:
58+
new_info.update(json.load(f))
59+
except ValueError:
60+
raise ToolError("An invalid package.json already exists; conversion aborted.")
61+
copy2('package.json', 'package.json~')
62+
print("A package.json already exists. It has been backed up to package.json~.")
63+
with open('package.json', 'w') as f:
64+
json.dump(new_info, f, indent=2, separators=(',', ': '))
65+
os.unlink('appinfo.json')
66+
67+
self._ignore_npm()
68+
69+
def _ignore_npm(self):
70+
if os.path.exists('.gitignore'):
71+
with open('.gitignore') as f:
72+
content = f.read()
73+
if 'node_modules' in content:
74+
return
75+
76+
with open('.gitignore', 'a') as f:
77+
f.write("\nnode_modules/\n")
78+
2779
def _convert_project(self):
2880
project_root = os.getcwd()
2981
project_template_path = os.path.join(self.get_sdk_path(), 'pebble', 'common', 'templates')
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# encoding: utf-8
2+
from __future__ import absolute_import, print_function
3+
4+
import subprocess
5+
6+
from . import SDKProjectCommand
7+
from pebble_tool.exceptions import ToolError
8+
import pebble_tool.util.npm as npm
9+
10+
__author__ = "katharine"
11+
12+
13+
class PackageManager(SDKProjectCommand):
14+
"""Manages npm packages."""
15+
command = 'package'
16+
has_subcommands = True
17+
18+
def __call__(self, args):
19+
super(PackageManager, self).__call__(args)
20+
args.sub_func(args)
21+
22+
@classmethod
23+
def add_parser(cls, parser):
24+
parser = super(PackageManager, cls).add_parser(parser)
25+
subparsers = parser.add_subparsers(title="subcommand")
26+
27+
install_parser = subparsers.add_parser("install", help="Installs the given package.")
28+
install_parser.add_argument('package', nargs='?', help="npm package to install.")
29+
install_parser.set_defaults(sub_func=cls.do_install)
30+
31+
uninstall_parser = subparsers.add_parser("uninstall", help="Uninstalls the given package.")
32+
uninstall_parser.add_argument('package', help="package to uninstall.")
33+
uninstall_parser.set_defaults(sub_func=cls.do_uninstall)
34+
35+
return parser
36+
37+
@classmethod
38+
def do_install(cls, args):
39+
try:
40+
npm.invoke_npm(["install", "--save", "--ignore-scripts", args.package])
41+
npm.invoke_npm(["dedupe"])
42+
npm.sanity_check()
43+
except subprocess.CalledProcessError:
44+
raise ToolError()
45+
46+
@classmethod
47+
def do_uninstall(cls, args):
48+
npm.invoke_npm(["uninstall", "--save", "--ignore-scripts", args.package])

0 commit comments

Comments
 (0)