Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorMarco Pesenti Gritti <mpgritti@gmail.com>2008-10-19 00:50:58 (GMT)
committer Marco Pesenti Gritti <mpgritti@gmail.com>2008-10-19 00:50:58 (GMT)
commit336368cbddd5ea79543be7d1b7e2eed142942cab (patch)
treead3080b476084f6ab320f9719463832940d379a8 /scripts
parent865b0335aa30412ce87e6a287d6e8cc699e0f623 (diff)
Some directory renaming.
Diffstat (limited to 'scripts')
-rw-r--r--scripts/bundlemodule.py94
-rw-r--r--scripts/check.py142
-rw-r--r--scripts/clear.py18
-rw-r--r--scripts/config.py111
-rw-r--r--scripts/data/pylintrc311
-rw-r--r--scripts/depscheck.py37
-rw-r--r--scripts/main.py116
-rwxr-xr-xscripts/mockbuild25
-rw-r--r--scripts/report.py291
-rw-r--r--scripts/sysdeps.py91
10 files changed, 1211 insertions, 25 deletions
diff --git a/scripts/bundlemodule.py b/scripts/bundlemodule.py
new file mode 100644
index 0000000..2372ba3
--- /dev/null
+++ b/scripts/bundlemodule.py
@@ -0,0 +1,94 @@
+__metaclass__ = type
+
+import os
+
+from jhbuild.errors import BuildStateError
+from jhbuild.modtypes import Package, get_branch, register_module_type
+
+__all__ = [ 'BundleModule' ]
+
+class BundleModule(Package):
+ type = 'bundle'
+
+ STATE_CHECKOUT = 'checkout'
+ STATE_FORCE_CHECKOUT = 'force_checkout'
+ STATE_BUILD = 'build'
+ STATE_INSTALL = 'install'
+
+ def __init__(self, name, branch, dependencies=[], after=[]):
+ Package.__init__(self, name, dependencies, after)
+ self.branch = branch
+
+ def get_srcdir(self, buildscript):
+ return self.branch.srcdir
+
+ def get_builddir(self, buildscript):
+ return self.get_srcdir(buildscript)
+
+ def get_revision(self):
+ return self.branch.branchname
+
+ def do_start(self, buildscript):
+ pass
+ do_start.next_state = STATE_CHECKOUT
+ do_start.error_states = []
+
+ def skip_checkout(self, buildscript, last_state):
+ # skip the checkout stage if the nonetwork flag is set
+ return buildscript.config.nonetwork
+
+ def do_checkout(self, buildscript):
+ srcdir = self.get_srcdir(buildscript)
+ buildscript.set_action('Checking out', self)
+ self.branch.checkout(buildscript)
+ # did the checkout succeed?
+ if not os.path.exists(srcdir):
+ raise BuildStateError('source directory %s was not created'
+ % srcdir)
+ do_checkout.next_state = STATE_BUILD
+ do_checkout.error_states = [STATE_FORCE_CHECKOUT]
+
+ def skip_force_checkout(self, buildscript, last_state):
+ return False
+
+ def do_force_checkout(self, buildscript):
+ buildscript.set_action('Checking out', self)
+ self.branch.force_checkout(buildscript)
+ do_force_checkout.next_state = STATE_BUILD
+ do_force_checkout.error_states = [STATE_FORCE_CHECKOUT]
+
+ def skip_build(self, buildscript, last_state):
+ return buildscript.config.nobuild
+
+ def do_build(self, buildscript):
+ buildscript.set_action('Building', self)
+ srcdir = self.get_srcdir(buildscript)
+ builddir = self.get_builddir(buildscript)
+ python = os.environ.get('PYTHON', 'python')
+ cmd = [python, 'setup.py', 'build']
+ buildscript.execute(cmd, cwd=srcdir)
+ do_build.next_state = STATE_INSTALL
+ do_build.error_states = [STATE_FORCE_CHECKOUT]
+
+ def skip_install(self, buildscript, last_state):
+ return buildscript.config.nobuild
+
+ def do_install(self, buildscript):
+ buildscript.set_action('Installing', self)
+ srcdir = self.get_srcdir(buildscript)
+ builddir = self.get_builddir(buildscript)
+ python = os.environ.get('PYTHON', 'python')
+ cmd = [python, 'setup.py', 'install']
+ cmd.extend(['--prefix', buildscript.config.prefix])
+ buildscript.execute(cmd, cwd=srcdir)
+ buildscript.packagedb.add(self.name, self.get_revision() or '')
+ do_install.next_state = Package.STATE_DONE
+ do_install.error_states = []
+
+
+def parse_bundle(node, config, uri, repositories, default_repo):
+ id = node.getAttribute('id')
+ branch = get_branch(node, repositories, default_repo)
+ return BundleModule(id, branch)
+
+register_module_type('bundle', parse_bundle)
diff --git a/scripts/check.py b/scripts/check.py
new file mode 100644
index 0000000..3213f87
--- /dev/null
+++ b/scripts/check.py
@@ -0,0 +1,142 @@
+import os
+import sys
+from optparse import make_option
+import random
+import signal
+import subprocess
+import time
+
+from jhbuild.commands import Command, register_command
+import jhbuild.config
+
+scripts_dir = os.path.dirname(__file__)
+data_dir = os.path.join(scripts_dir, 'data')
+pylintrc_path = os.path.join(data_dir, 'pylintrc')
+
+def check_display(display):
+ result = subprocess.call(['xdpyinfo', '-display', ':%d' % display],
+ stdout=open(os.devnull, "w"),
+ stderr=open(os.devnull, "w"))
+ return result == 0
+
+class UICheck(object):
+ def start(self):
+ self._mainloop = gobject.MainLoop()
+ self._exit_code = 0
+
+ self._start_shell()
+
+ gobject.idle_add(self._start_ui_check_cb)
+
+ self._mainloop.run()
+
+ return self._exit_code
+
+ def _start_shell(self):
+ print 'Launch the shell.'
+
+ env = os.environ
+ env['SUGAR_PROFILE'] = 'uicheck'
+ env['SUGAR_PROFILE_NAME'] = 'uicheck'
+
+ args = ['dbus-launch', '--exit-with-session', 'sugar']
+ shell_process = subprocess.Popen(args, env=env)
+
+ def _ui_check_exit_cb(self, pid, condition):
+ self._exit_code = os.WEXITSTATUS(condition)
+ self._mainloop.quit()
+
+ def _ui_check_read_cb(self, fd, condition):
+ sys.stdout.write(os.read(fd, 1024))
+ return True
+
+ def _start_ui_check_cb(self):
+ print 'Launch UI check.'
+
+ pid, stdin, stdout, stderr = gobject.spawn_async(
+ ['sugar-ui-check'], standard_output=True, standard_error=True,
+ flags=gobject.SPAWN_SEARCH_PATH | gobject.SPAWN_DO_NOT_REAP_CHILD)
+
+ gobject.io_add_watch(stdout, gobject.IO_IN, self._ui_check_read_cb)
+ gobject.io_add_watch(stderr, gobject.IO_IN, self._ui_check_read_cb)
+
+ gobject.child_watch_add(pid, self._ui_check_exit_cb)
+
+class cmd_check(Command):
+
+ name = 'check'
+ usage_args = ''
+
+ def __init__(self):
+ Command.__init__(self, [
+ make_option('-t', '--type', action='store',
+ dest='type', default=None,
+ help='specify the check type')
+ ])
+
+ def start_xvfb(self):
+ result = None
+
+ for port in range(100, 1000):
+ if not check_display(port):
+ try:
+ p = subprocess.Popen(['Xvfb', '-ac', ':%d' % port],
+ stdout=open(os.devnull, "w"),
+ stderr=subprocess.STDOUT)
+
+ tries = 10
+ while tries > 0:
+ if check_display(port):
+ os.environ['DISPLAY'] = ':%d' % port
+ return p.pid
+ else:
+ tries -= 1
+ time.sleep(0.1)
+ except OSError:
+ break
+
+ print 'Cannot execute xfvb, will use the default display.'
+ return None
+
+ def lint(self, module):
+ xvfb_pid = self.start_xvfb()
+ try:
+ subprocess.call(['pylint', module, '--rcfile=%s' % pylintrc_path])
+ finally:
+ os.kill(xvfb_pid, signal.SIGTERM)
+
+ def check_ui(self, config):
+ xvfb_pid = self.start_xvfb()
+ try:
+ result = UICheck().start()
+ finally:
+ os.kill(xvfb_pid, signal.SIGTERM)
+
+ return result
+
+ def check_pylint(self, config):
+ self.lint('sugar')
+ self.lint('jarabe')
+
+ ext_path = os.path.join(config.prefix, 'share', 'sugar', 'extensions')
+ jhbuild.config.addpath('PYTHONPATH', ext_path)
+
+ self.lint('cpsection')
+ self.lint('deviceicon')
+
+ def run(self, config, options, args):
+ result = 0
+
+ if not options.type or options.type == 'pylint':
+ self.check_pylint(config)
+ if not options.type or options.type == 'ui':
+ result = self.check_ui(config)
+
+ return result
+
+try:
+ import gobject
+
+ register_command(cmd_check)
+except ImportError:
+ print 'Disable check, pygobject is not installed.'
diff --git a/scripts/clear.py b/scripts/clear.py
new file mode 100644
index 0000000..cc25e34
--- /dev/null
+++ b/scripts/clear.py
@@ -0,0 +1,18 @@
+import os
+import shutil
+
+from jhbuild.commands import Command, register_command
+
+class cmd_clear(Command):
+
+ name = 'clear'
+ usage_args = ''
+
+ def run(self, config, options, args):
+ if os.path.exists(config.checkoutroot):
+ shutil.rmtree(config.checkoutroot)
+
+ if os.path.exists(config.prefix):
+ shutil.rmtree(config.prefix)
+
+register_command(cmd_clear)
diff --git a/scripts/config.py b/scripts/config.py
new file mode 100644
index 0000000..b9ccb19
--- /dev/null
+++ b/scripts/config.py
@@ -0,0 +1,111 @@
+import os
+import sys
+
+import jhbuild.config
+
+import sysdeps
+
+class Config(jhbuild.config.Config):
+ def __init__(self, rc_file):
+ scripts_dir = os.path.dirname(__file__)
+ self._base_dir = os.path.dirname(scripts_dir)
+ self._set_dir = os.path.join(self._base_dir, 'config', 'modulesets')
+
+ jhbuild.config.Config.__init__(self, self._ensure_rc_file(rc_file))
+
+ self._setup()
+
+ def get_user_path(self):
+ user_path = os.path.expanduser('~/.sugar-jhbuild')
+ if not os.path.exists(user_path):
+ os.mkdir(user_path)
+ return user_path
+
+ def _ensure_rc_file(self, rc_file):
+ if not os.path.exists(rc_file):
+ f = open(rc_file, 'w')
+ f.write('# Created by sugar-jhbuild')
+ f.close
+ return rc_file
+
+ def _setup(self):
+ self.autogenargs = ''
+
+ self.moduleset = []
+ self._add_moduleset('glucose-external.modules')
+ self._add_moduleset('tools.modules')
+ self._add_moduleset('extra.modules')
+ self._add_moduleset('extra-activities.modules')
+
+ release_dir = None
+ for f in os.listdir(self._set_dir):
+ if f.startswith('release-'):
+ release_dir = f
+
+ self._add_moduleset('glucose.modules', release_dir)
+ self._add_moduleset('fructose.modules', release_dir)
+
+ self.modules = [ 'meta-tools',
+ 'meta-glucose',
+ 'meta-fructose' ]
+
+ self.checkoutroot = os.path.join(self._base_dir, 'source')
+ self.tarballdir = self.checkoutroot
+
+ for package, source in sysdeps.get_packages():
+ if source and source not in self.skip:
+ self.skip.append(source)
+
+ def _add_moduleset(self, moduleset, release_dir=None):
+ if release_dir:
+ path = os.path.join(self._set_dir, release_dir)
+ else:
+ path = self._set_dir
+
+ self.moduleset.append(os.path.join(path, moduleset))
+
+ def setup_env(self):
+ self.prefix = os.path.join(self._base_dir, 'install')
+
+ jhbuild.config.Config.setup_env(self)
+
+ jhbuild.config.addpath('XDG_DATA_DIRS', '/usr/share')
+ jhbuild.config.addpath('XDG_DATA_DIRS', os.path.join(self.prefix, 'share'))
+
+ if self.use_lib64:
+ path = 'lib64/gtk-2.0/'
+ else:
+ path = 'lib/gtk-2.0/'
+ jhbuild.config.addpath('GTK_PATH', os.path.join(self.prefix, path))
+ jhbuild.config.addpath('GTK_DATA_PREFIX', self.prefix)
+
+ os.environ['SUGAR_PREFIX'] = self.prefix
+ os.environ['SUGAR_PATH'] = os.path.join(self.prefix, 'share', 'sugar')
+ os.environ['SUGAR_LOGGER_LEVEL'] = 'debug'
+
+ # Enable debug log of the Telepathy components
+ os.environ['GABBLE_DEBUG'] = 'all'
+ os.environ['SALUT_DEBUG'] = 'all'
+ os.environ['STREAM_ENGINE_DEBUG'] = 'all'
+
+ # We need to add the gtk-2.0 directory explicitly to
+ # the Python path since '.pth' files (here pygtk.pth)
+ # only work properly in system directories
+ pythonversion = 'python' + str(sys.version_info[0]) + '.' + \
+ str(sys.version_info[1])
+ if self.use_lib64:
+ pythonpath = os.path.join(self.prefix, 'lib64', pythonversion,
+ 'site-packages', 'gtk-2.0')
+ else:
+ pythonpath = os.path.join(self.prefix, 'lib', pythonversion,
+ 'site-packages', 'gtk-2.0')
+ jhbuild.config.addpath('PYTHONPATH', pythonpath)
+
+ python_lib = os.path.join(self.prefix, 'lib', 'python2.5', 'site-packages')
+ os.environ['PYTHON_LIB'] = python_lib
+
+ if 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
+ del os.environ['DBUS_SESSION_BUS_ADDRESS']
+
+ if not 'SUGAR_PROFILE' in os.environ:
+ os.environ['SUGAR_PROFILE'] = 'default'
diff --git a/scripts/data/pylintrc b/scripts/data/pylintrc
new file mode 100644
index 0000000..b161e38
--- /dev/null
+++ b/scripts/data/pylintrc
@@ -0,0 +1,311 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS,_sugarext.so,_sugarbaseext.so,dispatch
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+disable-msg-cat=refactor,information
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+# FIXME
+# W0105 reported on doc string for toplevel module variables
+disable-msg=C0111,C0103,C0302,W0511,W0233,W0231,W0142,W0613,W0703,W0603,W0105
+
+
+[REPORTS]
+
+# set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note).You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamicaly set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=no
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+acquired-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=([^_]*_)|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
diff --git a/scripts/depscheck.py b/scripts/depscheck.py
new file mode 100644
index 0000000..f318404
--- /dev/null
+++ b/scripts/depscheck.py
@@ -0,0 +1,37 @@
+import sys
+from optparse import make_option
+
+from jhbuild.commands import Command, register_command
+
+import sysdeps
+
+class cmd_depscheck(Command):
+
+ name = 'depscheck'
+ usage_args = ''
+
+ def __init__(self):
+ Command.__init__(self, [
+ make_option('-s', '--script',
+ action='store_true', dest='script', default=False,
+ help=_('script friendly output')),
+ ])
+
+ def run(self, config, options, args):
+ deps = sysdeps.get_packages()
+ if not options.script and not deps:
+ print 'Dependencies information is missing, skip sanity check.'
+ return
+
+ missing_deps = []
+ for package, source in deps:
+ if not sysdeps.check_package(package):
+ missing_deps.append(package)
+
+ if missing_deps:
+ if not options.script:
+ print 'Missing packages:'
+ print ' '.join(missing_deps)
+ sys.exit(1)
+
+register_command(cmd_depscheck)
diff --git a/scripts/main.py b/scripts/main.py
new file mode 100644
index 0000000..272fe98
--- /dev/null
+++ b/scripts/main.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# jhbuild - a build script for GNOME 1.x and 2.x
+# Copyright (C) 2001-2006 James Henstridge
+#
+# main.py: parses command line arguments and starts the build
+#
+# This program 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.
+#
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import sys, os
+import optparse
+import traceback
+
+import jhbuild.main
+import jhbuild.commands
+from jhbuild.errors import UsageError, FatalError
+
+import bundlemodule
+import depscheck
+import check
+import report
+import clear
+
+from config import Config
+
+def help_commands(option, opt_str, value, parser):
+ commands = [
+ ('build', 'update and compile (the default)'),
+ ('build-base', 'build the base sugar dependencies'),
+ ('buildone', 'modules build a single module'),
+ ('update', 'update from version control'),
+ ('updateone', 'update a fixed set of modules'),
+ ('list', 'list what modules would be built'),
+ ('info', 'prints information about modules'),
+ ('tinderbox', 'build non-interactively with logging'),
+ ('gui', 'build targets from a gui app'),
+ ('run', 'run a command in the build environment'),
+ ('shell', 'start a shell in the build environment'),
+ ('depscheck', 'check that required dependencies exists'),
+ ('dot', 'output a dependency graph for processing with graphviz'),
+ ]
+ print 'sugar-jhbuild commands are:'
+ for (cmd, description) in commands:
+ print ' %-15s %s' % (cmd, description)
+ print
+ print 'For more information run "sugar-jhbuild <command> --help"'
+ parser.exit()
+
+def main(args):
+ parser = optparse.OptionParser(
+ usage='%prog [ -f config ] command [ options ... ]',
+ description='Build sugar and his dependencies.')
+ parser.disable_interspersed_args()
+ parser.add_option('--help-commands', action='callback',
+ callback=help_commands,
+ help='Information about available jhbuild commands')
+ parser.add_option('-f', '--file', action='store', metavar='CONFIG',
+ type='string', dest='configfile',
+ default=os.path.join(os.environ['HOME'], '.olpc.jhbuildrc'),
+ help='use a non default configuration file')
+ parser.add_option('-m', '--moduleset', action='store', metavar='URI',
+ type='string', dest='moduleset', default=None,
+ help='use a non default module set')
+ parser.add_option('--no-interact', action='store_true',
+ dest='nointeract', default=False,
+ help='do not prompt for input')
+
+ options, args = parser.parse_args(args)
+
+ try:
+ config = Config(options.configfile)
+ except FatalError, exc:
+ sys.stderr.write('sugar-jhbuild: %s\n' % (str(exc)))
+ sys.exit(1)
+
+ if options.moduleset: config.moduleset = options.moduleset
+ if options.nointeract: config.interact = False
+
+ if not args or args[0][0] == '-':
+ command = 'build' # default to cvs update + compile
+ else:
+ command = args[0]
+ args = args[1:]
+
+ if command == 'build' and len(args) == 0:
+ print 'Checking dependencies...'
+ jhbuild.commands.run('depscheck', config, [])
+ elif command == 'run' and len(args) == 0:
+ args.append('sugar-emulator')
+
+ try:
+ return jhbuild.commands.run(command, config, args)
+ except UsageError, exc:
+ sys.stderr.write('sugar-jhbuild %s: %s\n' % (command, str(exc)))
+ parser.print_usage()
+ sys.exit(1)
+ except FatalError, exc:
+ sys.stderr.write('sugar-jhbuild %s: %s\n' % (command, str(exc)))
+ sys.exit(1)
+ except KeyboardInterrupt:
+ print "Interrupted"
+ sys.exit(1)
+ except EOFError:
+ print "EOF"
+ sys.exit(1)
diff --git a/scripts/mockbuild b/scripts/mockbuild
deleted file mode 100755
index 8bebb38..0000000
--- a/scripts/mockbuild
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh
-
-SJH="SJH_DISTRIBUTION=olpc-3 HOME=/ /opt/sugar/sugar-jhbuild"
-SNAPSHOT="sugar-snapshot-`date +%Y%m%d%H%M`.tar.bz2"
-
-mock --init
-mock --install yum git
-
-mock --shell << EOF
- cd /opt
- git clone git://dev.laptop.org/sugar-jhbuild sugar
-
- $SJH depscheck -s >/tmp/deps
-EOF
-
-mock --shell cat /tmp/deps | xargs mock --install
-
-mock --shell << EOF
- $SJH --no-interact build meta-glucose meta-fructose
-
- rm -rf /opt/sugar/source
- tar cvfjP $SNAPSHOT /opt/sugar
-EOF
-
-mock --copyout $SNAPSHOT .
diff --git a/scripts/report.py b/scripts/report.py
new file mode 100644
index 0000000..41e9a00
--- /dev/null
+++ b/scripts/report.py
@@ -0,0 +1,291 @@
+import os
+import sys
+import urllib
+import csv
+import re
+import smtplib
+import subprocess
+import htmllib
+import formatter
+from optparse import make_option
+import StringIO
+
+from jhbuild.commands import Command, register_command
+import jhbuild.moduleset
+
+def _get_ticket_uri(number):
+ return 'http://dev.laptop.org/ticket/' + number
+
+def _send_mail(sender, from_address, to_address, subject, text):
+ msg = 'From: %s<%s>\nTo: %s\nSubject: %s\n%s' % \
+ (sender, from_address, to_address, subject, text)
+
+ server = smtplib.SMTP('localhost')
+ server.sendmail(from_address, to_address, msg)
+ server.quit()
+
+class TextWriter(object):
+ def __init__(self, out):
+ self.out = out
+
+ def write_headline(self, headline):
+ self.out.write('= ' + headline + ' =\n\n')
+
+ def write_tickets(self, tickets, compact=False):
+ for ticket in tickets:
+ number = ticket['number']
+ summary = ticket['summary']
+ if compact:
+ self.out.write('* #%s %s\n' % (number, summary))
+ else:
+ self.out.write(summary + '\n')
+ self.out.write(_get_ticket_uri(number) + '\n\n')
+
+ if compact:
+ self.out.write('\n')
+
+ def write_testcases(self, testcases):
+ for number, testcase in testcases:
+ self.out.write('#%s\n\n%s\n\n\n' % (number, testcase))
+
+class ReleaseReport(object):
+ def __init__(self, config, release):
+ module, self._version = release.rsplit('-', 1)
+
+ module_set = jhbuild.moduleset.load(config)
+ self._module = module_set.modules[module]
+ self._config = config
+ self._tickets = []
+ self._testcases = []
+
+ def generate(self):
+ cwd = os.getcwd()
+ os.chdir(self._module.get_srcdir(self._config))
+
+ p = subprocess.Popen(['git', 'tag'], stdout=subprocess.PIPE)
+ tags = p.stdout.read().split('\n')
+ tags = [ tag for tag in tags if tag.startswith('v') ]
+
+ release_tag = 'v' + self._version
+ try:
+ i = tags.index(release_tag)
+ except ValueError:
+ print 'The tag you provided does not exist.'
+ return
+
+ if i > 0:
+ previous = tags[i - 1]
+ interval = previous + '..' + release_tag
+ else:
+ interval = release_tag
+
+ p = subprocess.Popen(['git', 'log', interval,
+ '--pretty=format:%an|||%s'],
+ stdout=subprocess.PIPE)
+ commits = p.stdout.read().split('\n')
+
+ tickets = []
+ for row in commits:
+ author, subject = row.split('|||')
+
+ match = re.search("\#([0-9]*)", subject)
+ if match:
+ tickets.append(match.group(1))
+
+ for n in tickets:
+ f = urllib.urlopen('http://dev.laptop.org/ticket/%s?format=csv' % n)
+
+ reader = csv.DictReader(f)
+ row = reader.next()
+
+ ticket = { 'number' : row['id'],
+ 'summary' : row['summary'] }
+ self._tickets.append(ticket)
+
+ f.close()
+
+ url = 'http://dev.laptop.org/ticket/%s?format=rss' % n
+
+ import feedparser
+ parser = feedparser.parse(url)
+
+ for entry in parser.entries:
+ out = StringIO.StringIO()
+
+ writer = formatter.DumbWriter(out)
+ form = formatter.AbstractFormatter(writer)
+
+ html_parser = htmllib.HTMLParser(form)
+ html_parser.feed(entry['summary_detail']['value'])
+ html_parser.close()
+
+ comment = out.getvalue().strip()
+
+ out.close()
+
+ marker = '|testcase|'
+ i = comment.lower().find(marker)
+ if i >= 0:
+ i += len(marker)
+ self._testcases.append([n, comment[i:].strip()])
+
+ os.chdir(cwd)
+
+ def write(self, writer):
+ writer.write_headline('Closed tickets')
+ writer.write_tickets(self._tickets, compact=True)
+
+ if self._testcases:
+ writer.write_headline('Testcases')
+ writer.write_testcases(self._testcases)
+
+ def save(self):
+ pass
+
+class ReviewsReport(object):
+ def __init__(self, config):
+ self._requested = {}
+ self._approved = {}
+ self._rejected = {}
+ self._state_path = os.path.join(config.get_user_path(), 'reviews')
+
+ def generate(self):
+ f = urllib.urlopen('http://dev.laptop.org/query?' \
+ 'format=csv&' \
+ 'component=sugar&' \
+ 'component=datastore&' \
+ 'component=presence-service&' \
+ 'component=journal-activity&' \
+ 'col=id&col=summary&col=keywords&' \
+ 'keywords=~r%2B&keywords=~r-&keywords=~r%3F')
+
+ reader = csv.reader(f)
+ reader.next()
+
+ for row in reader:
+ number = row[0]
+ keyword = row[2]
+
+ ticket = { 'number' : row[0],
+ 'summary' : row[1] }
+
+ if 'r?' in keyword:
+ ticket['review_state'] = 'r?'
+ self._requested[number] = ticket
+ if 'r+' in keyword:
+ ticket['review_state'] = 'r+'
+ self._approved[number] = ticket
+ if 'r-' in keyword:
+ ticket['review_state'] = 'r-'
+ self._rejected[number] = ticket
+
+ f.close()
+
+ def write(self, writer):
+ old_requested, old_approved, old_rejected = self._load()
+
+ requested = self._diff_tickets(old_requested, self._requested)
+ approved = self._diff_tickets(old_approved, self._approved)
+ rejected = self._diff_tickets(old_rejected, self._rejected)
+
+ if requested:
+ writer.write_headline('New requests')
+ writer.write_tickets(requested)
+
+ if approved:
+ writer.write_headline('Approved requests')
+ writer.write_tickets(approved)
+
+ if rejected:
+ writer.write_headline('Rejected requests')
+ writer.write_tickets(rejected)
+
+ def save(self):
+ s = json.write([self._requested, self._approved, self._rejected])
+
+ f = open(os.path.join(self._state_path), 'w')
+ f.write(s)
+ f.close()
+
+ def _load(self):
+ path = os.path.join(self._state_path)
+ if os.path.exists(path):
+ f = open(path, 'r')
+ result = json.read(f.read())
+ f.close()
+
+ return result
+ else:
+ return ({}, {}, {})
+
+ def _diff_tickets(self, old, new):
+ diff = []
+ for number, ticket in new.items():
+ if number in old:
+ old_ticket = old[number]
+ if old_ticket['review_state'] != ticket['review_state']:
+ diff.append(ticket)
+ else:
+ diff.append(ticket)
+
+ return diff
+
+class cmd_report(Command):
+
+ name = 'report'
+ usage_args = ''
+
+ def __init__(self):
+ Command.__init__(self, [
+ make_option('-t', '--type', action='store',
+ dest='type', default=None,
+ help='specify the report type'),
+ make_option('-s', '--sendto', action='append',
+ dest='sendto', default=None,
+ help='send report to the specified mail address')
+ ])
+
+ def run(self, config, options, args):
+ report_types = [ 'reviews', 'release' ]
+
+ if options.type not in report_types:
+ print 'Available reports:\n' + '\n'.join(report_types)
+ return 1
+
+ if options.type == 'reviews':
+ report = ReviewsReport(config)
+ elif options.type == 'release':
+ if args:
+ report = ReleaseReport(config, args[0])
+ else:
+ print 'Please provide a module name.'
+ return 1
+
+ report.generate()
+
+ out = StringIO.StringIO()
+ report.write(TextWriter(out))
+ text = out.getvalue()
+ out.close()
+
+ if options.sendto:
+ if text:
+ print 'Sending to ' + ', '.join(options.sendto)
+
+ for to_address in options.sendto:
+ _send_mail('Release Team',
+ 'mpgritti@gmail.com', to_address,
+ 'Reviews report', text)
+ else:
+ print 'Empty report, do not send'
+ else:
+ print text
+
+ report.save()
+
+try:
+ import json
+
+ register_command(cmd_report)
+except ImportError:
+ print 'Disable report, json-py is not installed.'
diff --git a/scripts/sysdeps.py b/scripts/sysdeps.py
new file mode 100644
index 0000000..b398cff
--- /dev/null
+++ b/scripts/sysdeps.py
@@ -0,0 +1,91 @@
+import os
+import subprocess
+
+from xml.dom import minidom
+
+scripts_dir = os.path.dirname(__file__)
+base_dir = os.path.dirname(scripts_dir)
+
+def get_distribution():
+ if 'SJH_DISTRIBUTION' in os.environ:
+ return os.environ['SJH_DISTRIBUTION'].split('-')
+
+ # Fedora
+ if os.path.exists('/etc/fedora-release'):
+ name = 'fedora'
+
+ f = open('/etc/fedora-release')
+ full_name = f.read()
+ f.close()
+
+ if 'Rawhide' in full_name:
+ version = 'rawhide'
+ else:
+ version = full_name.split(' ')[2]
+
+ return name, version
+
+ # Debian and Ubuntu
+ try:
+ out, err = subprocess.Popen(['lsb_release', '-is'],
+ stdout=subprocess.PIPE).communicate()
+ name = out.strip().lower()
+
+ out, err = subprocess.Popen(['lsb_release', '-rs'],
+ stdout=subprocess.PIPE).communicate()
+ version = out.strip()
+
+ if name == 'debian' and version == 'testing':
+ version = 'unstable'
+
+ return name, version
+ except OSError:
+ pass
+
+ return None, None
+
+def check_package(package):
+ name, version = get_distribution()
+ if name == 'fedora':
+ ret = subprocess.call(['rpm', '--quiet', '-q', package])
+ return ret == 0
+ elif name in ['ubuntu', 'debian']:
+ cmd = ["dpkg-query", "-f='${status}'", "-W", package]
+ out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
+ return out.find('install ok installed') != -1
+
+ return None
+
+def parse_dependencies():
+ name, version = get_distribution()
+ if name is None or version is None:
+ return None
+
+ filename = os.path.join(base_dir, 'config', 'sysdeps',
+ '%s-%s.xml' % (name, version))
+
+ if not os.path.exists(filename):
+ return None
+
+ return minidom.parse(filename)
+
+def get_packages():
+ document = parse_dependencies()
+ if document is None:
+ return []
+
+ packages = []
+ root = document.childNodes[0]
+
+ for node in root.childNodes:
+ if node.nodeType == node.ELEMENT_NODE:
+ if node.nodeName == 'package':
+ name = node.getAttribute('name')
+ if node.hasAttribute('source'):
+ source = node.getAttribute('source')
+ else:
+ source = None
+
+ packages.append((name, source))
+
+ return packages