config = None
options = None
+env = None
def get_default_config():
return {
- 'sdk_path': os.getenv("ANDROID_HOME"),
- 'ndk_path': os.getenv("ANDROID_NDK"),
- 'build_tools': "19.1.0",
+ 'sdk_path': os.getenv("ANDROID_HOME") or "",
+ 'ndk_path': os.getenv("ANDROID_NDK") or "",
+ 'build_tools': "20.0.0",
'ant': "ant",
'mvn3': "mvn",
'gradle': 'gradle',
'repo_url': "https://MyFirstFDroidRepo.org/fdroid/repo",
'repo_name': "My First FDroid Repo Demo",
'repo_icon': "fdroid-icon.png",
- 'repo_description':
+ 'repo_description': (
"This is a repository of apps to be used with FDroid. Applications in this "
- "repository are either official binaries built by the original application "
- "developers, or are binaries built from source by the admin of f-droid.org "
- "using the tools on https://gitlab.com/u/fdroid.",
- 'archive_older': 0,
+ + "repository are either official binaries built by the original application "
+ + "developers, or are binaries built from source by the admin of f-droid.org "
+ + "using the tools on https://gitlab.com/u/fdroid."),
+ 'archive_older': 0,
}
The config is read from config_file, which is in the current directory when
any of the repo management commands are used.
"""
- global config, options
+ global config, options, env
if config is not None:
return config
if not test_sdk_exists(config):
sys.exit(3)
+ if not test_build_tools_exists(config):
+ sys.exit(3)
+
+ bin_paths = {
+ 'aapt': [
+ os.path.join(config['sdk_path'], 'build-tools', config['build_tools'], 'aapt'),
+ ],
+ 'zipalign': [
+ os.path.join(config['sdk_path'], 'tools', 'zipalign'),
+ os.path.join(config['sdk_path'], 'build-tools', config['build_tools'], 'zipalign'),
+ ],
+ 'android': [
+ os.path.join(config['sdk_path'], 'tools', 'android'),
+ ],
+ 'adb': [
+ os.path.join(config['sdk_path'], 'platform-tools', 'adb'),
+ ],
+ }
+
+ for b, paths in bin_paths.items():
+ config[b] = None
+ for path in paths:
+ if os.path.isfile(path):
+ config[b] = path
+ break
+ if config[b] is None:
+ logging.warn("Could not find %s in any of the following paths:\n%s" % (
+ b, '\n'.join(paths)))
+
+ # There is no standard, so just set up the most common environment
+ # variables
+ env = os.environ
+ for n in ['ANDROID_HOME', 'ANDROID_SDK']:
+ env[n] = config['sdk_path']
+ for n in ['ANDROID_NDK', 'NDK']:
+ env[n] = config['ndk_path']
+
for k in ["keystorepass", "keypass"]:
if k in config:
write_password_file(k)
def test_sdk_exists(c):
if c['sdk_path'] is None:
# c['sdk_path'] is set to the value of ANDROID_HOME by default
- logging.critical('No Android SDK found! ANDROID_HOME is not set and sdk_path is not in config.py!')
- logging.info('You can use ANDROID_HOME to set the path to your SDK, i.e.:')
- logging.info('\texport ANDROID_HOME=/opt/android-sdk')
+ logging.error('No Android SDK found! ANDROID_HOME is not set and sdk_path is not in config.py!')
+ logging.error('You can use ANDROID_HOME to set the path to your SDK, i.e.:')
+ logging.error('\texport ANDROID_HOME=/opt/android-sdk')
return False
if not os.path.exists(c['sdk_path']):
logging.critical('Android SDK path "' + c['sdk_path'] + '" does not exist!')
if not os.path.isdir(c['sdk_path']):
logging.critical('Android SDK path "' + c['sdk_path'] + '" is not a directory!')
return False
- if not os.path.isdir(os.path.join(c['sdk_path'], 'build-tools')):
- logging.critical('Android SDK path "' + c['sdk_path'] + '" does not contain "build-tools/"!')
- return False
- if not os.path.isdir(os.path.join(c['sdk_path'], 'build-tools', c['build_tools'])):
- logging.critical('Configured build-tools version "' + c['build_tools'] + '" not found in the SDK!')
- return False
+ for d in ['build-tools', 'platform-tools', 'tools']:
+ if not os.path.isdir(os.path.join(c['sdk_path'], d)):
+ logging.critical('Android SDK path "%s" does not contain "%s/"!' % (
+ c['sdk_path'], d))
+ return False
return True
logging.critical('Android Build Tools path "'
+ versioned_build_tools + '" does not exist!')
return False
- if not os.path.exists(os.path.join(c['sdk_path'], 'build-tools', c['build_tools'], 'aapt')):
- logging.critical('Android Build Tools "'
- + versioned_build_tools
- + '" does not contain "aapt"!')
- return False
return True
for p in vercodes:
if p not in allids:
logging.critical("No such package: %s" % p)
- raise Exception("Found invalid app ids in arguments")
+ raise FDroidException("Found invalid app ids in arguments")
if not apps:
- raise Exception("No packages specified")
+ raise FDroidException("No packages specified")
error = False
for app in apps:
logging.critical("No such vercode %s for app %s" % (v, app['id']))
if error:
- raise Exception("Found invalid vercodes for some apps")
+ raise FDroidException("Found invalid vercodes for some apps")
return apps
try:
result = (m.group(1), m.group(2))
except AttributeError:
- raise Exception("Invalid apk name: %s" % filename)
+ raise FDroidException("Invalid apk name: %s" % filename)
return result
self.remote = remote
self.local = local
+ self.clone_failed = False
self.refreshed = False
self.srclib = None
# the repo - otherwise it must specify a valid revision.
def gotorevision(self, rev):
+ if self.clone_failed:
+ raise VCSException("Downloading the repository already failed once, not trying again.")
+
# The .fdroidvcs-id file for a repo tells us what VCS type
# and remote that directory was created from, allowing us to drop it
# automatically if either of those things changes.
else:
deleterepo = True
logging.info(
- "Repository details for {0} changed - deleting"
- .format(self.local))
+ "Repository details for %s changed - deleting" % (
+ self.local))
else:
deleterepo = True
- logging.info("Repository details missing - deleting")
+ logging.info("Repository details for %s missing - deleting" % (
+ self.local))
if deleterepo:
shutil.rmtree(self.local)
- self.gotorevisionx(rev)
+ exc = None
+
+ try:
+ self.gotorevisionx(rev)
+ except FDroidException, e:
+ exc = e
# If necessary, write the .fdroidvcs file.
- if writeback:
+ if writeback and not self.clone_failed:
with open(fdpath, 'w') as f:
f.write(cdata)
+ if exc is not None:
+ raise exc
+
# Derived classes need to implement this. It's called once basic checking
# has been performend.
def gotorevisionx(self, rev):
# a safety check.
def checkrepo(self):
p = SilentPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local)
- result = p.stdout.rstrip()
+ result = p.output.rstrip()
if not result.endswith(self.local):
raise VCSException('Repository mismatch')
# Brand new checkout
p = FDroidPopen(['git', 'clone', self.remote, self.local])
if p.returncode != 0:
- raise VCSException("Git clone failed")
+ self.clone_failed = True
+ raise VCSException("Git clone failed", p.output)
self.checkrepo()
else:
self.checkrepo()
# Discard any working tree changes
p = SilentPopen(['git', 'reset', '--hard'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git reset failed")
+ raise VCSException("Git reset failed", p.output)
# Remove untracked files now, in case they're tracked in the target
# revision (it happens!)
p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git clean failed")
+ raise VCSException("Git clean failed", p.output)
if not self.refreshed:
# Get latest commits and tags from remote
p = FDroidPopen(['git', 'fetch', 'origin'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git fetch failed")
+ raise VCSException("Git fetch failed", p.output)
p = SilentPopen(['git', 'fetch', '--prune', '--tags', 'origin'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git fetch failed")
+ raise VCSException("Git fetch failed", p.output)
# Recreate origin/HEAD as git clone would do it, in case it disappeared
p = SilentPopen(['git', 'remote', 'set-head', 'origin', '--auto'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git remote set-head failed")
+ lines = p.output.splitlines()
+ if 'Multiple remote HEAD branches' not in lines[0]:
+ raise VCSException("Git remote set-head failed", p.output)
+ branch = lines[1].split(' ')[-1]
+ p2 = SilentPopen(['git', 'remote', 'set-head', 'origin', branch], cwd=self.local)
+ if p2.returncode != 0:
+ raise VCSException("Git remote set-head failed", p.output + '\n' + p2.output)
self.refreshed = True
# origin/HEAD is the HEAD of the remote, e.g. the "default branch" on
# a github repo. Most of the time this is the same as origin/master.
- rev = str(rev if rev else 'origin/HEAD')
+ rev = rev or 'origin/HEAD'
p = SilentPopen(['git', 'checkout', '-f', rev], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git checkout of '%s' failed" % rev)
+ raise VCSException("Git checkout of '%s' failed" % rev, p.output)
# Get rid of any uncontrolled files left behind
p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git clean failed")
+ raise VCSException("Git clean failed", p.output)
def initsubmodules(self):
self.checkrepo()
]:
p = SilentPopen(['git', 'submodule', 'foreach', '--recursive'] + cmd, cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git submodule reset failed")
+ raise VCSException("Git submodule reset failed", p.output)
p = FDroidPopen(['git', 'submodule', 'sync'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git submodule sync failed")
+ raise VCSException("Git submodule sync failed", p.output)
p = FDroidPopen(['git', 'submodule', 'update', '--init', '--force', '--recursive'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git submodule update failed")
+ raise VCSException("Git submodule update failed", p.output)
def gettags(self):
self.checkrepo()
p = SilentPopen(['git', 'tag'], cwd=self.local)
- return p.stdout.splitlines()
+ return p.output.splitlines()
def latesttags(self, alltags, number):
self.checkrepo()
+ 'xargs -I@ git log --format=format:"%at @%n" -1 @ | '
+ 'sort -n | awk \'{print $2}\''],
cwd=self.local, shell=True)
- return p.stdout.splitlines()[-number:]
+ return p.output.splitlines()[-number:]
class vcs_gitsvn(vcs):
# a safety check.
def checkrepo(self):
p = SilentPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local)
- result = p.stdout.rstrip()
+ result = p.output.rstrip()
if not result.endswith(self.local):
raise VCSException('Repository mismatch')
gitsvn_cmd += ' -b %s' % i[9:]
p = SilentPopen([gitsvn_cmd + " %s %s" % (remote_split[0], self.local)], shell=True)
if p.returncode != 0:
- raise VCSException("Git clone failed")
+ self.clone_failed = True
+ raise VCSException("Git clone failed", p.output)
else:
p = SilentPopen([gitsvn_cmd + " %s %s" % (self.remote, self.local)], shell=True)
if p.returncode != 0:
- raise VCSException("Git clone failed")
+ self.clone_failed = True
+ raise VCSException("Git clone failed", p.output)
self.checkrepo()
else:
self.checkrepo()
# Discard any working tree changes
p = SilentPopen(['git', 'reset', '--hard'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git reset failed")
+ raise VCSException("Git reset failed", p.output)
# Remove untracked files now, in case they're tracked in the target
# revision (it happens!)
p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git clean failed")
+ raise VCSException("Git clean failed", p.output)
if not self.refreshed:
# Get new commits, branches and tags from repo
p = SilentPopen(['%sgit svn fetch %s' % self.userargs()], cwd=self.local, shell=True)
raise VCSException("Git svn fetch failed")
p = SilentPopen(['%sgit svn rebase %s' % self.userargs()], cwd=self.local, shell=True)
if p.returncode != 0:
- raise VCSException("Git svn rebase failed")
+ raise VCSException("Git svn rebase failed", p.output)
self.refreshed = True
- rev = str(rev if rev else 'master')
+ rev = rev or 'master'
if rev:
nospaces_rev = rev.replace(' ', '%20')
# Try finding a svn tag
- p = SilentPopen(['git', 'checkout', 'tags/' + nospaces_rev], cwd=self.local)
+ for treeish in ['origin/', '']:
+ p = SilentPopen(['git', 'checkout', treeish + 'tags/' + nospaces_rev],
+ cwd=self.local)
+ if p.returncode == 0:
+ break
if p.returncode != 0:
# No tag found, normal svn rev translation
# Translate svn rev into git format
rev_split = rev.split('/')
- if len(rev_split) > 1:
- treeish = 'origin/' + rev_split[0]
- svn_rev = rev_split[1]
- else:
- # if no branch is specified, then assume trunk (ie. 'master'
- # branch):
- treeish = 'origin/master'
- svn_rev = rev
+ p = None
+ for treeish in ['origin/', '']:
+ if len(rev_split) > 1:
+ treeish += rev_split[0]
+ svn_rev = rev_split[1]
+
+ else:
+ # if no branch is specified, then assume trunk (i.e. 'master' branch):
+ treeish += 'master'
+ svn_rev = rev
+
+ svn_rev = svn_rev if svn_rev[0] == 'r' else 'r' + svn_rev
+
+ p = SilentPopen(['git', 'svn', 'find-rev', svn_rev, treeish],
+ cwd=self.local)
+ git_rev = p.output.rstrip()
- p = SilentPopen(['git', 'svn', 'find-rev', 'r' + svn_rev, treeish], cwd=self.local)
- git_rev = p.stdout.rstrip()
+ if p.returncode == 0 and git_rev:
+ break
if p.returncode != 0 or not git_rev:
# Try a plain git checkout as a last resort
p = SilentPopen(['git', 'checkout', rev], cwd=self.local)
if p.returncode != 0:
- raise VCSException("No git treeish found and direct git checkout of '%s' failed" % rev)
+ raise VCSException("No git treeish found and direct git checkout of '%s' failed" % rev, p.output)
else:
# Check out the git rev equivalent to the svn rev
p = SilentPopen(['git', 'checkout', git_rev], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git svn checkout of '%s' failed" % rev)
+ raise VCSException("Git svn checkout of '%s' failed" % rev, p.output)
# Get rid of any uncontrolled files left behind
p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Git clean failed")
+ raise VCSException("Git clean failed", p.output)
def gettags(self):
self.checkrepo()
p = SilentPopen(['git', 'svn', 'find-rev', 'HEAD'], cwd=self.local)
if p.returncode != 0:
return None
- return p.stdout.strip()
+ return p.output.strip()
class vcs_svn(vcs):
if not os.path.exists(self.local):
p = SilentPopen(['svn', 'checkout', self.remote, self.local] + self.userargs())
if p.returncode != 0:
- raise VCSException("Svn checkout of '%s' failed" % rev)
+ self.clone_failed = True
+ raise VCSException("Svn checkout of '%s' failed" % rev, p.output)
else:
for svncommand in (
'svn revert -R .',
r"svn status | awk '/\?/ {print $2}' | xargs rm -rf"):
p = SilentPopen([svncommand], cwd=self.local, shell=True)
if p.returncode != 0:
- raise VCSException("Svn reset ({0}) failed in {1}".format(svncommand, self.local))
+ raise VCSException("Svn reset ({0}) failed in {1}".format(svncommand, self.local), p.output)
if not self.refreshed:
p = SilentPopen(['svn', 'update'] + self.userargs(), cwd=self.local)
if p.returncode != 0:
- raise VCSException("Svn update failed")
+ raise VCSException("Svn update failed", p.output)
self.refreshed = True
revargs = list(['-r', rev] if rev else [])
p = SilentPopen(['svn', 'update', '--force'] + revargs + self.userargs(), cwd=self.local)
if p.returncode != 0:
- raise VCSException("Svn update failed")
+ raise VCSException("Svn update failed", p.output)
def getref(self):
p = SilentPopen(['svn', 'info'], cwd=self.local)
- for line in p.stdout.splitlines():
+ for line in p.output.splitlines():
if line and line.startswith('Last Changed Rev: '):
return line[18:]
return None
if not os.path.exists(self.local):
p = SilentPopen(['hg', 'clone', self.remote, self.local])
if p.returncode != 0:
- raise VCSException("Hg clone failed")
+ self.clone_failed = True
+ raise VCSException("Hg clone failed", p.output)
else:
p = SilentPopen(['hg status -uS | xargs rm -rf'], cwd=self.local, shell=True)
if p.returncode != 0:
- raise VCSException("Hg clean failed")
+ raise VCSException("Hg clean failed", p.output)
if not self.refreshed:
p = SilentPopen(['hg', 'pull'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Hg pull failed")
+ raise VCSException("Hg pull failed", p.output)
self.refreshed = True
- rev = str(rev if rev else 'default')
+ rev = rev or 'default'
if not rev:
return
p = SilentPopen(['hg', 'update', '-C', rev], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Hg checkout of '%s' failed" % rev)
+ raise VCSException("Hg checkout of '%s' failed" % rev, p.output)
p = SilentPopen(['hg', 'purge', '--all'], cwd=self.local)
# Also delete untracked files, we have to enable purge extension for that:
- if "'purge' is provided by the following extension" in p.stdout:
+ if "'purge' is provided by the following extension" in p.output:
with open(self.local + "/.hg/hgrc", "a") as myfile:
myfile.write("\n[extensions]\nhgext.purge=\n")
p = SilentPopen(['hg', 'purge', '--all'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("HG purge failed")
+ raise VCSException("HG purge failed", p.output)
elif p.returncode != 0:
- raise VCSException("HG purge failed")
+ raise VCSException("HG purge failed", p.output)
def gettags(self):
p = SilentPopen(['hg', 'tags', '-q'], cwd=self.local)
- return p.stdout.splitlines()[1:]
+ return p.output.splitlines()[1:]
class vcs_bzr(vcs):
if not os.path.exists(self.local):
p = SilentPopen(['bzr', 'branch', self.remote, self.local])
if p.returncode != 0:
- raise VCSException("Bzr branch failed")
+ self.clone_failed = True
+ raise VCSException("Bzr branch failed", p.output)
else:
p = SilentPopen(['bzr', 'clean-tree', '--force', '--unknown', '--ignored'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Bzr revert failed")
+ raise VCSException("Bzr revert failed", p.output)
if not self.refreshed:
p = SilentPopen(['bzr', 'pull'], cwd=self.local)
if p.returncode != 0:
- raise VCSException("Bzr update failed")
+ raise VCSException("Bzr update failed", p.output)
self.refreshed = True
revargs = list(['-r', rev] if rev else [])
p = SilentPopen(['bzr', 'revert'] + revargs, cwd=self.local)
if p.returncode != 0:
- raise VCSException("Bzr revert of '%s' failed" % rev)
+ raise VCSException("Bzr revert of '%s' failed" % rev, p.output)
def gettags(self):
p = SilentPopen(['bzr', 'tags'], cwd=self.local)
return [tag.split(' ')[0].strip() for tag in
- p.stdout.splitlines()]
+ p.output.splitlines()]
def retrieve_string(app_dir, string, xmlfiles=None):
relpath = os.path.join(root_dir, path)
if not os.path.isdir(relpath):
continue
- logging.info("Found subproject at %s" % path)
+ logging.debug("Found subproject at %s" % path)
libraries.append(path)
return libraries
def remove_debuggable_flags(root_dir):
# Remove forced debuggable flags
- logging.info("Removing debuggable flags")
+ logging.debug("Removing debuggable flags from %s" % root_dir)
for root, dirs, files in os.walk(root_dir):
if 'AndroidManifest.xml' in files:
path = os.path.join(root, 'AndroidManifest.xml')
return (max_version, max_vercode, max_package)
-class BuildException(Exception):
+class FDroidException(Exception):
def __init__(self, value, detail=None):
self.value = value
self.detail = detail
return ret
-class VCSException(Exception):
- def __init__(self, value):
- self.value = value
+class VCSException(FDroidException):
+ pass
- def __str__(self):
- return self.value
+
+class BuildException(FDroidException):
+ pass
# Get the specified source library.
name, subdir = name.split('/', 1)
if name not in metadata.srclibs:
- raise BuildException('srclib ' + name + ' not found.')
+ raise VCSException('srclib ' + name + ' not found.')
srclib = metadata.srclibs[name]
s_tuple = t
break
if s_tuple is None:
- raise BuildException('Missing recursive srclib %s for %s' % (
+ raise VCSException('Missing recursive srclib %s for %s' % (
lib, name))
place_srclib(libdir, n, s_tuple[2])
n += 1
p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=libdir)
if p.returncode != 0:
raise BuildException("Error running prepare command for srclib %s"
- % name, p.stdout)
+ % name, p.output)
if basepath:
libdir = sdir
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)
+ (app['id'], build['version']), p.output)
# Apply patches if any
if build['patch']:
's@compileSdkVersion *[0-9]*@compileSdkVersion ' + n + '@g',
'build.gradle'],
cwd=root_dir)
- if '@' in build['gradle']:
- gradle_dir = os.path.join(root_dir, build['gradle'].split('@', 1)[1])
- gradle_dir = os.path.normpath(gradle_dir)
- FDroidPopen(['sed', '-i',
- 's@compileSdkVersion *[0-9]*@compileSdkVersion ' + n + '@g',
- 'build.gradle'],
- cwd=gradle_dir)
# Remove forced debuggable flags
remove_debuggable_flags(root_dir)
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)
+ (app['id'], build['version']), p.output)
# Generate (or update) the ant build file, build.xml...
if build['update'] and build['update'] != ['no'] and build['type'] == 'ant':
- parms = [os.path.join(config['sdk_path'], 'tools', 'android'), 'update']
- lparms = parms + ['lib-project']
- parms = parms + ['project']
+ parms = [config['android'], 'update', 'lib-project']
+ lparms = [config['android'], 'update', 'project']
if build['target']:
parms += ['-t', build['target']]
for d in update_dirs:
subdir = os.path.join(root_dir, d)
if d == '.':
- print("Updating main project")
+ logging.debug("Updating main project")
cmd = parms + ['-p', d]
else:
- print("Updating subproject %s" % d)
+ logging.debug("Updating subproject %s" % d)
cmd = lparms + ['-p', d]
p = FDroidPopen(cmd, cwd=root_dir)
# 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.stdout.startswith("Error: "):
- raise BuildException("Failed to update project at %s" % d, p.stdout)
+ if p.returncode != 0 or p.output.startswith("Error: "):
+ raise BuildException("Failed to update project at %s" % d, p.output)
# Clean update dirs via ant
if d != '.':
logging.info("Cleaning subproject %s" % d)
if p.returncode != 0:
logging.critical("Failed to get apk manifest information")
sys.exit(1)
- for line in p.stdout.splitlines():
+ for line in p.output.splitlines():
if 'android:debuggable' in line and not line.endswith('0x0'):
return True
return False
class PopenResult:
returncode = None
- stdout = ''
+ output = ''
def SilentPopen(commands, cwd=None, shell=False):
:returns: A PopenResult.
"""
+ global env
+
if cwd:
cwd = os.path.normpath(cwd)
logging.debug("Directory: %s" % cwd)
logging.debug("> %s" % ' '.join(commands))
result = PopenResult()
- p = subprocess.Popen(commands, cwd=cwd, shell=shell,
+ p = subprocess.Popen(commands, cwd=cwd, shell=shell, env=env,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout_queue = Queue.Queue()
while not stdout_reader.eof():
while not stdout_queue.empty():
line = stdout_queue.get()
- if output:
+ if output or options.verbose:
# Output directly to console
- sys.stdout.write(line)
- sys.stdout.flush()
- result.stdout += line
+ sys.stderr.write(line)
+ sys.stderr.flush()
+ result.output += line
time.sleep(0.1)