Initial project import
authorRalf Jung <post@ralfj.de>
Tue, 12 Jun 2012 19:52:04 +0000 (21:52 +0200)
committerRalf Jung <post@ralfj.de>
Tue, 12 Jun 2012 19:52:04 +0000 (21:52 +0200)
build_system.py [new file with mode: 0644]
config.py.sample [new file with mode: 0644]
kdebuildpy.py [new file with mode: 0755]
vcs.py [new file with mode: 0644]

diff --git a/build_system.py b/build_system.py
new file mode 100644 (file)
index 0000000..5035e68
--- /dev/null
@@ -0,0 +1,37 @@
+import os, subprocess
+
+'''A build system has three methods: "configure" (with optionak "force" parameter), "build" and "install"'''
+
+# Compile, build, and install cmake projects:
+class CMake:
+       def __init__(self, sourceFolder, buildFolder, config):
+               self.sourceFolder = os.path.abspath(sourceFolder)
+               self.buildFolder = os.path.abspath(buildFolder)
+               self.installDir = config.installDir
+               self.buildType = config.buildType
+               self.jobs = config.jobs
+               self.buildCmdPrefix = config.buildCmdPrefix
+               self.installCmdPrefix = config.installCmdPrefix
+       
+       def configure(self, force=False):
+               print "Configuring",self.folder
+               if not os.path.exists(self.buildFolder): os.makedirs(self.buildFolder)
+               os.chdir(self.buildFolder)
+               # check if we actually need to work
+               cacheFile = 'CMakeCache.txt'
+               if os.path.exists(cacheFile) and os.path.exists('Makefile') and not force: return
+               # yes we do! make sure we start clean, and then go ahead
+               if os.path.exists(cacheFile): os.remove(cacheFile)
+               os.putenv('PKG_CONFIG_PATH', os.path.join(self.installDir, 'lib', 'pkgconfig')) # I found no way to do this within cmake
+               subprocess.check_call(['cmake', self.sourceFolder, '-DCMAKE_BUILD_TYPE='+self.buildType, '-DCMAKE_INSTALL_PREFIX='+self.installDir])
+               os.unsetenv('PKG_CONFIG_PATH')
+       
+       def build(self):
+               print "Building",self.folder
+               os.chdir(self.buildFolder)
+               subprocess.check_call(self.buildCmdPrefix + ['make', '-j'+str(self.jobs)])
+       
+       def install(self):
+               print "Installing",self.folder
+               os.chdir(self.buildFolder)
+               subprocess.check_call(self.installCmdPrefix + ['make', 'install'])
diff --git a/config.py.sample b/config.py.sample
new file mode 100644 (file)
index 0000000..3aa886c
--- /dev/null
@@ -0,0 +1,31 @@
+# global configuration
+installDir = '/opt/kde'
+buildDir = 'build'
+buildType = 'Debug'
+jobs = 2
+buildCmdPrefix = ['nice']
+installCmdPrefix = []
+# the modules we are interested in
+KDEBranch = 'KDE/4.8'
+modules = [
+       # KDE core
+#      {'type': 'kde+svn', 'in-folder': 'kde', 'name': 'oxygen-icons', 'svn-path': 'tags/KDE/4.8.2/oxygen-icons'},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kdelibs', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kactivities', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kdepimlibs', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kde-runtime', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kde-workspace', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kde-baseapps', 'version': 'origin/'+KDEBranch},
+#      {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kdepim-runtime', 'version': 'origin/'+KDEBranch},
+       # KDE applications
+#      {'type': 'kde+svn', 'in-folder': 'kde', 'name': 'kde-wallpapers', 'svn-path': 'branches/'+KDEBranch+'/kde-wallpapers'},
+       {'type': 'kde+svn', 'in-folder': 'kde', 'name': 'kdenetwork', 'svn-path': 'branches/'+KDEBranch+'/kdenetwork'},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'konsole', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kate', 'version': 'origin/'+KDEBranch},
+       {'type': 'kde+git', 'in-folder': 'kde', 'name': 'kwallet', 'version': 'origin/'+KDEBranch},
+       # Extragear applications, addons
+#      {'type': 'kde+git', 'in-folder': 'extragear', 'name': 'polkit-kde-agent-1', 'version': 'v0.99.0'},
+       {'type': 'kde+git', 'in-folder': 'extragear', 'name': 'networkmanagement', 'version': 'v0.9.0.1'},
+#      {'type': 'kde+git', 'in-folder': 'extragear', 'name': 'kdevplatform', 'version': 'v1.2.3'},
+#      {'type': 'kde+git', 'in-folder': 'extragear', 'name': 'kdevelop', 'version': 'v4.2.3'},
+]
diff --git a/kdebuildpy.py b/kdebuildpy.py
new file mode 100755 (executable)
index 0000000..720c96b
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+import vcs, build_system, imp
+import argparse, os, sys, subprocess
+from collections import OrderedDict
+
+# read command-line arguments
+parser = argparse.ArgumentParser(description='Build KDE')
+parser.add_argument("-c, --config",
+                    dest="config", default="config.py",
+                    help="kdebuildpy config file")
+parser.add_argument("--reconfigure",
+                    action="store_true", dest="reconfigure",
+                    help="Force configuration to be run")
+parser.add_argument("--phases", choices=["update", "configure", "compile"], nargs='*', metavar='PHASE',
+                    dest="phases", default=["update", "configure", "compile"],
+                    help="For each module, run the given phases in the given order. Possible phases are: update, configure, compile")
+parser.add_argument("--resume-from", metavar='MODULE',
+                    dest="resume_from",
+                    help="Resume building from the given repository")
+parser.add_argument("modules",  metavar='MODULE', nargs='*',
+                    help="Manually specify modules to be built")
+args = parser.parse_args()
+
+# load config
+config = imp.load_source('config', args.config)
+
+# template for a KDE project: combine git/svn with cmake
+def sourceFolder(module):
+       return os.path.join(module['in-folder'], module['name'])
+def buildFolder(module):
+       return os.path.join(config.buildDir, sourceFolder(module))
+
+class KDEGitProject(vcs.Git, build_system.CMake):
+       def __init__(self, module):
+               vcs.Git.__init__(self, sourceFolder(module),'kde:'+module['name'], module['version'])
+               build_system.CMake.__init__(self, sourceFolder(module), buildFolder(module), config)
+               self.name = module['name']
+
+class KDESVNProject(vcs.SVN, build_system.CMake):
+       def __init__(self, module):
+               vcs.SVN.__init__(self, sourceFolder(module), 'svn://svn.kde.org/home/kde/'+module['svn-path'])
+               build_system.CMake.__init__(self, sourceFolder(module), buildFolder(module), config)
+               self.name = module['name']
+
+moduleTypes = {
+       'kde+git': KDEGitProject,
+       'kde+svn': KDESVNProject
+}
+
+# return the position of the given item in the list
+def findInList(list, item):
+       for i in xrange(len(list)):
+               if list[i] == item:
+                       return i
+       raise Exception("%s not found in list" % str(item))
+
+# collect list of projects (separate run, since the actual compilation will change the working directory!)
+projects = OrderedDict()
+for module in config.modules:
+       if module['name'] in projects:
+               raise Exception("Duplicate module name "+module['name'])
+       if not module['type'] in moduleTypes:
+               raise Exception("Invalid module type "+module['type'])
+       projects[module['name']] = moduleTypes[module['type']](module) # create module of proper type
+
+# now check what we have to do
+workProjects = []
+if args.modules:
+       if args.resume_from is not None:
+               raise Exception("Can not use --resume-from and manually specify modules")
+       for module in args.modules:
+               if not module in projects:
+                       raise Exception("Project %s does not exist" % module)
+               workProjects.append(projects[module])
+elif args.resume_from is None: workProjects = projects.values() # all the projects
+else:
+       if not args.resume_from in projects:
+               raise Exception("Project %s does not exist" % args.resume_from)
+       startWith = projects[args.resume_from]
+       startIndex = findInList(projects.values(), startWith)
+       workProjects = projects.values()[startIndex:]
+
+# and do it!
+for project in workProjects:
+       try:
+               for phase in args.phases:
+                       if phase == 'update':
+                               project.update()
+                       elif phase == 'configure':
+                               project.configure(force=args.reconfigure)
+                       elif phase == 'compile':
+                               project.build()
+                               project.install()
+                       else:
+                               raise Exception("Invalid phase "+phase)
+       except (subprocess.CalledProcessError, KeyboardInterrupt) as e:
+               print >> sys.stderr
+               print >> sys.stderr
+               if isinstance(e, KeyboardInterrupt): # str(e) would be the empty string
+                       print >> sys.stderr, "Interruped by user while processing %s" % (project.name)
+               else:
+                       print >> sys.stderr, "Error while processing %s: %s" % (project.name, str(e))
+               print >> sys.stderr
+               sys.exit(1)
diff --git a/vcs.py b/vcs.py
new file mode 100644 (file)
index 0000000..994f03b
--- /dev/null
+++ b/vcs.py
@@ -0,0 +1,62 @@
+import os, git, subprocess
+
+'''A VCS must have an "update" method with an optional "force" parameter.'''
+
+# Fetch updates from git
+class Git:
+       def __init__(self, folder, url, commit):
+               self.folder = os.path.abspath(folder)
+               self.url = url
+               self.commit = commit
+       
+       class _ProgressPrinter(git.remote.RemoteProgress):
+               def update(self, op_code, cur_count, max_count=None, message=''):
+                       print self._cur_line+(" "*30)+"\r",
+       
+       def update(self):
+               print "Updating",self.folder
+               isBranch = (self.commit.startswith('origin/'))
+               if isBranch:
+                       branchname = self.commit[len('origin/'):]
+               else:
+                       branchname = "tag"
+               # get us a git repository, and the "origin" remote
+               if os.path.exists(self.folder):
+                       # load existing repo
+                       repo = git.Repo(self.folder)
+                       origin = repo.remotes.origin
+               else:
+                       # create a new one
+                       os.makedirs(self.folder)
+                       repo = git.Repo.init(self.folder)
+                       origin = repo.create_remote('origin', self.url)
+               origin.fetch(progress=Git._ProgressPrinter()) # download new data
+               print " "*80+"\r", # clean the line we are in
+               # create/find correct branch
+               if branchname in repo.heads:
+                       branch = repo.heads[branchname]
+               else:
+                       branch = repo.create_head(branchname, self.commit)
+                       if isBranch:
+                               branch.set_tracking_branch(origin.refs[branchname])
+               # update it to the latest remote commit
+               branch.checkout()
+               repo.git.rebase(self.commit)
+               print "...done",
+               if repo.head.reference.commit != repo.refs[self.commit].commit:
+                       print "(keeping local patches around)",
+               print
+
+# Fetch updates via SVN
+class SVN:
+       def __init__(self, folder, svnPath):
+               self.folder = os.path.abspath(folder)
+               self.svnPath = svnPath
+       
+       def update(self):
+               print "Updating",self.folder
+               if os.path.exists(self.folder):
+                       os.chdir(self.folder) # go into repository
+                       subprocess.check_call(['svn', 'switch', self.svnPath]) # and update to the URL we got
+               else:
+                       subprocess.check_call(['svn', 'co', self.svnPath, self.folder])# just download it