blob: dd49dc8d4e762ff2f1e4bdf0f124033975f75357 [file] [log] [blame]
# Copyright (c) 2014 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
from webports import configuration, binary_package, package, util, paths
from webports import pkg_info, error
DEFAULT_INDEX = os.path.join(paths.NACLPORTS_ROOT, 'lib', 'prebuilt.txt')
EXTRA_KEYS = package.EXTRA_KEYS + ['BIN_URL', 'BIN_SIZE', 'BIN_SHA1']
PREBUILT_ROOT = os.path.join(paths.PACKAGES_ROOT, 'prebuilt')
def extract_pkg_info(filename):
"""Return the pkg_info contents from a binary package."""
pkg = binary_package.BinaryPackage(filename)
return pkg.get_pkg_info()
def write_index(index_filename, binaries):
"""Create a package index file from set of binaries on disk.
Args:
index_filename: The name of the file to write create.
binaries: List of (filename, url) pairs containing packages to include.
Returns:
A PackageIndex object based on the contents of the newly written file.
"""
# Write index to a temporary file and then rename it, to avoid
# leaving a partial index file on disk.
tmp_name = index_filename + '.tmp'
with open(tmp_name, 'w') as output_file:
for i, (filename, url) in enumerate(binaries):
sha1 = util.hash_file(filename)
if i != 0:
output_file.write('\n')
output_file.write(extract_pkg_info(filename))
output_file.write('BIN_URL=%s\n' % url)
output_file.write('BIN_SIZE=%s\n' % os.path.getsize(filename))
output_file.write('BIN_SHA1=%s\n' % sha1)
os.rename(tmp_name, index_filename)
return index_from_file(index_filename)
def index_from_file(filename):
with open(filename) as f:
contents = f.read()
return PackageIndex(filename, contents)
def get_current_index():
if not os.path.exists(DEFAULT_INDEX):
return PackageIndex('', '')
return index_from_file(DEFAULT_INDEX)
class PackageIndex(object):
"""In memory representation of a package index file.
This class is used to read a package index of disk and stores
it in memory as dictionary keys on package name + configuration.
"""
valid_keys = pkg_info.VALID_KEYS + EXTRA_KEYS
required_keys = pkg_info.REQUIRED_KEYS + EXTRA_KEYS
def __init__(self, filename, index_data):
self.filename = filename
self.packages = {}
self.parse_index(index_data)
def contains(self, package_name, config):
"""Returns True if the index contains the given package in the given
configuration, False otherwise."""
return (package_name, config) in self.packages
def installable(self, package_name, config):
"""Returns True if the index contains the given package and it is
installable in the currently configured SDK."""
info = self.packages.get((package_name, config))
if not info:
return False
version = util.get_sdk_version()
if info['BUILD_SDK_VERSION'] != version:
util.log_verbose('Prebuilt package was built with different SDK version: '
'%s vs %s' % (info['BUILD_SDK_VERSION'], version))
return False
return True
def download(self, package_name, config):
if not os.path.exists(PREBUILT_ROOT):
util.makedirs(PREBUILT_ROOT)
info = self.packages[(package_name, config)]
filename = os.path.join(PREBUILT_ROOT, os.path.basename(info['BIN_URL']))
if os.path.exists(filename):
try:
util.verify_hash(filename, info['BIN_SHA1'])
return filename
except util.HashVerificationError:
pass
util.log('Downloading prebuilt binary ...')
util.download_file(filename, info['BIN_URL'])
util.verify_hash(filename, info['BIN_SHA1'])
return filename
def parse_index(self, index_data):
if not index_data:
return
for info_files in index_data.split('\n\n'):
info = pkg_info.parse_pkg_info(info_files, self.filename, self.valid_keys,
self.required_keys)
debug = info['BUILD_CONFIG'] == 'debug'
config = configuration.Configuration(info['BUILD_ARCH'],
info['BUILD_TOOLCHAIN'], debug)
key = (info['NAME'], config)
if key in self.packages:
error.Error('package index contains duplicate: %s' % str(key))
self.packages[key] = info