blob: a033c4049d0be472847dd20bcafbad5bb70ad46a [file] [log] [blame]
# Copyright 2014 The Chromium 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 logging
import os
import sys
import subprocess
import tempfile
import urllib
import build_common
from util import file_util
class BaseGetAndUnpackArchiveFromURL(object):
"""Handles downloading and extracting a package from a URL."""
# Override these in a derived class
NAME = None
DEPS_FILE = None
FINAL_DIR = None
STAGE_DIR = None
DOWNLOAD_NAME = None
@classmethod
def _unpack_update(cls, download_file):
raise NotImplementedError('Please implement this in a derived class.')
@classmethod
def _gsretrieve(cls, url, download_file):
try:
cmd = [build_common.get_gsutil_executable(), 'cp', url, download_file]
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
logging.error('Cannot download ' + url + '. '
'Did you make sure to run prodaccess?')
sys.exit(1)
@classmethod
def _fetch_and_stage_update(cls, url):
"""Downloads an update file to a temp directory, and manages replacing the
final directory with the stage directory contents."""
result = True
try:
tmp_dir = tempfile.mkdtemp(suffix='.tmp', prefix=cls.DOWNLOAD_NAME)
try:
file_util.rmtree(cls.STAGE_DIR, ignore_errors=True)
os.mkdir(cls.STAGE_DIR)
download_file = os.path.join(tmp_dir, cls.DOWNLOAD_NAME)
if url.startswith('gs://'):
cls._gsretrieve(url, download_file)
else:
urllib.urlretrieve(url, download_file)
cls._unpack_update(download_file)
file_util.rmtree(cls.FINAL_DIR, ignore_errors=True)
os.rename(cls.STAGE_DIR, cls.FINAL_DIR)
finally:
file_util.rmtree(cls.STAGE_DIR, ignore_errors=True)
file_util.rmtree(tmp_dir, ignore_errors=True)
except Exception as e:
print e
result = False
return result
@classmethod
def post_update_work(cls):
"""Override in derived classes to perform additional work after downloading
and unpacking the download."""
return True
@classmethod
def check_and_perform_update(cls):
"""Checks the current and dependency stamps, and performs the update if
they are different."""
deps_file = file_util.read_metadata_file(cls.DEPS_FILE)
url = deps_file[0]
stamp_file = build_common.StampFile(
','.join(deps_file), os.path.join(cls.FINAL_DIR, 'URL'))
if stamp_file.is_up_to_date():
return True
print 'INFO: Updating %s...' % cls.NAME
if not cls._fetch_and_stage_update(url):
print 'Failed to update %s.' % cls.NAME
return False
stamp_file.update()
result = cls.post_update_work()
print 'INFO: Done'
return result