diff options
Diffstat (limited to 'mesonbuild/msubprojects.py')
-rw-r--r-- | mesonbuild/msubprojects.py | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py new file mode 100644 index 0000000..6af8b41 --- /dev/null +++ b/mesonbuild/msubprojects.py @@ -0,0 +1,179 @@ +import os, subprocess + +from . import mlog +from .mesonlib import Popen_safe +from .wrap.wrap import API_ROOT, PackageDefinition +from .wrap import wraptool + +def update_wrapdb_file(wrap, repo_dir, options): + patch_url = wrap.get('patch_url') + branch, revision = wraptool.parse_patch_url(patch_url) + new_branch, new_revision = wraptool.get_latest_version(wrap.name) + if new_branch == branch and new_revision == revision: + mlog.log(' -> Up to date.') + return + wraptool.update_wrap_file(wrap.filename, wrap.name, new_branch, new_revision) + msg = [' -> New wrap file downloaded.'] + # Meson reconfigure won't use the new wrap file as long as the source + # directory exists. We don't delete it ourself to avoid data loss in case + # user has changes in their copy. + if os.path.isdir(repo_dir): + msg += ['To use it, delete', mlog.bold(repo_dir), 'and run', mlog.bold('meson --reconfigure')] + mlog.log(*msg) + +def update_file(wrap, repo_dir, options): + patch_url = wrap.values.get('patch_url', '') + if patch_url.startswith(API_ROOT): + update_wrapdb_file(wrap, repo_dir, options) + elif not os.path.isdir(repo_dir): + # The subproject is not needed, or it is a tarball extracted in + # 'libfoo-1.0' directory and the version has been bumped and the new + # directory is 'libfoo-2.0'. In that case forcing a meson + # reconfigure will download and use the new tarball. + mlog.log(' -> Subproject has not been checked out. Run', mlog.bold('meson --reconfigure'), 'to fetch it if needed.') + else: + # The subproject has not changed, or the new source and/or patch + # tarballs should be extracted in the same directory than previous + # version. + mlog.log(' -> Subproject has not changed, or the new source/patch needs to be extracted on the same location.\n' + + ' In that case, delete', mlog.bold(repo_dir), 'and run', mlog.bold('meson --reconfigure')) + +def git(cmd, workingdir): + return subprocess.check_output(['git', '-C', workingdir] + cmd, + stderr=subprocess.STDOUT).decode() + +def update_git(wrap, repo_dir, options): + if not os.path.isdir(repo_dir): + mlog.log(' -> Not used.') + return + revision = wrap.get('revision') + ret = git(['rev-parse', '--abbrev-ref', 'HEAD'], repo_dir).strip() + if ret == 'HEAD': + try: + # We are currently in detached mode, just checkout the new revision + git(['fetch'], repo_dir) + git(['checkout', revision], repo_dir) + except subprocess.CalledProcessError as e: + out = e.output.decode().strip() + mlog.log(' -> Could not checkout revision', mlog.cyan(revision)) + mlog.log(mlog.red(out)) + mlog.log(mlog.red(str(e))) + return + elif ret == revision: + try: + # We are in the same branch, pull latest commits + git(['-c', 'rebase.autoStash=true', 'pull', '--rebase'], repo_dir) + except subprocess.CalledProcessError as e: + out = e.output.decode().strip() + mlog.log(' -> Could not rebase', mlog.bold(repo_dir), 'please fix and try again.') + mlog.log(mlog.red(out)) + mlog.log(mlog.red(str(e))) + return + else: + # We are in another branch, probably user created their own branch and + # we should rebase it on top of wrap's branch. + if options.rebase: + try: + git(['fetch'], repo_dir) + git(['-c', 'rebase.autoStash=true', 'rebase', revision], repo_dir) + except subprocess.CalledProcessError as e: + out = e.output.decode().strip() + mlog.log(' -> Could not rebase', mlog.bold(repo_dir), 'please fix and try again.') + mlog.log(mlog.red(out)) + mlog.log(mlog.red(str(e))) + return + else: + mlog.log(' -> Target revision is', mlog.bold(revision), 'but currently in branch is', mlog.bold(ret), '\n' + + ' To rebase your branch on top of', mlog.bold(revision), 'use', mlog.bold('--rebase'), 'option.') + return + + git(['submodule', 'update'], repo_dir) + commit_message = git(['show', '--quiet', '--pretty=format:%h%n%d%n%s%n[%an]'], repo_dir) + parts = [s.strip() for s in commit_message.split('\n')] + mlog.log(' ->', mlog.yellow(parts[0]), mlog.red(parts[1]), parts[2], mlog.blue(parts[3])) + +def update_hg(wrap, repo_dir, options): + if not os.path.isdir(repo_dir): + mlog.log(' -> Not used.') + return + revno = wrap.get('revision') + if revno.lower() == 'tip': + # Failure to do pull is not a fatal error, + # because otherwise you can't develop without + # a working net connection. + subprocess.call(['hg', 'pull'], cwd=repo_dir) + else: + if subprocess.call(['hg', 'checkout', revno], cwd=repo_dir) != 0: + subprocess.check_call(['hg', 'pull'], cwd=repo_dir) + subprocess.check_call(['hg', 'checkout', revno], cwd=repo_dir) + +def update_svn(wrap, repo_dir, options): + if not os.path.isdir(repo_dir): + mlog.log(' -> Not used.') + return + revno = wrap.get('revision') + p, out = Popen_safe(['svn', 'info', '--show-item', 'revision', repo_dir]) + current_revno = out + if current_revno == revno: + return + if revno.lower() == 'head': + # Failure to do pull is not a fatal error, + # because otherwise you can't develop without + # a working net connection. + subprocess.call(['svn', 'update'], cwd=repo_dir) + else: + subprocess.check_call(['svn', 'update', '-r', revno], cwd=repo_dir) + +def update(options): + src_dir = os.path.relpath(os.path.realpath(options.sourcedir)) + if not os.path.isfile(os.path.join(src_dir, 'meson.build')): + mlog.error('Directory', mlog.bold(src_dir), 'does not seem to be a Meson source directory.') + return 1 + subprojects_dir = os.path.join(src_dir, 'subprojects') + if not os.path.isdir(subprojects_dir): + mlog.log('Directory', mlog.bold(src_dir), 'does not seem to have subprojects.') + return 0 + files = [] + for name in options.subprojects: + f = os.path.join(subprojects_dir, name + '.wrap') + if not os.path.isfile(f): + mlog.error('Subproject', mlog.bold(name), 'not found.') + return 1 + else: + files.append(f) + if not files: + for f in os.listdir(subprojects_dir): + if f.endswith('.wrap'): + files.append(os.path.join(subprojects_dir, f)) + for f in files: + wrap = PackageDefinition(f) + directory = wrap.values.get('directory', wrap.name) + dirname = os.path.join(subprojects_dir, directory) + mlog.log('Updating %s...' % wrap.name) + if wrap.type == 'file': + update_file(wrap, dirname, options) + elif wrap.type == 'git': + update_git(wrap, dirname, options) + elif wrap.type == 'hg': + update_hg(wrap, dirname, options) + elif wrap.type == 'svn': + update_svn(wrap, dirname, options) + else: + mlog.log(' -> Cannot update', wrap.type, 'subproject') + return 0 + +def add_arguments(parser): + subparsers = parser.add_subparsers(title='Commands', dest='command') + subparsers.required = True + + p = subparsers.add_parser('update', help='Update all subprojects from wrap files') + p.add_argument('--sourcedir', default='.', + help='Path to source directory') + p.add_argument('--rebase', default=False, action='store_true', + help='Rebase your branch on top of wrap\'s revision (git only)') + p.add_argument('subprojects', nargs='*', + help='List of subprojects (default: all)') + p.set_defaults(subprojects_func=update) + +def run(options): + return options.subprojects_func(options) |