#!/usr/bin/env python # # Copyright (c) 2007, 2008 Agostino Russo # # Written by Agostino Russo # # pack.py is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # pack.py is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import sys import os import shutil from modulegraph.find_modules import find_modules from modulegraph.modulegraph import SourceModule, Package, Script, Extension from os.path import abspath, join, basename, dirname, exists import py_compile from optparse import OptionParser import logging from version import * log = logging.getLogger(__name__) class Application(object): def __init__(self, root_dir): self.root_dir = os.path.abspath(root_dir) def run(self): self.localize() self.parse_commandline_arguments() self.set_logger() self.create_dirs() self.add_dependencies(self.main_script) self.add_extras() def localize(self): try: import gettext gettext.install(APPLICATION_NAME) except ImportError: import __builtin__ def dummytrans (text): """A _ function for systems without gettext. Effectively a NOOP""" return text __builtin__.__dict__['_'] = dummytrans def print_readme(self): readme = ajoin(self.root_dir, 'README') readme = open(readme, 'r') content = readme.read() readme.close() print content def parse_commandline_arguments(self): usage = "%prog [options] main_script [extra extra extra]" version = version="%s-rev%s" % (APPLICATION_VERSION, APPLICATION_REVISION) description='Pypack packages a script into a directory containing all needed depencies suitable for "folder deployment". Run `python pypack --readme` for more information.' parser = OptionParser(usage=usage, description = description, version = version) parser.add_option("--readme", action="store_true", dest="readme", help="show more information") parser.add_option("--verbose", action="store_true", dest="verbose", help="run in verbose mode, all messages are displayed") parser.add_option("--debug", action="store_true", dest="debug", help="print debug messages to stdout") parser.add_option("--print", action="store_true", dest="print_dependencies", help="print dependencies and exit") parser.add_option("--outputdir", dest="out_dir", default="./build", help="directory where the files will be built, if non existent, it will be created") parser.add_option("--bytecompile", action="store_true", dest="bytecompile", help="bytecompile the python files") options, args = parser.parse_args() self.options, args = parser.parse_args() if options.readme: print self.print_readme() sys.exit(0) if not len(args): parser.print_help() sys.exit(1) self.main_script = args[0] self.extras = args[1:] if self.options.debug: self.options.verbose = True if self.options.print_dependencies: self.print_dependencies() sys.exit(0) def create_dirs(self): self.out_dir = self.options.out_dir self.out_dir = ajoin(self.out_dir) if os.path.exists(self.out_dir): print 'ERROR: the build directory "%s" already exists.\nRemove that before running %s again.' % (self.out_dir, APPLICATION_NAME) sys.exit(1) self.lib_dir = ajoin(self.out_dir, 'lib') os.makedirs(self.lib_dir) def set_logger(self): ''' Adjust the application root logger settings ''' root_logger = logging.getLogger() if root_logger.handlers: handler = root_logger.handlers[0] else: handler = logging.StreamHandler() root_logger.addHandler(handler) formatter = logging.Formatter('%(message)s', datefmt='%m-%d %H:%M') handler.setFormatter(formatter) root_logger.setLevel(logging.DEBUG) if self.options.verbose: handler.setLevel(logging.DEBUG) else: handler.setLevel(logging.ERROR) def print_dependencies(self): scripts = [self.main_script] scripts += [f for f in self.extras if f[:-3] == '.py'] modules = [] for script in scripts: for m in get_modules(script): modules.append(m) for m in modules: print m def add_dependencies(self, script): ''' Adds all dependencies to lib_dir ''' modules = get_modules(script) lib_dir = self.lib_dir for module in modules: if not module.filename: continue if isinstance(module, SourceModule): target = module2path(lib_dir, module.identifier) target += '.py' self.compile(module.filename, target) elif isinstance(module, Package): target = module2path(lib_dir, module.identifier) target = ajoin(target, '__init__.py') self.compile(module.filename, target) elif isinstance(module, Script): log.debug("Script %s", script) if module.identifier[0] != "_": script_name = basename(script) target = ajoin(self.out_dir, script_name) self.compile(module.filename, target) elif isinstance(module, Extension): target = module2path(lib_dir, module.identifier) target = dirname(target) makedirs(target) log.debug("copying %s -> %s" % (module.filename, target)) shutil.copy(module.filename, target) else: log.error("Unkown module %s", module) def compile(self, source, target): log.debug("copying %s -> %s", source, target) makedirs(dirname(target)) shutil.copy(source, target) if self.options.bytecompile: log.debug("compiling %s", target) dfile = target if target.startswith(self.out_dir): dfile = target[len(self.out_dir):] py_compile.compile(target, dfile=dfile, doraise=True) os.unlink(target) def add_extras(self): ''' Add extra files, modules, pacakges or directories ''' for extra in self.extras: if os.path.isfile(extra): if extra.endswith('.py'): self.add_dependencies(extra) target = os.path.basename(extra) target = ajoin(self.lib_dir, target) self.compile(extra, target) else: target = os.path.basename(extra) target = ajoin(self.out_dir, target) log.debug("copying %s -> %s", extra, target) shutil.copy(extra, target) elif os.path.isdir(extra): target = os.path.basename(extra) target = ajoin(self.out_dir, target) log.debug("copying %s -> %s", extra, target) shutil.copytree(extra, target, symlinks=False) else: print 'ERROR: The extra argument "%s" cannot be found' % extra sys.exit(1) def ajoin(*args): return os.path.abspath(os.path.join(*args)) def makedirs(target_path): try: os.makedirs(target_path) except: pass def module2path(root_dir, module_name): path = module_name.split('.') path = ajoin(root_dir, *path) return path def get_modules(script_path): sys.path.insert(0, os.path.dirname(script_path)) mf = find_modules((script_path,)) del sys.path[0] modules = mf.flatten() return modules