def main(): """Run googkit. """ cwd = os.getcwd() logging.basicConfig(level=logging.INFO, format='%(message)s') tree = CommandTree() googkit.lib.plugin.load(tree) arg = ArgumentParser.parse(sys.argv) if not arg.commands and arg.option('--version'): print_version() sys.exit() CommandClass = tree.command_class(arg.commands) if CommandClass is None: Help(tree, arg).print_help() sys.exit() try: env = Environment(cwd, arg, tree) command = CommandClass(env) command.run() except InvalidOptionError as e: logging.error(_('[Error] {message}').format(message=str(e))) Help(tree, arg).print_help() sys.exit(1) except GoogkitError as e: logging.error(_('[Error] {message}').format(message=str(e))) sys.exit(1)
def download_closure_compiler(self): """Downloads Closure Compiler resources to the compiler root that defined in a config file. """ tmp_path = tempfile.mkdtemp() compiler_zip = os.path.join(tmp_path, 'compiler.zip') compiler_zip_url = self.config.compiler_zip() logging.info(_('Downloading Closure Compiler...')) try: request.urlretrieve(compiler_zip_url, compiler_zip) except IOError as e: raise GoogkitError( _('Dowloading Closure Compiler failed: {message}').format( massage=str(e))) compiler_root = self.config.compiler_root() os.path.join('tools', 'sub', 'unzip.py') with zipfile.ZipFile(compiler_zip) as z: z.extractall(compiler_root) shutil.rmtree(tmp_path) logging.info(_('Done.'))
def _print_usage(self): commands_mark = _('<commands>') if self._available_commands else '' if self._correct_commands: print(_('Usage: googkit {cmd} {cmds_mark}').format( cmd=' '.join(self._correct_commands), cmds_mark=commands_mark)) else: print(_('Usage: googkit {cmds_mark}').format( cmds_mark=commands_mark))
def build_production(self, html_path, project_root, should_clean=False): """Builds resources is in the specified project root for production. Removes old resources if should_clean is True. """ config = self.config self.setup_files(config.production_dir(), should_clean) logging.info(_('Building for production: ' + html_path)) args = self.production_arguments(html_path, project_root) self._build(args, project_root) logging.info(_('Done.'))
def _print_usage(self): commands_mark = _('<commands>') if self._available_commands else '' if self._correct_commands: print( _('Usage: googkit {cmd} {cmds_mark}').format( cmd=' '.join(self._correct_commands), cmds_mark=commands_mark)) else: print( _('Usage: googkit {cmds_mark}').format( cmds_mark=commands_mark))
def load(tree): """Loads googkit plugins to the specified instance of CommandTree. """ base_dir = googkit.lib.path.plugin() for filename in os.listdir(base_dir): plugin_dir = os.path.join(base_dir, filename) if not os.path.isdir(plugin_dir): continue init_path = os.path.join(plugin_dir, INIT_FILE) if not os.path.exists(init_path): continue command_path = os.path.join(plugin_dir, COMMAND_FILE) if not os.path.exists(command_path): continue module_name = 'plugins.{filename}.command'.format(filename=filename) module = __import__(module_name, fromlist=['command']) if not hasattr(module, 'register'): raise GoogkitError( _('No register method found for plugin: {module}').format( module=module_name)) module.register(tree)
def download_closure_library(self): """Downloads Closure Library resources to the library root that defined in a config file. """ library_repos = self.config.library_repos() library_root = self.config.library_root() logging.info(_('Downloading Closure Library...')) try: googkit.lib.clone.run(library_repos, library_root) except GoogkitError as e: raise GoogkitError( _('Dowloading Closure Library failed: {message}').format( message=str(e))) logging.info('Done.')
def run_internal(self): project_root = googkit.lib.path.project_root(self.env.cwd) with working_directory(project_root): self.update_deps() self.update_testrunner() logging.info(_('Updated dependencies.'))
def load(tree): """Loads googkit plugins to the specified instance of CommandTree. """ base_dir = googkit.lib.path.plugin() for filename in os.listdir(base_dir): plugin_dir = os.path.join(base_dir, filename) if not os.path.isdir(plugin_dir): continue init_path = os.path.join(plugin_dir, INIT_FILE) if not os.path.exists(init_path): continue command_path = os.path.join(plugin_dir, COMMAND_FILE) if not os.path.exists(command_path): continue module_name = 'plugins.{filename}.command'.format(filename=filename) module = __import__(module_name, fromlist=['command']) if not hasattr(module, 'register'): raise GoogkitError(_('No register method found for plugin: {module}').format( module=module_name)) module.register(tree)
def _load_config(self): default_config = googkit.lib.path.default_config() user_config = googkit.lib.path.user_config() project_config = googkit.lib.path.project_config(self.env.cwd) if project_config is None: raise GoogkitError(_('No config file found.')) config = Config() config.load(project_config, user_config, default_config) return config
def copy_template(self, dst_dir): """Copy template files (include default googkit.cfg, demonstration files) to the specified directory. """ template_dir = googkit.lib.path.template() conflicted = set(os.listdir(dst_dir)) & set(os.listdir(template_dir)) if conflicted: raise GoogkitError(_('Conflicted files: {files}').format( files=', '.join(conflicted))) distutils.dir_util.copy_tree(template_dir, dst_dir)
def update_testrunner(self): """Updates a test file list for the unit-test runner. """ config = self.config js_dev_dir = config.js_dev_dir() testrunner = config.testrunner() if not os.path.exists(testrunner): return testrunner_dir = os.path.dirname(testrunner) test_file_pattern = config.test_file_pattern() tests = [] for dirpath, dirnames, filenames in os.walk(js_dev_dir): for filename in filenames: if re.search(test_file_pattern, filename) is None: continue path = os.path.join(dirpath, filename) relpath = os.path.relpath(path, testrunner_dir) tests.append(relpath) logging.debug(_('Found test on {path}').format(path=path)) lines = [] for line in open(testrunner): marker = '/*@test_files@*/' if line.find(marker) >= 0: indent = googkit.lib.strutil.line_indent(line) line = indent + self.update_tests(line, tests) + marker + '\n' lines.append(line) with open(testrunner, 'w') as f: for line in lines: f.write(line) logging.debug( _('Updated a test runner on {path}').format(path=testrunner))
def update_testrunner(self): """Updates a test file list for the unit-test runner. """ config = self.config js_dev_dir = config.js_dev_dir() testrunner = config.testrunner() if not os.path.exists(testrunner): return testrunner_dir = os.path.dirname(testrunner) test_file_pattern = config.test_file_pattern() tests = [] for dirpath, dirnames, filenames in os.walk(js_dev_dir): for filename in filenames: if re.search(test_file_pattern, filename) is None: continue path = os.path.join(dirpath, filename) relpath = os.path.relpath(path, testrunner_dir) tests.append(relpath) logging.debug(_('Found test on {path}').format(path=path)) lines = [] for line in open(testrunner): marker = '/*@test_files@*/' if line.find(marker) >= 0: indent = googkit.lib.strutil.line_indent(line) line = indent + self.update_tests(line, tests) + marker + '\n' lines.append(line) with open(testrunner, 'w') as f: for line in lines: f.write(line) logging.debug(_('Updated a test runner on {path}').format( path=testrunner))
def print_help(self): """Prints a help message. """ last_command = None if not self._argument.commands else self._argument.commands[-1] if not self._is_valid_commands(): print(_('Invalid command: {cmd}').format(cmd=last_command)) print('') self._print_usage() self._print_available_commands(last_command) self._print_available_options()
def copy_template(self, dst_dir): """Copy template files (include default googkit.cfg, demonstration files) to the specified directory. """ template_dir = googkit.lib.path.template() conflicted = set(os.listdir(dst_dir)) & set(os.listdir(template_dir)) if conflicted: raise GoogkitError( _('Conflicted files: {files}').format( files=', '.join(conflicted))) distutils.dir_util.copy_tree(template_dir, dst_dir)
def print_help(self): """Prints a help message. """ last_command = None if not self._argument.commands else self._argument.commands[ -1] if not self._is_valid_commands(): print(_('Invalid command: {cmd}').format(cmd=last_command)) print('') self._print_usage() self._print_available_commands(last_command) self._print_available_options()
def _print_available_commands(self, command): if not self._available_commands: return print('') if self._is_valid_commands(): print(_('Available commands:')) commands = self._available_commands else: candidates = Help.candidates(self._available_commands, command) if len(candidates) == 0: print(_('Available commands:')) commands = self._available_commands elif len(candidates) == 1: print(_('Did you mean this?')) commands = candidates else: print(_('Did you mean one of these?')) commands = candidates for name in commands: print(' ' + name)
def build_debug(self, html_path, project_root, should_clean=False): """Builds resources is in the specified project root for debugging. Removes old resources if should_clean is True. """ config = self.config self.setup_files(config.debug_dir(), should_clean) logging.info(_('Building for debug: ') + html_path) args = self.debug_arguments(html_path, project_root) self._build(args, project_root) # The root path should be set by 'sourceRoot', but Closure Compiler # doesn't support this attribute. # So set 'sourceRoot' to 'project_root' directory manually until # Closure Compiler supports this feature. html_relpath = os.path.relpath(html_path, config.development_dir()) debug_html_path = os.path.join(config.debug_dir(), html_relpath) compiled_js_path = self.compiled_js_path(debug_html_path) source_map_path = compiled_js_path + '.map' self.modify_source_map(source_map_path, project_root) logging.info(_('Done.'))
def _print_available_options(self): cls = self._tree.command_class(self._argument.commands) if not cls: return supported_options = cls.supported_options() if not supported_options: return print('') print(_('Available options:')) for name in supported_options: print(' ' + name)
def setup_main_scripts(self): """Set-up a main scripts as an entry point for the user application. This script will be generated automatically under the rule: #. Search HTML-like documents into development/. #. Generate the namespace path from a filename of the document. #. Search scripts that are named ``{namespace}.js`` into js_dev/ #. If some scripts are not found, generate the scripts. """ config = self.config devel_dir = config.development_dir() html_list = [] for ext in self.HTML_LIKE_EXT: html_list += glob.glob(os.path.join(devel_dir, '*' + ext)) for html_path in html_list: if os.path.abspath(html_path) == os.path.abspath( config.testrunner()): continue basename = self.namespace_by_html(html_path) + '.js' script_path = os.path.join(config.js_dev_dir(), basename) if os.path.exists(script_path): logging.debug( _('Skip generating the entry point: {path}').format( path=script_path)) continue main_script_template = os.path.join(googkit.lib.path.template(), 'development', 'js_dev', 'googkit_index.js') shutil.copyfile(main_script_template, script_path) logging.debug( _('Generate the entry point: {path}').format(path=script_path))
def _pull(repos, target_path): args = [_git_cmd(), 'pull'] popen_args = { 'cwd': target_path, 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, } proc = subprocess.Popen(args, **popen_args) result = proc.communicate() if proc.returncode != 0: raise GoogkitError(_('Git pull failed: {message}').format( message=result[1].decode())) logging.debug(result[0].decode())
def _build(self, builder_args, project_root): builder = self.config.closurebuilder() cmd = ['python', builder] + [str(arg) for arg in builder_args] popen_args = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE} builder_proc = subprocess.Popen(cmd, **popen_args) result = builder_proc.communicate() if builder_proc.returncode != 0: raise GoogkitError( _('Compilation failed:\n{message}').format( message=result[1].decode())) else: logging.debug(result[1].decode())
def _clone(repos, target_path): # git-clone on Windows expected unix-like path args = [_git_cmd(), 'clone', repos, request.pathname2url(target_path)] popen_args = { 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, } proc = subprocess.Popen(args, **popen_args) result = proc.communicate() if proc.returncode != 0: raise GoogkitError(_('Git clone failed: {message}').format( message=result[1].decode())) logging.debug(result[0].decode())
def update_deps(self): """Updates module dependencies by using depswriter.py. """ config = self.config js_dev_dir = config.js_dev_dir() deps_js = config.deps_js() base_js_dir = os.path.dirname(config.base_js()) js_dev_dir_rel = os.path.relpath(js_dev_dir, base_js_dir) args_format = { 'deps_js': deps_js, 'js_dev': js_dev_dir, 'js_dev_rel': js_dev_dir_rel, } args = [ 'python', config.depswriter(), '--root_with_prefix="{js_dev} {js_dev_rel}"'.format(**args_format), '--output_file="{deps_js}"'.format(**args_format) ] popen_args = { 'shell': True, 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, } # depswriter.py doesn't work with arguments including white-space. # For example, # it works: # # $ python depswriter.py --root_with_prefix="path path" # # but it doesn't work: # # $ python depswriter.py "--root_with_prefix=\"path path\"" proc = subprocess.Popen(' '.join(args), **popen_args) result = proc.communicate() if proc.returncode != 0: raise GoogkitError( _('Updating dependencies failed: {message}').format( message=result[1].decode())) logging.debug('Updated ' + deps_js)
def apply_config(self, path): """Applies configurations to a file that the path point to. """ lines = [] updaters = { 'base.js': { 'marker': '<!--@base_js@-->', 'update': self.update_base_js }, 'deps.js': { 'marker': '<!--@deps_js@-->', 'update': self.update_deps_js }, 'multitestrunner.css': { 'marker': '<!--@multitestrunner_css@-->', 'update': self.update_multitestrunner_css }, 'require_main': { 'marker': '<!--@require_main@-->', 'update': self.update_require_main }, 'provide_main': { 'marker': '/*@provide_main@*/', 'update': self.update_provide_main }, } with open(path) as fp: for line in fp: for updater_name in updaters.keys(): marker = updaters[updater_name]['marker'] update = updaters[updater_name]['update'] if line.find(marker) >= 0: msg = _('Replaced a {name} path on {path}').format( name=updater_name, path=path) logging.debug(msg) line = '{indent}{content}{marker}\n'.format( indent=googkit.lib.strutil.line_indent(line), content=update(line, path), marker=marker) lines.append(line) with open(path, 'w') as fp: for line in lines: fp.write(line)
def update_deps(self): """Updates module dependencies by using depswriter.py. """ config = self.config js_dev_dir = config.js_dev_dir() deps_js = config.deps_js() base_js_dir = os.path.dirname(config.base_js()) js_dev_dir_rel = os.path.relpath(js_dev_dir, base_js_dir) args_format = { 'deps_js': deps_js, 'js_dev': js_dev_dir, 'js_dev_rel': js_dev_dir_rel, } args = [ 'python', config.depswriter(), '--root_with_prefix="{js_dev} {js_dev_rel}"'.format(**args_format), '--output_file="{deps_js}"'.format(**args_format) ] popen_args = { 'shell': True, 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, } # depswriter.py doesn't work with arguments including white-space. # For example, # it works: # # $ python depswriter.py --root_with_prefix="path path" # # but it doesn't work: # # $ python depswriter.py "--root_with_prefix=\"path path\"" proc = subprocess.Popen(' '.join(args), **popen_args) result = proc.communicate() if proc.returncode != 0: raise GoogkitError(_('Updating dependencies failed: {message}').format( message=result[1].decode())) logging.debug('Updated ' + deps_js)
def _build(self, builder_args, project_root): builder = self.config.closurebuilder() cmd = ['python', builder] + [str(arg) for arg in builder_args] popen_args = { 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE } builder_proc = subprocess.Popen(cmd, **popen_args) result = builder_proc.communicate() if builder_proc.returncode != 0: raise GoogkitError(_('Compilation failed:\n{message}').format( message=result[1].decode())) else: logging.debug(result[1].decode())
def lint(self): """Lints project resouces by using Closure Linter. """ if googkit.lib.file.which('gjslint') is None: raise GoogkitError(_('Required command not found: gjslint')) paths = self._sources() args = OptionBuilder() flagfile = self.config.linter_flagfile() if os.path.exists(flagfile): args.add('--flagfile', flagfile) cmd = ['gjslint'] + [str(arg) for arg in args] + paths popen_args = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE} proc = subprocess.Popen(cmd, **popen_args) result = proc.communicate() logging.info(result[0].decode())
def lint(self): """Lints project resouces by using Closure Linter. """ if googkit.lib.file.which('gjslint') is None: raise GoogkitError(_('Required command not found: gjslint')) paths = self._sources() args = OptionBuilder() flagfile = self.config.linter_flagfile() if os.path.exists(flagfile): args.add('--flagfile', flagfile) cmd = ['gjslint'] + [str(arg) for arg in args] + paths popen_args = { 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE } proc = subprocess.Popen(cmd, **popen_args) result = proc.communicate() logging.info(result[0].decode())
def print_version(): """Prints a googkit version. """ print(_('googkit {version}').format(version=VERSION))
def run_internal(self): cwd = self.env.cwd self.copy_template(cwd) logging.info( _('Initialized googkit project in {path}').format(path=cwd))
def run_internal(self): cwd = self.env.cwd self.copy_template(cwd) logging.info(_('Initialized googkit project in {path}').format( path=cwd))