aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/wrap
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/wrap')
-rw-r--r--mesonbuild/wrap/wrap.py212
-rwxr-xr-xmesonbuild/wrap/wraptool.py200
2 files changed, 412 insertions, 0 deletions
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
new file mode 100644
index 0000000..2818fa0
--- /dev/null
+++ b/mesonbuild/wrap/wrap.py
@@ -0,0 +1,212 @@
+# Copyright 2015 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .. import mlog
+import urllib.request, os, hashlib, shutil
+import subprocess
+import sys
+
+try:
+ import ssl
+ has_ssl = True
+ API_ROOT = 'https://wrapdb.mesonbuild.com/v1/'
+except ImportError:
+ has_ssl = False
+ API_ROOT = 'http://wrapdb.mesonbuild.com/v1/'
+
+def build_ssl_context():
+ ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ ctx.options |= ssl.OP_NO_SSLv2
+ ctx.options |= ssl.OP_NO_SSLv3
+ ctx.verify_mode = ssl.CERT_REQUIRED
+ ctx.load_default_certs()
+ return ctx
+
+def open_wrapdburl(urlstring):
+ global ssl_warning_printed
+ if has_ssl:
+ try:
+ return urllib.request.urlopen(urlstring)#, context=build_ssl_context())
+ except urllib.error.URLError:
+ if not ssl_warning_printed:
+ print('SSL connection failed. Falling back to unencrypted connections.')
+ ssl_warning_printed = True
+ if not ssl_warning_printed:
+ print('Warning: SSL not available, traffic not authenticated.',
+ file=sys.stderr)
+ ssl_warning_printed = True
+ # Trying to open SSL connection to wrapdb fails because the
+ # certificate is not known.
+ if urlstring.startswith('https'):
+ urlstring = 'http' + urlstring[5:]
+ return urllib.request.urlopen(urlstring)
+
+
+class PackageDefinition:
+ def __init__(self, fname):
+ self.values = {}
+ ifile = open(fname)
+ first = ifile.readline().strip()
+
+ if first == '[wrap-file]':
+ self.type = 'file'
+ elif first == '[wrap-git]':
+ self.type = 'git'
+ else:
+ raise RuntimeError('Invalid format of package file')
+ for line in ifile:
+ line = line.strip()
+ if line == '':
+ continue
+ (k, v) = line.split('=', 1)
+ k = k.strip()
+ v = v.strip()
+ self.values[k] = v
+
+ def get(self, key):
+ return self.values[key]
+
+ def has_patch(self):
+ return 'patch_url' in self.values
+
+class Resolver:
+ def __init__(self, subdir_root):
+ self.subdir_root = subdir_root
+ self.cachedir = os.path.join(self.subdir_root, 'packagecache')
+
+ def resolve(self, packagename):
+ fname = os.path.join(self.subdir_root, packagename + '.wrap')
+ dirname = os.path.join(self.subdir_root, packagename)
+ if not os.path.isfile(fname):
+ if os.path.isdir(dirname):
+ # No wrap file but dir exists -> user put it there manually.
+ return packagename
+ return None
+ p = PackageDefinition(fname)
+ if p.type == 'file':
+ if not os.path.isdir(self.cachedir):
+ os.mkdir(self.cachedir)
+ self.download(p, packagename)
+ self.extract_package(p)
+ elif p.type == 'git':
+ self.get_git(p)
+ else:
+ raise RuntimeError('Unreachable code.')
+ return p.get('directory')
+
+ def get_git(self, p):
+ checkoutdir = os.path.join(self.subdir_root, p.get('directory'))
+ revno = p.get('revision')
+ is_there = os.path.isdir(checkoutdir)
+ if is_there:
+ if revno.lower() == 'head':
+ subprocess.check_call(['git', 'pull'], cwd=checkoutdir)
+ else:
+ if subprocess.call(['git', 'checkout', revno], cwd=checkoutdir) != 0:
+ subprocess.check_call(['git', 'fetch'], cwd=checkoutdir)
+ subprocess.check_call(['git', 'checkout', revno],
+ cwd=checkoutdir)
+ else:
+ subprocess.check_call(['git', 'clone', p.get('url'),
+ p.get('directory')], cwd=self.subdir_root)
+ if revno.lower() != 'head':
+ subprocess.check_call(['git', 'checkout', revno],
+ cwd=checkoutdir)
+
+
+ def get_data(self, url):
+ blocksize = 10*1024
+ if url.startswith('https://wrapdb.mesonbuild.com'):
+ resp = open_wrapdburl(url)
+ else:
+ resp = urllib.request.urlopen(url)
+ dlsize = int(resp.info()['Content-Length'])
+ print('Download size:', dlsize)
+ print('Downloading: ', end='')
+ sys.stdout.flush()
+ printed_dots = 0
+ blocks = []
+ downloaded = 0
+ while True:
+ block = resp.read(blocksize)
+ if block == b'':
+ break
+ downloaded += len(block)
+ blocks.append(block)
+ ratio = int(downloaded/dlsize * 10)
+ while printed_dots < ratio:
+ print('.', end='')
+ sys.stdout.flush()
+ printed_dots += 1
+ print('')
+ resp.close()
+ return b''.join(blocks)
+
+ def get_hash(self, data):
+ h = hashlib.sha256()
+ h.update(data)
+ hashvalue = h.hexdigest()
+ return hashvalue
+
+ def download(self, p, packagename):
+ ofname = os.path.join(self.cachedir, p.get('source_filename'))
+ if os.path.exists(ofname):
+ mlog.log('Using', mlog.bold(packagename), 'from cache.')
+ return
+ srcurl = p.get('source_url')
+ mlog.log('Dowloading', mlog.bold(packagename), 'from', mlog.bold(srcurl))
+ srcdata = self.get_data(srcurl)
+ dhash = self.get_hash(srcdata)
+ expected = p.get('source_hash')
+ if dhash != expected:
+ raise RuntimeError('Incorrect hash for source %s:\n %s expected\n %s actual.' % (packagename, expected, dhash))
+ open(ofname, 'wb').write(srcdata)
+ if p.has_patch():
+ purl = p.get('patch_url')
+ mlog.log('Downloading patch from', mlog.bold(purl))
+ pdata = self.get_data(purl)
+ phash = self.get_hash(pdata)
+ expected = p.get('patch_hash')
+ if phash != expected:
+ raise RuntimeError('Incorrect hash for patch %s:\n %s expected\n %s actual' % (packagename, expected, phash))
+ open(os.path.join(self.cachedir, p.get('patch_filename')), 'wb').write(pdata)
+ else:
+ mlog.log('Package does not require patch.')
+
+ def extract_package(self, package):
+ if sys.version_info < (3, 5):
+ try:
+ import lzma
+ del lzma
+ try:
+ shutil.register_unpack_format('xztar', ['.tar.xz', '.txz'], shutil._unpack_tarfile, [], "xz'ed tar-file")
+ except shutil.RegistryError:
+ pass
+ except ImportError:
+ pass
+ target_dir = os.path.join(self.subdir_root, package.get('directory'))
+ if os.path.isdir(target_dir):
+ return
+ extract_dir = self.subdir_root
+ # Some upstreams ship packages that do not have a leading directory.
+ # Create one for them.
+ try:
+ package.get('lead_directory_missing')
+ os.mkdir(target_dir)
+ extract_dir = target_dir
+ except KeyError:
+ pass
+ shutil.unpack_archive(os.path.join(self.cachedir, package.get('source_filename')), extract_dir)
+ if package.has_patch():
+ shutil.unpack_archive(os.path.join(self.cachedir, package.get('patch_filename')), self.subdir_root)
diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py
new file mode 100755
index 0000000..d2f0a28
--- /dev/null
+++ b/mesonbuild/wrap/wraptool.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python3
+
+# Copyright 2015-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import sys, os
+import configparser
+import shutil
+
+from glob import glob
+
+from .wrap import API_ROOT, open_wrapdburl
+
+help_templ = '''This program allows you to manage your Wrap dependencies
+using the online wrap database http://wrapdb.mesonbuild.com.
+
+Run this command in your top level source directory.
+
+Usage:
+
+%s <command> [options]
+
+Commands:
+
+ list - show all available projects
+ search - search the db by name
+ install - install the specified project
+ update - update the project to its newest available release
+ info - show available versions of a project
+ status - show installed and available versions of your projects
+
+'''
+
+
+def print_help():
+ print(help_templ % sys.argv[0])
+
+def get_result(urlstring):
+ u = open_wrapdburl(urlstring)
+ data = u.read().decode('utf-8')
+ jd = json.loads(data)
+ if jd['output'] != 'ok':
+ print('Got bad output from server.')
+ print(data)
+ sys.exit(1)
+ return jd
+
+def get_projectlist():
+ jd = get_result(API_ROOT + 'projects')
+ projects = jd['projects']
+ return projects
+
+def list_projects():
+ projects = get_projectlist()
+ for p in projects:
+ print(p)
+
+def search(name):
+ jd = get_result(API_ROOT + 'query/byname/' + name)
+ for p in jd['projects']:
+ print(p)
+
+def get_latest_version(name):
+ jd = get_result(API_ROOT + 'query/get_latest/' + name)
+ branch = jd['branch']
+ revision = jd['revision']
+ return (branch, revision)
+
+def install(name):
+ if not os.path.isdir('subprojects'):
+ print('Subprojects dir not found. Run this script in your source root directory.')
+ sys.exit(1)
+ if os.path.isdir(os.path.join('subprojects', name)):
+ print('Subproject directory for this project already exists.')
+ sys.exit(1)
+ wrapfile = os.path.join('subprojects', name + '.wrap')
+ if os.path.exists(wrapfile):
+ print('Wrap file already exists.')
+ sys.exit(1)
+ (branch, revision) = get_latest_version(name)
+ u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%s/get_wrap' % (name, branch, revision))
+ data = u.read()
+ open(wrapfile, 'wb').write(data)
+ print('Installed', name, 'branch', branch, 'revision', revision)
+
+def get_current_version(wrapfile):
+ cp = configparser.ConfigParser()
+ cp.read(wrapfile)
+ cp = cp['wrap-file']
+ patch_url = cp['patch_url']
+ arr = patch_url.split('/')
+ branch = arr[-3]
+ revision = int(arr[-2])
+ return (branch, revision, cp['directory'], cp['source_filename'], cp['patch_filename'])
+
+def update(name):
+ if not os.path.isdir('subprojects'):
+ print('Subprojects dir not found. Run this command in your source root directory.')
+ sys.exit(1)
+ wrapfile = os.path.join('subprojects', name + '.wrap')
+ if not os.path.exists(wrapfile):
+ print('Project', name, 'is not in use.')
+ sys.exit(1)
+ (branch, revision, subdir, src_file, patch_file) = get_current_version(wrapfile)
+ (new_branch, new_revision) = get_latest_version(name)
+ if new_branch == branch and new_revision == revision:
+ print('Project', name, 'is already up to date.')
+ sys.exit(0)
+ u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%d/get_wrap' % (name, new_branch, new_revision))
+ data = u.read()
+ shutil.rmtree(os.path.join('subprojects', subdir), ignore_errors=True)
+ try:
+ os.unlink(os.path.join('subprojects/packagecache', src_file))
+ except FileNotFoundError:
+ pass
+ try:
+ os.unlink(os.path.join('subprojects/packagecache', patch_file))
+ except FileNotFoundError:
+ pass
+ open(wrapfile, 'wb').write(data)
+ print('Updated', name, 'to branch', new_branch, 'revision', new_revision)
+
+def info(name):
+ jd = get_result(API_ROOT + 'projects/' + name)
+ versions = jd['versions']
+ if len(versions) == 0:
+ print('No available versions of', name)
+ sys.exit(0)
+ print('Available versions of %s:' % name)
+ for v in versions:
+ print(' ', v['branch'], v['revision'])
+
+def status():
+ print('Subproject status')
+ for w in glob('subprojects/*.wrap'):
+ name = os.path.split(w)[1][:-5]
+ try:
+ (latest_branch, latest_revision) = get_latest_version(name)
+ except Exception:
+ print('', name, 'not available in wrapdb.')
+ continue
+ try:
+ (current_branch, current_revision, _, _, _) = get_current_version(w)
+ except Exception:
+ print('Wrap file not from wrapdb.')
+ continue
+ if current_branch == latest_branch and current_revision == latest_revision:
+ print('', name, 'up to date. Branch %s, revision %d.' % (current_branch, current_revision))
+ else:
+ print('', name, 'not up to date. Have %s %d, but %s %d is available.' % (current_branch, current_revision, latest_branch, latest_revision))
+
+def run(args):
+ if len(sys.argv) < 1 or sys.argv[0] == '-h' or sys.argv[1] == '--help':
+ print_help()
+ return 0
+ command = args[0]
+ args = args[1:]
+ if command == 'list':
+ list_projects()
+ elif command == 'search':
+ if len(args) != 1:
+ print('Search requires exactly one argument.')
+ return 1
+ search(args[0])
+ elif command == 'install':
+ if len(args) != 1:
+ print('Install requires exactly one argument.')
+ return 1
+ install(args[0])
+ elif command == 'update':
+ if len(args) != 1:
+ print('update requires exactly one argument.')
+ return 1
+ update(args[0])
+ elif command == 'info':
+ if len(args) != 1:
+ print('info requires exactly one argument.')
+ return 1
+ info(args[0])
+ elif command == 'status':
+ status()
+ else:
+ print('Unknown command', command)
+ return 1
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))