# Execute the build script...
print "Starting build..."
chan = sshs.get_transport().open_session()
+ chan.get_pty()
cmdline = 'python build.py --on-server'
if force:
cmdline += ' --force --test'
cmdline += " %s:%s" % (app['id'], thisbuild['vercode'])
chan.exec_command('bash -c ". ~/.bsenv && ' + cmdline + '"')
output = ''
- error = ''
while not chan.exit_status_ready():
while chan.recv_ready():
output += chan.recv(1024)
- while chan.recv_stderr_ready():
- error += chan.recv_stderr(1024)
time.sleep(0.1)
print "...getting exit status"
returncode = chan.recv_exit_status()
if len(get) == 0:
break
output += get
- while True:
- get = chan.recv_stderr(1024)
- if len(get) == 0:
- break
- error += get
if returncode != 0:
- raise BuildException("Build.py failed on server for %s:%s" % (app['id'], thisbuild['version']), output, error)
+ raise BuildException("Build.py failed on server for %s:%s" % (app['id'], thisbuild['version']), output)
# Retrieve the built files...
print "Retrieving build output..."
ftp.get(apkfile, os.path.join(output_dir, apkfile))
ftp.get(tarball, os.path.join(output_dir, tarball))
except:
- raise BuildException("Build failed for %s:%s - missing output files" % (app['id'], thisbuild['version']), output, error)
+ raise BuildException("Build failed for %s:%s - missing output files" % (app['id'], thisbuild['version']), output)
ftp.close()
finally:
p = FDroidPopen(cmd, cwd=maven_dir)
elif thisbuild['type'] == 'gradle':
+
print "Cleaning Gradle project..."
cmd = [config['gradle'], 'clean']
else:
gradle_dir = root_dir
+ adapt_gradle(gradle_dir)
+ for name, number, libpath in srclibpaths:
+ adapt_gradle(libpath)
+
p = FDroidPopen(cmd, cwd=gradle_dir)
elif thisbuild['type'] == 'kivy':
if p is not None and p.returncode != 0:
raise BuildException("Error cleaning %s:%s" %
- (app['id'], thisbuild['version']), p.stdout, p.stderr)
+ (app['id'], thisbuild['version']), p.stdout)
# Scan before building...
print "Scanning source for common problems..."
if p.returncode != 0:
raise BuildException("Error running build command for %s:%s" %
- (app['id'], thisbuild['version']), p.stdout, p.stderr)
+ (app['id'], thisbuild['version']), p.stdout)
# Build native stuff if required...
if thisbuild.get('buildjni') not in (None, 'no'):
del manifest_text
p = FDroidPopen([ndkbuild], cwd=os.path.join(root_dir,d))
if p.returncode != 0:
- raise BuildException("NDK build failed for %s:%s" % (app['id'], thisbuild['version']), p.stdout, p.stderr)
+ raise BuildException("NDK build failed for %s:%s" % (app['id'], thisbuild['version']), p.stdout)
p = None
# Build the release...
elif thisbuild['type'] == 'gradle':
print "Building Gradle project..."
if '@' in thisbuild['gradle']:
- flavour = thisbuild['gradle'].split('@')[0]
+ flavours = thisbuild['gradle'].split('@')[0].split(',')
gradle_dir = thisbuild['gradle'].split('@')[1]
gradle_dir = os.path.join(root_dir, gradle_dir)
else:
- flavour = thisbuild['gradle']
+ flavours = thisbuild['gradle'].split(',')
gradle_dir = root_dir
's@compileSdkVersion[ ]*[0-9]*@compileSdkVersion '+level+'@g',
'build.gradle'], cwd=gradle_dir)
- adapt_gradle(gradle_dir)
-
- for name, number, libpath in srclibpaths:
- adapt_gradle(libpath)
-
- if flavour in ['main', 'yes', '']:
- flavour = ''
+ if len(flavours) == 1 and flavours[0] in ['main', 'yes', '']:
+ flavours[0] = ''
commands = [config['gradle']]
if 'preassemble' in thisbuild:
for task in thisbuild['preassemble'].split():
commands.append(task)
- commands += ['assemble'+flavour+'Release']
+ commands += ['assemble'+''.join(flavours)+'Release']
p = FDroidPopen(commands, cwd=gradle_dir)
bindir = os.path.join(root_dir, 'bin')
if p.returncode != 0:
- raise BuildException("Build failed for %s:%s" % (app['id'], thisbuild['version']), p.stdout, p.stderr)
+ raise BuildException("Build failed for %s:%s" % (app['id'], thisbuild['version']), p.stdout)
print "Successfully built version " + thisbuild['version'] + ' of ' + app['id']
# Find the apk name in the output...
dd = build_dir
if 'subdir' in thisbuild:
dd = os.path.join(dd, thisbuild['subdir'])
- if flavour in ['main', 'yes', '']:
+ if len(flavours) == 1 and flavours[0] == '':
name = '-'.join([os.path.basename(dd), 'release', 'unsigned'])
else:
- name = '-'.join([os.path.basename(dd), flavour, 'release', 'unsigned'])
+ name = '-'.join([os.path.basename(dd), '-'.join(flavours), 'release', 'unsigned'])
src = os.path.join(dd, 'build', 'apk', name+'.apk')
else:
stdout_apk = '\n'.join([
if len(txt) > 8192:
txt = txt[-8192:]
txt = "Build completed at " + time.strftime("%Y-%m-%d %H:%M:%SZ", time.gmtime()) + "\n\n" + txt
- newpage.save(wikilog, summary='Build log')
+ newpage.save(txt, summary='Build log')
except:
print "Error while attempting to publish build log"
return (max_version, max_vercode, max_package)
class BuildException(Exception):
- def __init__(self, value, stdout = None, stderr = None):
+ def __init__(self, value, detail = None):
self.value = value
- self.stdout = stdout
- self.stderr = stderr
+ self.detail = detail
def get_wikitext(self):
ret = repr(self.value) + "\n"
- if self.stdout:
- ret += "=stdout=\n"
+ if self.detail:
+ ret += "=detail=\n"
ret += "<pre>\n"
- ret += str(self.stdout)
- ret += "</pre>\n"
- if self.stderr:
- ret += "=stderr=\n"
- ret += "<pre>\n"
- ret += str(self.stderr)
+ ret += str(self.detail)
ret += "</pre>\n"
return ret
def __str__(self):
ret = repr(self.value)
- if self.stdout:
- ret += "\n==== stdout begin ====\n%s\n==== stdout end ====" % self.stdout.strip()
- if self.stderr:
- ret += "\n==== stderr begin ====\n%s\n==== stderr end ====" % self.stderr.strip()
+ if self.detail:
+ ret += "\n==== detail begin ====\n%s\n==== detail end ====" % self.detail.strip()
return ret
class VCSException(Exception):
p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=libdir)
if p.returncode != 0:
raise BuildException("Error running prepare command for srclib %s"
- % name, p.stdout, p.stderr)
+ % name, p.stdout)
if srclib["Update Project"] == "Yes":
print "Updating srclib %s at path %s" % (name, libdir)
# Check to see whether an error was returned without a proper exit
# code (this is the case for the 'no target set or target invalid'
# error)
- if p.returncode != 0 or (p.stderr != "" and
- p.stderr.startswith("Error: ")):
+ if p.returncode != 0 or p.stdout.startswith("Error: "):
raise BuildException("Failed to update srclib project {0}"
- .format(name), p.stdout, p.stderr)
+ .format(name), p.stdout)
remove_signing_keys(libdir)
p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=root_dir)
if p.returncode != 0:
raise BuildException("Error running init command for %s:%s" %
- (app['id'], build['version']), p.stdout, p.stderr)
+ (app['id'], build['version']), p.stdout)
# Generate (or update) the ant build file, build.xml...
updatemode = build.get('update', 'auto')
# Check to see whether an error was returned without a proper exit
# code (this is the case for the 'no target set or target invalid'
# error)
- if p.returncode != 0 or (p.stderr != "" and
- p.stderr.startswith("Error: ")):
+ if p.returncode != 0 or p.stdout.startswith("Error: "):
raise BuildException("Failed to update project at %s" % d,
- p.stdout, p.stderr)
+ p.stdout)
# Update the local.properties file...
localprops = [ os.path.join(build_dir, 'local.properties') ]
p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=root_dir)
if p.returncode != 0:
raise BuildException("Error running prebuild command for %s:%s" %
- (app['id'], build['version']), p.stdout, p.stderr)
+ (app['id'], build['version']), p.stdout)
return (root_dir, srclibpaths)
def FDroidPopen(commands, cwd=None):
"""
- Runs a command the FDroid way and returns return code and output
+ Run a command and capture the output.
- :param commands and cwd like in subprocess.Popen
+ :param commands: command and argument list like in subprocess.Popen
+ :param cwd: optionally specifies a working directory
+ :returns: A PopenResult.
"""
if options.verbose:
result = PopenResult()
p = subprocess.Popen(commands, cwd=cwd,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout_queue = Queue.Queue()
stdout_reader = AsynchronousFileReader(p.stdout, stdout_queue)
stdout_reader.start()
- stderr_queue = Queue.Queue()
- stderr_reader = AsynchronousFileReader(p.stderr, stderr_queue)
- stderr_reader.start()
- # Check the queues for output (until there is no more to get)
- while not stdout_reader.eof() or not stderr_reader.eof():
- # Show what we received from standard output
+ # Check the queue for output (until there is no more to get)
+ while not stdout_reader.eof():
while not stdout_queue.empty():
line = stdout_queue.get()
if options.verbose:
sys.stdout.flush()
result.stdout += line
- # Show what we received from standard error
- while not stderr_queue.empty():
- line = stderr_queue.get()
- if options.verbose:
- # Output directly to console
- sys.stderr.write(line)
- sys.stderr.flush()
- result.stderr += line
time.sleep(0.1)
p.communicate()
return result
def remove_signing_keys(build_dir):
+ comment = re.compile(r'[ ]*//')
+ signing_configs = re.compile(r'[\t ]*signingConfigs[ \t]*{[ \t]*$')
+ r_open = re.compile(r'.*{[\t ]*$')
+ r_close = re.compile(r'.*}[\t ]*$')
for root, dirs, files in os.walk(build_dir):
if 'build.gradle' in files:
path = os.path.join(root, 'build.gradle')
opened = 0
with open(path, "w") as o:
for line in lines:
- if 'signingConfigs ' in line:
+ if comment.match(line):
+ pass
+ elif signing_configs.match(line):
opened = 1
changed = True
elif opened > 0:
- if '{' in line:
+ if r_open.match(line):
opened += 1
- elif '}' in line:
- opened -=1
+ elif r_close.match(line):
+ opened -= 1
elif any(s in line for s in (
' signingConfig ',
'android.signingConfigs.',