Permalink
Newer
Older
100644 220 lines (174 sloc) 6.4 KB
Aug 17, 2015 @ibab Add initial code
1 #!/usr/bin/env python
2 # coding: utf-8
3
4 from __future__ import print_function
5
6 import os
7 import sys
8 import shutil
9 from subprocess import check_call
Mar 13, 2016 @astiunov Added scripts to setup.py
10 from glob import glob
Aug 17, 2015 @ibab Add initial code
11
12 v = sys.version_info
13 if v[:2] < (3,3):
14 error = "ERROR: Jupyter Hub requires Python version 3.3 or above."
15 print(error, file=sys.stderr)
16 sys.exit(1)
17
18 def mtime(path):
19 """shorthand for mtime"""
20 return os.stat(path).st_mtime
21
22
23 if os.name in ('nt', 'dos'):
24 error = "ERROR: Windows is not supported"
25 print(error, file=sys.stderr)
26
27 # At least we're on the python version we need, move on.
28
29 from distutils.core import setup
30
31 pjoin = os.path.join
32 here = os.path.abspath(os.path.dirname(__file__))
33
34 from distutils.cmd import Command
35 from distutils.command.build_py import build_py
36 from distutils.command.sdist import sdist
37
38 npm_path = ':'.join([
39 pjoin(here, 'node_modules', '.bin'),
40 os.environ.get("PATH", os.defpath),
41 ])
42
43 here = os.path.abspath(os.path.dirname(__file__))
44 share = pjoin(here, 'share')
45 static = pjoin(share, 'static')
46
47 class BaseCommand(Command):
48 """Dumb empty command because Command needs subclasses to override too much"""
49 user_options = []
50
51 def initialize_options(self):
52 pass
53
54 def finalize_options(self):
55 pass
56
57 def get_inputs(self):
58 return []
59
60 def get_outputs(self):
61 return []
62
63 class Bower(BaseCommand):
64 description = "fetch static client-side components with bower"
65
66 user_options = []
67 bower_dir = pjoin(static, 'components')
68 node_modules = pjoin(here, 'node_modules')
69
70 def should_run(self):
71 if not os.path.exists(self.bower_dir):
72 return True
73 return mtime(self.bower_dir) < mtime(pjoin(here, 'bower.json'))
74
75 def should_run_npm(self):
76 if not shutil.which('npm'):
77 print("npm unavailable", file=sys.stderr)
78 return False
79 if not os.path.exists(self.node_modules):
80 return True
81 return mtime(self.node_modules) < mtime(pjoin(here, 'package.json'))
82
83 def run(self):
84 if not self.should_run():
85 print("bower dependencies up to date")
86 return
87
88 if self.should_run_npm():
89 print("installing build dependencies with npm")
90 check_call(['npm', 'install'], cwd=here)
91 os.utime(self.node_modules)
92
93 env = os.environ.copy()
94 env['PATH'] = npm_path
95
96 try:
97 check_call(
98 ['bower', 'install', '--allow-root', '--config.interactive=false'],
99 cwd=here,
100 env=env,
101 )
102 except OSError as e:
103 print("Failed to run bower: %s" % e, file=sys.stderr)
104 print("You can install js dependencies with `npm install`", file=sys.stderr)
105 raise
106 os.utime(self.bower_dir)
107 # update data-files in case this created new files
108 self.distribution.data_files = get_data_files()
109
110
111 class CSS(BaseCommand):
112 description = "compile CSS from LESS"
113
114 def should_run(self):
115 """Does less need to run?"""
116 # from IPython.html.tasks.py
117
118 css_targets = [pjoin(static, 'css', 'style.min.css')]
119 css_maps = [t + '.map' for t in css_targets]
120 targets = css_targets + css_maps
121 if not all(os.path.exists(t) for t in targets):
122 # some generated files don't exist
123 return True
124 earliest_target = sorted(mtime(t) for t in targets)[0]
125
126 # check if any .less files are newer than the generated targets
127 for (dirpath, dirnames, filenames) in os.walk(static):
128 for f in filenames:
129 if f.endswith('.less'):
130 path = pjoin(static, dirpath, f)
131 timestamp = mtime(path)
132 if timestamp > earliest_target:
133 return True
134
135 return False
136
137 def run(self):
138 if not self.should_run():
139 print("CSS up-to-date")
140 return
141
142 self.run_command('js')
143
144 style_less = pjoin(static, 'less', 'style.less')
145 style_css = pjoin(static, 'css', 'style.min.css')
146 sourcemap = style_css + '.map'
147
148 env = os.environ.copy()
149 env['PATH'] = npm_path
150 try:
151 check_call([
152 'lessc', '--clean-css',
153 '--source-map-basepath={}'.format(static),
154 '--source-map={}'.format(sourcemap),
155 '--source-map-rootpath=../',
156 style_less, style_css,
157 ], cwd=here, env=env)
158 except OSError as e:
159 print("Failed to run lessc: %s" % e, file=sys.stderr)
160 print("You can install js dependencies with `npm install`", file=sys.stderr)
161 raise
162 # update data-files in case this created new files
163 self.distribution.data_files = get_data_files()
164
165 def get_data_files():
166 """Get data files in share/jupyter"""
167
168 data_files = []
169 ntrim = len(here) + 1
170
171 for (d, dirs, filenames) in os.walk(static):
172 data_files.append((
173 d[ntrim:],
174 [ pjoin(d, f) for f in filenames ]
175 ))
176 return data_files
177
178
179 setup_args = dict(
180 name = 'everware',
181 packages = ['everware'],
Mar 13, 2016 @astiunov Added scripts to setup.py
182 scripts = glob(pjoin('scripts', '*')),
Aug 17, 2015 @ibab Add initial code
183 version = '0.0.0',
184 description = """Everware""",
185 long_description = "",
186 author = "",
187 author_email = "",
188 url = "",
189 license = "BSD",
190 platforms = "Linux, Mac OS X",
191 keywords = ['Interactive', 'Interpreter', 'Shell', 'Web'],
192 classifiers = [
193 'Intended Audience :: Developers',
194 'Intended Audience :: System Administrators',
195 'Intended Audience :: Science/Research',
196 'License :: OSI Approved :: BSD License',
197 'Programming Language :: Python',
198 'Programming Language :: Python :: 3',
199 ],
200 )
201
202 setup_args['cmdclass'] = {'js': Bower, 'css': CSS}
203
204 # setuptools requirements
205 if 'setuptools' in sys.modules:
206 setup_args['install_requires'] = install_requires = []
207 with open('requirements.txt') as f:
208 for line in f.readlines():
209 req = line.strip()
210 if not req or req.startswith(('-e', '#')):
211 continue
212 install_requires.append(req)
213
214
215 def main():
216 setup(**setup_args)
217
218 if __name__ == '__main__':
219 main()