|
| 1 | +#python |
| 2 | +#encoding=utf-8 |
| 3 | +import os |
| 4 | +import sys |
| 5 | +import ui |
| 6 | +import utils.commandline |
| 7 | +from utils.commandline import arg, cwdarg, option, usage |
| 8 | +from staticcompiler import StaticPackage, Workspace, PublishPackageException, NoPublishPathException, PackageNotFoundException |
| 9 | + |
| 10 | +@cwdarg |
| 11 | +@usage(u'scompiler workspace [源库路径]') |
| 12 | +def workspace(root_path): |
| 13 | + u''' 源库所在工作区 ''' |
| 14 | + if StaticPackage.get_root(root_path): |
| 15 | + ui.msg(Workspace.get_workspace(root_path)) |
| 16 | + else: |
| 17 | + ui.msg(u'不是一个源库') |
| 18 | + return 1 |
| 19 | + |
| 20 | +@arg('filename') |
| 21 | +@option('force', '-f', '--force', action = 'store_true', help = u'强制重新编译') |
| 22 | +@usage(u'scompiler compile 发布库中的某个js/css文件 [options]') |
| 23 | +def compile(filename, force = False): |
| 24 | + u'编译一个css/js文件' |
| 25 | + root_path = StaticPackage.get_root(filename) |
| 26 | + if not root_path: |
| 27 | + ui.error(u'没有找到源文件') |
| 28 | + else: |
| 29 | + package = StaticPackage(root_path) |
| 30 | + try: |
| 31 | + package.compile(filename, force = force) |
| 32 | + except IOError, e: |
| 33 | + ui.error('%s file not found' % e.filename) |
| 34 | + except PackageNotFoundException, e: |
| 35 | + ui.error('%s package not found' % e.url) |
| 36 | + |
| 37 | + package.build_files() |
| 38 | + |
| 39 | +@cwdarg |
| 40 | +@arg('link_path') |
| 41 | +@option('force', '-f', '--force', action='store_true', help=u'强制建立发布文件夹') |
| 42 | +def link(path, link_path, force = False): |
| 43 | + u''' 将发布库与源库进行映射 |
| 44 | +
|
| 45 | +如果库设置了url,则同时使用.package文件进行连接,需要工作区支持,如果没有url,则只进行本地连接。''' |
| 46 | + |
| 47 | + publish_path, root_path = StaticPackage.get_roots(path) |
| 48 | + if not publish_path and not root_path and link_path: |
| 49 | + publish_path, root_path = StaticPackage.get_roots(link_path) |
| 50 | + path, link_path = link_path, path |
| 51 | + |
| 52 | + if not publish_path: |
| 53 | + publish_path = os.path.realpath(link_path) |
| 54 | + else: |
| 55 | + root_path = os.path.realpath(link_path) |
| 56 | + |
| 57 | + if not root_path: |
| 58 | + ui.error('package not found') |
| 59 | + |
| 60 | + package = StaticPackage(root_path, publish_path = publish_path) |
| 61 | + |
| 62 | + if not os.path.exists(publish_path): |
| 63 | + if force: |
| 64 | + os.makedirs(publish_path) |
| 65 | + else: |
| 66 | + ui.msg(u'%s path not exists, run scompiler link path -f to create it.' % publish_path) |
| 67 | + return 1 |
| 68 | + |
| 69 | + package.link() |
| 70 | + |
| 71 | + ui.msg(u'linked publish %s to %s'% (publish_path, root_path)) |
| 72 | + |
| 73 | +@cwdarg |
| 74 | +@arg('publish_path') |
| 75 | +@option('force', '-f', '--force', help = u'强制编译', action = 'store_true') |
| 76 | +@usage(u'scompiler publish [源库路径] [发布库路径] [options]') |
| 77 | +def publish(path, publish_path = None, force = False): |
| 78 | + u'''将整个目录进行发布''' |
| 79 | + |
| 80 | + do_link = False |
| 81 | + # 指定了第二个参数,则path一定是一个源库,第二个参数则是发布库,并自动进行link |
| 82 | + if publish_path: |
| 83 | + root_path = StaticPackage.get_root(path) |
| 84 | + path = publish_path # 发布整个库 |
| 85 | + do_link = True |
| 86 | + # 没有指定第二个参数,则path一定是一个发布库 |
| 87 | + else: |
| 88 | + publish_path, root_path = StaticPackage.get_roots(path) |
| 89 | + |
| 90 | + def get_files(dir_path): |
| 91 | + paths = [] |
| 92 | + for root, dirs, files in os.walk(dir_path): |
| 93 | + if '.svn' in root: |
| 94 | + dirs[:] = [] |
| 95 | + continue |
| 96 | + for file in files: |
| 97 | + path = os.path.join(root, file) |
| 98 | + if os.path.splitext(path)[1] in ('.css', '.js'): |
| 99 | + paths.append(path) |
| 100 | + |
| 101 | + return paths |
| 102 | + |
| 103 | + if not publish_path: |
| 104 | + ui.msg(u'No publish path.') |
| 105 | + else: |
| 106 | + package = StaticPackage(root_path, publish_path = publish_path) |
| 107 | + if not package.publish_path: |
| 108 | + ui.msg(u'No publish path.') |
| 109 | + else: |
| 110 | + ui.msg(u'publish to %s from %s' % (path, package.root)) |
| 111 | + all_files = get_files(path) |
| 112 | + for file in all_files: |
| 113 | + try: |
| 114 | + package.compile(file, force = force) |
| 115 | + except IOError, e: |
| 116 | + ui.error('%s file not found' % e.filename) |
| 117 | + except PackageNotFoundException, e: |
| 118 | + ui.error('No package found ' + e.url) |
| 119 | + |
| 120 | + package.build_files() |
| 121 | + if do_link: |
| 122 | + package.link() |
| 123 | + |
| 124 | +@cwdarg |
| 125 | +@usage(u'scompiler load [工作区路径]') |
| 126 | +def load(workspace_path): |
| 127 | + u''' 加载本地工作区 ''' |
| 128 | + |
| 129 | + if os.path.isfile(workspace_path): |
| 130 | + workspace_path = os.path.dirname(workspace_path) |
| 131 | + |
| 132 | + workspace = Workspace(workspace_path) |
| 133 | + added_count = 0 |
| 134 | + for root, dirs, files in os.walk(workspace_path): |
| 135 | + if StaticPackage.is_root(root): |
| 136 | + dirs[:] = [] |
| 137 | + if not workspace.has_package(root): |
| 138 | + workspace.add_package(root) |
| 139 | + added_count = added_count + 1 |
| 140 | + |
| 141 | + if len(workspace.useless_packages): |
| 142 | + for package in workspace.useless_packages: |
| 143 | + ui.msg(u'删除无用package %s' % package) |
| 144 | + |
| 145 | + workspace.rebuild_package() |
| 146 | + |
| 147 | + ui.msg(u'已加入 %s 个源库' % added_count) |
| 148 | + |
| 149 | +@cwdarg |
| 150 | +@option('show_url', '-u', '--show-url', action = 'store_true', help = u'显示有url的源库的url') |
| 151 | +@usage(u'scompiler packages [工作区路径]') |
| 152 | +def packages(workspace_path, show_url = False): |
| 153 | + u''' 本工作区中所有源库 ''' |
| 154 | + |
| 155 | + if os.path.isfile(workspace_path): |
| 156 | + workspace_path = os.path.dirname(workspace_path) |
| 157 | + |
| 158 | + # 有可能不是workspace跟路径,而是某个子路径 |
| 159 | + workspace_path = Workspace.get_workspace(workspace_path) |
| 160 | + if not workspace_path: |
| 161 | + ui.error(u'没有工作区') |
| 162 | + return 1 |
| 163 | + else: |
| 164 | + workspace = Workspace(workspace_path) |
| 165 | + if show_url: |
| 166 | + for url in workspace.url_packages.keys(): |
| 167 | + ui.msg(url) |
| 168 | + else: |
| 169 | + for local_path in workspace.local_packages.keys(): |
| 170 | + ui.msg(os.path.realpath(os.path.join(workspace.root, local_path))) |
| 171 | + |
| 172 | +@cwdarg |
| 173 | +@option('publish_path', '-p', '--publish-path', type = 'string', help = u'发布目录') |
| 174 | +@option('force', '-f', '--force', action = 'store_true', help = u'强制建立发布目录') |
| 175 | +def init(root_path, publish_path = None, force = False): |
| 176 | + u''' 初始化一个新的库 |
| 177 | + |
| 178 | +初始化一个新的库,建立template-config.xml配置文件及常用的目录,如果指定了 -p 参数,还可以自动建立与发布目录的连接''' |
| 179 | + ui.msg(u'初始化%s' % root_path) |
| 180 | + try: |
| 181 | + StaticPackage.init(root_path) |
| 182 | + except PublishPackageException: |
| 183 | + ui.error(u'发布目录不能被初始化') |
| 184 | + return 1 |
| 185 | + |
| 186 | + if publish_path: |
| 187 | + link(root_path, publish_path, force = force) |
| 188 | + |
| 189 | +@cwdarg |
| 190 | +@usage(u'scompiler root [源库路径]') |
| 191 | +def root(root_path): |
| 192 | + u''' 源库的根路径 ''' |
| 193 | + ui.msg(StaticPackage.get_root(root_path)) |
| 194 | + |
| 195 | +@cwdarg |
| 196 | +@usage(u'scompiler source [源库路径] [options]') |
| 197 | +def source(publish_path): |
| 198 | + u''' 映射的源库路径 ''' |
| 199 | + if StaticPackage.get_publish(publish_path): |
| 200 | + StaticPackage.get_root(publish_path) |
| 201 | + else: |
| 202 | + ui.error(u'不是一个发布库') |
| 203 | + |
| 204 | +@cwdarg |
| 205 | +@usage(u'scompiler libs [源库路径]') |
| 206 | +@option('show_url', '-u', '--show-url', help=u'显示url而非本地路径', action='store_true') |
| 207 | +@option('all', '-a', '--all', help=u'递归显示所有', action='store_true') |
| 208 | +@option('reverse', '-r', '--reverse', help=u'显示被依赖的', action='store_true') |
| 209 | +def libs(root_path, show_url = False, all = False, reverse = False): |
| 210 | + u''' 库的相关依赖库 ''' |
| 211 | + root_path = StaticPackage.get_root(root_path) |
| 212 | + if not root_path: |
| 213 | + ui.error(u'不是源库') |
| 214 | + return 1 |
| 215 | + |
| 216 | + package = StaticPackage(root_path) |
| 217 | + # 这个库没有设置url,不可能被别的库依赖 |
| 218 | + if not package.url: |
| 219 | + ui.error(u'no url') |
| 220 | + return 1 |
| 221 | + |
| 222 | + # 显示依赖自己的 |
| 223 | + if reverse: |
| 224 | + libs = package.get_reverse_libs(all = all) |
| 225 | + # 显示依赖了谁 |
| 226 | + else: |
| 227 | + libs = package.get_libs(all = all) |
| 228 | + |
| 229 | + if show_url: |
| 230 | + if package.workspace: |
| 231 | + for local_path in libs: |
| 232 | + url = package.workspace.local_packages.get(local_path) |
| 233 | + if url: |
| 234 | + ui.msg(url) |
| 235 | + |
| 236 | + else: |
| 237 | + ui.error(u'没有工作区') |
| 238 | + else: |
| 239 | + for local_path in libs: |
| 240 | + ui.msg(local_path) |
| 241 | + |
| 242 | +@arg('filename') |
| 243 | +def includes(filename): |
| 244 | + u''' 某文件所有依赖的文件 ''' |
| 245 | + filename = os.path.realpath(filename) |
| 246 | + root_path = StaticPackage.get_root(filename) |
| 247 | + package = StaticPackage(root_path) |
| 248 | + |
| 249 | + for file in package.get_includes(filename): |
| 250 | + print file |
| 251 | + |
| 252 | +@arg('filename') |
| 253 | +@option('all', '-a', '--all', help=u'递归显示所有', action='store_true') |
| 254 | +def included(filename, all = False): |
| 255 | + u''' 所有引用了此文件的文件 ''' |
| 256 | + filename = os.path.realpath(filename) |
| 257 | + root_path = StaticPackage.get_root(filename) |
| 258 | + package = StaticPackage(root_path) |
| 259 | + |
| 260 | + for file in package.get_included(filename, all = all): |
| 261 | + print file |
| 262 | + |
| 263 | +@arg('workspace_path') |
| 264 | +@option('fastcgi', '--fastcgi', help = u'使用fastcgi进行serve', action = 'store_true') |
| 265 | +@option('port', '--port', help = u'指定端口号', type = 'int') |
| 266 | +@option('debug', '-d', '--debug', help = u'debug模式', action = 'store_true') |
| 267 | +def serve(workspace_path = None, fastcgi = False, port = 8080, debug = False): |
| 268 | + u''' 启动一个静态服务器 |
| 269 | +
|
| 270 | +请指定工作区路径''' |
| 271 | + |
| 272 | + if workspace_path: |
| 273 | + workspace = Workspace(os.path.realpath(workspace_path)) |
| 274 | + else: |
| 275 | + workspace = None |
| 276 | + |
| 277 | + def print_request(environ, start_response): |
| 278 | + ''' 输出fastcgi本次请求的相关信息 ''' |
| 279 | + |
| 280 | + import cgi |
| 281 | + start_response('200 OK', [('Content-Type', 'text/html')]) |
| 282 | + yield '<html><head><title>Hello World!</title></head>\n' \ |
| 283 | + '<body>\n' \ |
| 284 | + '<p>Hello World!</p>\n' \ |
| 285 | + '<table border="1">' |
| 286 | + names = environ.keys() |
| 287 | + names.sort() |
| 288 | + for name in names: |
| 289 | + yield '<tr><td>%s</td><td>%s</td></tr>\n' % ( |
| 290 | + name, cgi.escape(`environ[name]`)) |
| 291 | + |
| 292 | + form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ, |
| 293 | + keep_blank_values=1) |
| 294 | + if form.list: |
| 295 | + yield '<tr><th colspan="2">Form data</th></tr>' |
| 296 | + |
| 297 | + for field in form.list: |
| 298 | + yield '<tr><td>%s</td><td>%s</td></tr>\n' % ( |
| 299 | + field.name, field.value) |
| 300 | + |
| 301 | + yield '</table>\n' \ |
| 302 | + '</body></html>\n' |
| 303 | + |
| 304 | + def listen(environ, start_response): |
| 305 | + ''' 监听请求 ''' |
| 306 | + if environ['DOCUMENT_URI'].endswith('/fuck.css'): |
| 307 | + return print_request(environ, start_response) |
| 308 | + |
| 309 | + DEBUG = debug |
| 310 | + |
| 311 | + filename = os.path.realpath(environ['REQUEST_FILENAME']) |
| 312 | + url = environ['DOCUMENT_URI'] |
| 313 | + |
| 314 | + force = False # 是否强制重新编译 |
| 315 | + # 没有 referer 时强制重新编译 |
| 316 | + if not 'HTTP_REFERER' in environ.keys(): |
| 317 | + force = True |
| 318 | + |
| 319 | + publish_path, root_path = StaticPackage.get_roots(filename, workspace = workspace) |
| 320 | + if root_path: |
| 321 | + package = StaticPackage(root_path, publish_path, workspace = workspace) |
| 322 | + |
| 323 | + try: |
| 324 | + package.compile(filename, force = force, workspace = workspace) |
| 325 | + except IOError, e: |
| 326 | + start_response('400 Compile Error', []) |
| 327 | + return '%s file not found' % e.filename |
| 328 | + except PackageNotFoundException, e: |
| 329 | + start_response('400 Compile Error', []) |
| 330 | + return '%s package not found' % e.url |
| 331 | + |
| 332 | + package.build_files() |
| 333 | + |
| 334 | + mimetypes = { |
| 335 | + '.css': 'text/css', |
| 336 | + '.js': 'text/javascript' |
| 337 | + } |
| 338 | + mimetype = mimetypes[os.path.splitext(filename)[1]] |
| 339 | + |
| 340 | + if not os.path.exists(filename): |
| 341 | + # 文件不存在 |
| 342 | + start_response('404 Not Found', []) |
| 343 | + return '404 Not Found' |
| 344 | + |
| 345 | + start_response('200 OK', [('Content-Type', mimetype)]) |
| 346 | + return [open(filename).read()] |
| 347 | + |
| 348 | + if fastcgi: |
| 349 | + from flup.server.fcgi import WSGIServer |
| 350 | + |
| 351 | + WSGIServer(listen, bindAddress=('localhost', port), debug = debug).run() |
| 352 | + else: |
| 353 | + ui.msg(u'现在还不支持server,请使用 scompiler serve --fastcgi 方式') |
| 354 | + |
| 355 | +def main(): |
| 356 | + commands = [init, compile, publish, link, load, serve, packages, workspace, root, source, libs, includes, included] |
| 357 | + if len(sys.argv) < 2: |
| 358 | + ui.msg(u'使用 scompiler help 得到用法') |
| 359 | + else: |
| 360 | + command = sys.argv[1] |
| 361 | + if command == 'help': |
| 362 | + subcommand = None |
| 363 | + if len(sys.argv) > 2: |
| 364 | + subcommand = globals()[sys.argv[2]] |
| 365 | + utils.commandline.help(commands, subcommand) |
| 366 | + else: |
| 367 | + all = globals() |
| 368 | + if command in all: |
| 369 | + utils.commandline.call(commands, globals()[command]) |
| 370 | + else: |
| 371 | + utils.commandline.help(commands) |
0 commit comments