chiark / gitweb /
Made the build process incremental, unless --clean is specified
authorCiaran Gultnieks <ciaran@ciarang.com>
Tue, 4 Jan 2011 23:58:44 +0000 (23:58 +0000)
committerCiaran Gultnieks <ciaran@ciarang.com>
Tue, 4 Jan 2011 23:58:44 +0000 (23:58 +0000)
build.py

index e35b08830b4b6d29917ed38fb13dc465a3ec48c8..4a6fd12b78369188e733babf402a75559f393928 100644 (file)
--- a/build.py
+++ b/build.py
@@ -39,237 +39,253 @@ parser.add_option("-v", "--verbose", action="store_true", default=False,
                   help="Spew out even more information than normal")
 parser.add_option("-p", "--package", default=None,
                   help="Build only the specified package")
+parser.add_option("-c", "--clean", action="store_true", default=False,
+                  help="Clean mode - build everything from scratch")
 (options, args) = parser.parse_args()
 
 # Get all apps...
 apps = read_metadata()
 
-unsigned_dir = 'unsigned'
-if os.path.exists(unsigned_dir):
-    shutil.rmtree(unsigned_dir)
-os.mkdir(unsigned_dir)
+#Clear and/or create the 'built' directory, depending on mode:
+built_dir = 'built'
+if options.clean:
+    if os.path.exists(built_dir):
+        shutil.rmtree(built_dir)
+if not os.path.exists(built_dir):
+    os.mkdir(built_dir)
 
 for app in apps:
 
     if (app['disabled'] is None and app['repo'] != '' 
             and app['repotype'] != '' and (options.package is None or
-            options.package == app['id'])):
+            options.package == app['id']) and len(app['builds']) > 0):
 
-        print "About to build " + app['id']
+        print "Processing " + app['id']
 
         build_dir = 'build_' + app['id']
 
-        # Remove the build directory if it already exists...
-        if os.path.exists(build_dir):
-            shutil.rmtree(build_dir)
-
-        # Get the source code...
-        if app['repotype'] == 'git':
-            if subprocess.call(['git', 'clone', app['repo'], build_dir]) != 0:
-                print "Git clone failed"
-                sys.exit(1)
-        elif app['repotype'] == 'svn':
-            if not app['repo'].endswith("*"):
-                if subprocess.call(['svn', 'checkout', app['repo'], build_dir]) != 0:
-                    print "Svn checkout failed"
-                    sys.exit(1)
-        elif app['repotype'] == 'hg':
-            if subprocess.call(['hg', 'clone', app['repo'], build_dir]) !=0:
-                print "Hg clone failed"
-                sys.exit(1)
-        else:
-            print "Invalid repo type " + app['repotype'] + " in " + app['id']
-            sys.exit(1)
+        got_source = False
+
 
         for thisbuild in app['builds']:
 
-            print "Building version " + thisbuild['version']
+            dest = os.path.join(built_dir, app['id'] + '_' +
+                    thisbuild['vercode'] + '.apk')
+            dest_unsigned = dest + "_unsigned"
 
-            # Optionally, the actual app source can be in a subdirectory...
-            if thisbuild.has_key('subdir'):
-                if app['repotype'] == 'svn' and app['repo'].endswith("*"):
-                    root_dir = build_dir
-                    if subprocess.call(['svn', 'checkout',
-                            app['repo'][:-1] + thisbuild['subdir'],
-                            build_dir]) != 0:
-                        print "Svn checkout failed"
-                        sys.exit(1)
-                else:
-                    root_dir = os.path.join(build_dir, thisbuild['subdir'])
+            if os.path.exists(dest):
+                print "Version " + thisbuild['version'] + " already exists"
             else:
