diff --git a/interpreter.py b/interpreter.py index 6c4f07ee6..33dbbefb9 100644 --- a/interpreter.py +++ b/interpreter.py @@ -19,6 +19,7 @@ import dependencies import mlog import build import optinterpreter +import wrap import os, sys, platform, subprocess, shutil, uuid class InterpreterException(coredata.MesonException): @@ -915,7 +916,12 @@ class Interpreter(): subdir = os.path.join('subprojects', dirname) abs_subdir = os.path.join(self.build.environment.get_source_dir(), subdir) if not os.path.isdir(abs_subdir): - raise InterpreterException('Subproject directory does not exist.') + r = wrap.Resolver(os.path.join(self.build.environment.get_source_dir(), 'subprojects')) + resolved = r.resolve(dirname) + if resolved is None: + raise InterpreterException('Subproject directory does not exist and can not be downloaded.') + subdir = os.path.join('subprojects', resolved) + abs_subdir = os.path.join(self.build.environment.get_source_dir(), 'subprojects', subdir) self.global_args_frozen = True mlog.log('\nExecuting subproject ', mlog.bold(dirname), '.\n', sep='') subi = Interpreter(self.build, dirname, subdir) diff --git a/manual tests/1 basic/main.c b/manual tests/1 basic/main.c new file mode 100644 index 000000000..39d3a9aad --- /dev/null +++ b/manual tests/1 basic/main.c @@ -0,0 +1,12 @@ +#include +#include + +int main(int argc, char **argv) { + sqlite3 *db; + if(sqlite3_open(":memory:", &db) != SQLITE_OK) { + printf("Sqlite failed.\n"); + return 1; + } + sqlite3_close(db); + return 0; +} diff --git a/manual tests/1 basic/meson.build b/manual tests/1 basic/meson.build new file mode 100644 index 000000000..afdda3404 --- /dev/null +++ b/manual tests/1 basic/meson.build @@ -0,0 +1,11 @@ +project('downloader', 'c') + +s = subproject('sqlite') + +e = executable('dtest', 'main.c', +include_directories : s.get_variable('sqinc'), +link_args : ['-pthread', '-ldl'], +c_args : '-pthread', +link_with : s.get_variable('sqlib')) + +test('dltest', e) diff --git a/manual tests/1 basic/subprojects/sqlite.wrap b/manual tests/1 basic/subprojects/sqlite.wrap new file mode 100644 index 000000000..879a170d6 --- /dev/null +++ b/manual tests/1 basic/subprojects/sqlite.wrap @@ -0,0 +1,10 @@ +[mesonwrap] +directory = sqlite-amalgamation-3080802 + +source_url = http://sqlite.com/2015/sqlite-amalgamation-3080802.zip +source_filename = sqlite-amalgamation-3080802.zip +source_hash = 5ebeea0dfb75d090ea0e7ff84799b2a7a1550db3fe61eb5f6f61c2e971e57663 + +patch_url = https://dl.dropboxusercontent.com/u/37517477/sqlite-meson.tar.gz +patch_filename = sqlite-meson.tar.gz +patch_hash = 8c9d00702d5fe4a6bf25a36b821a332f6b2dfd117c66fe818b88b23d604635e9 diff --git a/wrap.py b/wrap.py new file mode 100644 index 000000000..4402cba28 --- /dev/null +++ b/wrap.py @@ -0,0 +1,92 @@ +# 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. + +import mlog +import glob, urllib.request, os, hashlib, shutil + +class PackageDefinition: + def __init__(self, fname): + self.values = {} + ifile = open(fname) + first = ifile.readline().strip() + if first != '[mesonwrap]': + 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') + if not os.path.isfile(fname): + return None + p = PackageDefinition(fname) + self.download(p, packagename) + self.extract_package(p) + return p.get('directory') + + def get_data(self, url): + u = urllib.request.urlopen(url) + data = u.read() + u.close() + h = hashlib.sha256() + h.update(data) + hashvalue = h.hexdigest() + return (data, hashvalue) + + def download(self, p, packagename): + ofname = os.path.join(self.cachedir, p.get('source_filename')) + if os.path.exists(ofname): + print('Using', packagename, 'from cache.') + return + srcurl = p.get('source_url') + print('Dowloading', packagename, 'from', srcurl) + (srcdata, dhash) = self.get_data(srcurl) + expected = p.get('source_hash') + if dhash != expected: + raise RuntimeError('Incorrect hash for source %s:\n %s expected\n %s actual.' % (packagename, expected, dhash)) + if p.has_patch(): + purl = p.get('patch_url') + print('Downloading patch from', purl) + (pdata, phash) = self.get_data(purl) + 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: + print('Package does not require patch.') + open(ofname, 'wb').write(srcdata) + + def extract_package(self, package): + if os.path.isdir(os.path.join(self.subdir_root, package.get('directory'))): + return + print(os.path.join(self.cachedir, package.get('source_filename'))) + shutil.unpack_archive(os.path.join(self.cachedir, package.get('source_filename')), self.subdir_root) + if package.has_patch(): + shutil.unpack_archive(os.path.join(self.cachedir, package.get('patch_filename')), self.subdir_root)