-                root_dir = build_dir
-
-            if app['repotype'] == 'git':
-                if subprocess.call(['git', 'checkout', thisbuild['commit']],
-                        cwd=build_dir) != 0:
-                    print "Git checkout failed"
-                    sys.exit(1)
-            elif app['repotype'] == 'svn':
-                if subprocess.call(['svn', 'update', '-r', thisbuild['commit']],
-                        cwd=build_dir) != 0:
-                    print "Svn update failed"
-                    sys.exit(1)
-            elif app['repotype'] == 'hg':
-                if subprocess.call(['hg', 'checkout', thisbuild['commit']],
-                        cwd=build_dir) != 0:
-                    print "Hg checkout failed"
-                    sys.exit(1)
+                print "Building version " + thisbuild['version']
+
+                if not got_source:
+
+                    got_source = True
+
+                    # Remove the build directory if it already exists...
+                    if os.path.exists(build_dir):
+                        shutil.rmtree(build_dir)
+
+                    # Get the source code...
+                    if app['repotype'] == 'git':
+                        if subprocess.call(['git', 'clone', app['repo'], build_dir]) != 0:
+                            print "Git clone failed"
+                            sys.exit(1)
+                    elif app['repotype'] == 'svn':
+                        if not app['repo'].endswith("*"):
+                            if subprocess.call(['svn', 'checkout', app['repo'], build_dir]) != 0:
+                                print "Svn checkout failed"
+                                sys.exit(1)
+                    elif app['repotype'] == 'hg':
+                        if subprocess.call(['hg', 'clone', app['repo'], build_dir]) !=0:
+                            print "Hg clone failed"
+                            sys.exit(1)
+                    else:
+                        print "Invalid repo type " + app['repotype'] + " in " + app['id']
+                        sys.exit(1)
 
-            else:
-                print "Invalid repo type " + app['repotype']
-                sys.exit(1)
-
-            # Generate (or update) the ant build file, build.xml...
-            parms = ['android','update','project','-p','.']
-            parms.append('--subprojects')
-            if thisbuild.has_key('target'):
-                parms.append('-t')
-                parms.append(thisbuild['target'])
-            if subprocess.call(parms, cwd=root_dir) != 0:
-                print "Failed to update project"
-                sys.exit(1)
-
-            # If the app has ant set up to sign the release, we need to switch
-            # that off, because we want the unsigned apk...
-            if os.path.exists(os.path.join(root_dir, 'build.properties')):
-                if subprocess.call(['sed','-i','s/^key.store/#/',
-                    'build.properties'], cwd=root_dir) !=0:
-                    print "Failed to amend build.properties"
-                    sys.exit(1)
-
-            # Fix old-fashioned 'sdk-location' in local.properties by copying
-            # from sdk.dir, if necessary...
-            if (thisbuild.has_key('oldsdkloc') and
-                    thisbuild['oldsdkloc'] == "yes"):
-                locprops = os.path.join(root_dir, 'local.properties')
-                f = open(locprops, 'r')
-                props = f.read()
-                f.close()
-                sdkloc = re.match(r".*^sdk.dir=(\S+)$.*", props,
-                    re.S|re.M).group(1)
-                props += "\nsdk-location=" + sdkloc + "\n"
-                f = open(locprops, 'w')
-                f.write(props)
-                f.close()
-
-            #Delete unwanted file...
-            if thisbuild.has_key('rm'):
-                os.remove(os.path.join(root_dir, thisbuild['rm']))
-
-            #Build the source tarball right before we build the relase...
-            tarname = app['id'] + '_' + thisbuild['vercode'] + '_src'
-            tarball = tarfile.open(os.path.join(unsigned_dir,
-                tarname + '.tar.gz'), "w:gz")
-            tarball.add(build_dir, tarname)
-            tarball.close()
-
-            # Build the release...
-            p = subprocess.Popen(['ant','release'], cwd=root_dir, 
-                    stdout=subprocess.PIPE)
-            output = p.communicate()[0]
-            if p.returncode != 0:
-                print output
-                print "Build failed"
-                sys.exit(1)
-
-            # Find the apk name in the output...
-            if thisbuild.has_key('bindir'):
-                bindir = os.path.join(build_dir, thisbuild['bindir'])
-            else:
-                bindir = os.path.join(root_dir, 'bin')
-            src = re.match(r".*^.*Creating (\S+) for release.*$.*", output,
-                    re.S|re.M).group(1)
-            src = os.path.join(bindir, src)
-
-            # By way of a sanity check, make sure the version and version
-            # code in our new apk match what we expect...
-            p = subprocess.Popen([aapt_path,'dump','badging',
-               src], stdout=subprocess.PIPE)
-            output = p.communicate()[0]
-            vercode = None
-            version = None
-            for line in output.splitlines():
-                if line.startswith("package:"):
-                    pat = re.compile(".*versionCode='([0-9]*)'.*")
-                    vercode = re.match(pat, line).group(1)
-                    pat = re.compile(".*versionName='([^']*)'.*")
-                    version = re.match(pat, line).group(1)
-
-            # Some apps (e.g. Timeriffic) have had the bonkers idea of
-            # including the entire changelog in the version number. Remove
-            # it so we can compare. (TODO: might be better to remove it
-            # before we compile, in fact)
-            index = version.find(" //")
-            if index != -1:
-                version = version[:index]
-
-            if (version != thisbuild['version'] or
-                    vercode != thisbuild['vercode']):
-                print "Unexpected version/version code in output"
-                sys.exit(1)
-
-            # Copy the unsigned apk to our 'unsigned' directory to be
-            # dealt with later...
-            dest = os.path.join(unsigned_dir, app['id'] + '_' +
-                    thisbuild['vercode'] + '.apk')
-            shutil.copyfile(src, dest)
-
-            # Figure out the key alias name we'll use. Only the first 8
-            # characters are significant, so we'll use the first 8 from
-            # the MD5 of the app's ID and hope there are no collisions.
-            # If a collision does occur later, we're going to have to
-            # come up with a new alogrithm, AND rename all existing keys
-            # in the keystore!
-            if keyaliases.has_key(app['id']):
-                # For this particular app, the key alias is overridden...
-                keyalias = keyaliases[app['id']]
-            else:
-                m = md5.new()
-                m.update(app['id'])
-                keyalias = m.hexdigest()[:8]
-            print "Key alias: " + keyalias
-
-            # See if we already have a key for this application, and
-            # if not generate one...
-            p = subprocess.Popen(['keytool', '-list',
-                '-alias', keyalias, '-keystore', keystore,
-                '-storepass', keystorepass], stdout=subprocess.PIPE)
-            output = p.communicate()[0]
-            if p.returncode !=0:
-                print "Key does not exist - generating..."
-                p = subprocess.Popen(['keytool', '-genkey',
-                    '-keystore', keystore, '-alias', keyalias,
-                    '-keyalg', 'RSA', '-keysize', '2048',
-                    '-validity', '10000',
-                    '-storepass', keystorepass, '-keypass', keypass,
-                    '-dname', keydname], stdout=subprocess.PIPE)
-                output = p.communicate()[0]
-                print output
-                if p.returncode != 0:
-                    print "Failed to generate key"
-                    sys.exit(1)
-
-            # Sign the application...
-            p = subprocess.Popen(['jarsigner', '-keystore', keystore,
-                   '-storepass', keystorepass, '-keypass', keypass,
-                    dest, keyalias], stdout=subprocess.PIPE)
-            output = p.communicate()[0]
-            print output
-            if p.returncode != 0:
-                print "Failed to sign application"
-                sys.exit(1)
-
-            # Zipalign it...
-            tmpfile = dest + ".tmp"
-            os.rename(dest, tmpfile)
-            p = subprocess.Popen(['zipalign', '-v', '4',
-                    tmpfile, dest], stdout=subprocess.PIPE)
-            output = p.communicate()[0]
-            print output
-            if p.returncode != 0:
-                print "Failed to align application"
-                sys.exit(1)
-            os.remove(tmpfile)
+
+                    # Optionally, the actual app source can be in a subdirectory...
+                    if thisbuild.has_key('subdir'):
+                        if app['repotype'] == 'svn' and app['repo'].endswith("*"):
+                            root_dir = build_dir
+                            if subprocess.call(['svn', 'checkout',
+                                    app['repo'][:-1] + thisbuild['subdir'],
+                                    build_dir]) != 0:
+                                print "Svn checkout failed"
+                                sys.exit(1)
+                        else:
+                            root_dir = os.path.join(build_dir, thisbuild['subdir'])
+                    else:
+                        root_dir = build_dir
+
+                    if app['repotype'] == 'git':
+                        if subprocess.call(['git', 'checkout', thisbuild['commit']],
+                                cwd=build_dir) != 0:
+                            print "Git checkout failed"
+                            sys.exit(1)
+                    elif app['repotype'] == 'svn':
+                        if subprocess.call(['svn', 'update', '-r', thisbuild['commit']],
+                                cwd=build_dir) != 0:
+                            print "Svn update failed"
+                            sys.exit(1)
+                    elif app['repotype'] == 'hg':
+                        if subprocess.call(['hg', 'checkout', thisbuild['commit']],
+                                cwd=build_dir) != 0:
+                            print "Hg checkout failed"
+                            sys.exit(1)
+
+                    else:
+                        print "Invalid repo type " + app['repotype']
+                        sys.exit(1)
+
+                    # Generate (or update) the ant build file, build.xml...
+                    parms = ['android','update','project','-p','.']
+                    parms.append('--subprojects')
+                    if thisbuild.has_key('target'):
+                        parms.append('-t')
+                        parms.append(thisbuild['target'])
+                    if subprocess.call(parms, cwd=root_dir) != 0:
+                        print "Failed to update project"
+                        sys.exit(1)
+
+                    # If the app has ant set up to sign the release, we need to switch
+                    # that off, because we want the unsigned apk...
+                    if os.path.exists(os.path.join(root_dir, 'build.properties')):
+                        if subprocess.call(['sed','-i','s/^key.store/#/',
+                            'build.properties'], cwd=root_dir) !=0:
+                            print "Failed to amend build.properties"
+                            sys.exit(1)
+
+                    # Fix old-fashioned 'sdk-location' in local.properties by copying
+                    # from sdk.dir, if necessary...
+                    if (thisbuild.has_key('oldsdkloc') and
+                            thisbuild['oldsdkloc'] == "yes"):
+                        locprops = os.path.join(root_dir, 'local.properties')
+                        f = open(locprops, 'r')
+                        props = f.read()
+                        f.close()
+                        sdkloc = re.match(r".*^sdk.dir=(\S+)$.*", props,
+                            re.S|re.M).group(1)
+                        props += "\nsdk-location=" + sdkloc + "\n"
+                        f = open(locprops, 'w')
+                        f.write(props)
+                        f.close()
+
+                    #Delete unwanted file...
+                    if thisbuild.has_key('rm'):
+                        os.remove(os.path.join(root_dir, thisbuild['rm']))
+
+                    #Build the source tarball right before we build the relase...
+                    tarname = app['id'] + '_' + thisbuild['vercode'] + '_src'
+                    tarball = tarfile.open(os.path.join(built_dir,
+                        tarname + '.tar.gz'), "w:gz")
+                    tarball.add(build_dir, tarname)
+                    tarball.close()
+
+                    # Build the release...
+                    p = subprocess.Popen(['ant','release'], cwd=root_dir, 
+                            stdout=subprocess.PIPE)
+                    output = p.communicate()[0]
+                    if p.returncode != 0:
+                        print output
+                        print "Build failed"
+                        sys.exit(1)
+
+                    # Find the apk name in the output...
+                    if thisbuild.has_key('bindir'):
+                        bindir = os.path.join(build_dir, thisbuild['bindir'])
+                    else:
+                        bindir = os.path.join(root_dir, 'bin')
+                    src = re.match(r".*^.*Creating (\S+) for release.*$.*", output,
+                            re.S|re.M).group(1)
+                    src = os.path.join(bindir, src)
+
+                    # By way of a sanity check, make sure the version and version
+                    # code in our new apk match what we expect...
+                    p = subprocess.Popen([aapt_path,'dump','badging',
+                       src], stdout=subprocess.PIPE)
+                    output = p.communicate()[0]
+                    vercode = None
+                    version = None
+                    for line in output.splitlines():
+                        if line.startswith("package:"):
+                            pat = re.compile(".*versionCode='([0-9]*)'.*")
+                            vercode = re.match(pat, line).group(1)
+                            pat = re.compile(".*versionName='([^']*)'.*")
+                            version = re.match(pat, line).group(1)
+
+                    # Some apps (e.g. Timeriffic) have had the bonkers idea of
+                    # including the entire changelog in the version number. Remove
+                    # it so we can compare. (TODO: might be better to remove it
+                    # before we compile, in fact)
+                    index = version.find(" //")
+                    if index != -1:
+                        version = version[:index]
+
+                    if (version != thisbuild['version'] or
+                            vercode != thisbuild['vercode']):
+                        print "Unexpected version/version code in output"
+                        sys.exit(1)
+
+                    # Copy the unsigned apk to our 'built' directory for further
+                    # processing...
+                    shutil.copyfile(src, dest_unsigned)
+
+                    # Figure out the key alias name we'll use. Only the first 8
+                    # characters are significant, so we'll use the first 8 from
+                    # the MD5 of the app's ID and hope there are no collisions.
+                    # If a collision does occur later, we're going to have to
+                    # come up with a new alogrithm, AND rename all existing keys
+                    # in the keystore!
+                    if keyaliases.has_key(app['id']):
+                        # For this particular app, the key alias is overridden...
+                        keyalias = keyaliases[app['id']]
+                    else:
+                        m = md5.new()
+                        m.update(app['id'])
+                        keyalias = m.hexdigest()[:8]
+                    print "Key alias: " + keyalias
+
+                    # See if we already have a key for this application, and
+                    # if not generate one...
+                    p = subprocess.Popen(['keytool', '-list',
+                        '-alias', keyalias, '-keystore', keystore,
+                        '-storepass', keystorepass], stdout=subprocess.PIPE)
+                    output = p.communicate()[0]
+                    if p.returncode !=0:
+                        print "Key does not exist - generating..."
+                        p = subprocess.Popen(['keytool', '-genkey',
+                            '-keystore', keystore, '-alias', keyalias,
+                            '-keyalg', 'RSA', '-keysize', '2048',
+                            '-validity', '10000',
+                            '-storepass', keystorepass, '-keypass', keypass,
+                            '-dname', keydname], stdout=subprocess.PIPE)
+                        output = p.communicate()[0]
+                        print output
+                        if p.returncode != 0:
+                            print "Failed to generate key"
+                            sys.exit(1)
+
+                    # Sign the application...
+                    p = subprocess.Popen(['jarsigner', '-keystore', keystore,
+                           '-storepass', keystorepass, '-keypass', keypass,
+                            dest_unsigned, keyalias], stdout=subprocess.PIPE)
+                    output = p.communicate()[0]
+                    print output
+                    if p.returncode != 0:
+                        print "Failed to sign application"
+                        sys.exit(1)
+
+                    # Zipalign it...
+                    p = subprocess.Popen(['zipalign', '-v', '4',
+                            dest_unsigned, dest], stdout=subprocess.PIPE)
+                    output = p.communicate()[0]
+                    print output
+                    if p.returncode != 0:
+                        print "Failed to align application"
+                        sys.exit(1)
+                    os.remove(dest_unsigned)
 
 print "Finished."