blob: 58f136dbe1e285d95c012693a8dda89c1c82149d [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
import hashlib
import imp
import os
import subprocess
import sys
DART_DIR = os.path.abspath(
os.path.normpath(os.path.join(__file__, '..', '..', '..')))
def GetUtils():
'''Dynamically load the tools/utils.py python module.'''
return imp.load_source('utils', os.path.join(DART_DIR, 'tools', 'utils.py'))
SYSTEM_RENAMES = {
'win32': 'windows',
'windows': 'windows',
'win': 'windows',
'linux': 'linux',
'linux2': 'linux',
'lucid32': 'linux',
'lucid64': 'linux',
'darwin': 'macos',
'mac': 'macos',
'macos': 'macos',
}
ARCH_RENAMES = {
'ia32': 'ia32',
'x64': 'x64',
'arm': 'arm',
'arm64': 'arm64',
}
class Channel(object):
BETA = 'beta'
BLEEDING_EDGE = 'be'
DEV = 'dev'
STABLE = 'stable'
TRY = 'try'
INTEGRATION = 'integration'
ALL_CHANNELS = [BETA, BLEEDING_EDGE, DEV, STABLE, TRY, INTEGRATION]
class ReleaseType(object):
RAW = 'raw'
SIGNED = 'signed'
RELEASE = 'release'
ALL_TYPES = [RAW, SIGNED, RELEASE]
class Mode(object):
RELEASE = 'release'
DEBUG = 'debug'
ALL_MODES = [RELEASE, DEBUG]
class GCSNamer(object):
"""
This class is used for naming objects in our "gs://dart-archive/"
GoogleCloudStorage bucket. It's structure is as follows:
For every (channel,revision,release-type) tuple we have a base path:
gs://dart-archive/channels/{be,beta,dev,stable,try,integration}
/{raw,signed,release}/{revision,latest}/
Under every base path, the following structure is used:
- /VERSION
- /api-docs/dartdocs-gen-api.zip
- /sdk/dartsdk-{linux,macos,windows}-{ia32,x64}-release.zip
"""
def __init__(self,
channel=Channel.BLEEDING_EDGE,
release_type=ReleaseType.RAW,
internal=False):
assert channel in Channel.ALL_CHANNELS
assert release_type in ReleaseType.ALL_TYPES
self.channel = channel
self.release_type = release_type
if internal:
self.bucket = 'gs://dart-archive-internal'
else:
self.bucket = 'gs://dart-archive'
# Functions for quering complete gs:// filepaths
def version_filepath(self, revision):
return '%s/channels/%s/%s/%s/VERSION' % (self.bucket, self.channel,
self.release_type, revision)
def sdk_zipfilepath(self, revision, system, arch, mode):
return '/'.join([
self.sdk_directory(revision),
self.sdk_zipfilename(system, arch, mode)
])
def unstripped_filepath(self, revision, system, arch):
return '/'.join([
self._variant_directory('unstripped', revision), system, arch,
self.unstripped_filename(system)
])
def apidocs_zipfilepath(self, revision):
return '/'.join(
[self.apidocs_directory(revision),
self.dartdocs_zipfilename()])
# Functions for querying gs:// directories
def base_directory(self, revision):
return '%s/channels/%s/%s/%s' % (self.bucket, self.channel,
self.release_type, revision)
def sdk_directory(self, revision):
return self._variant_directory('sdk', revision)
def linux_packages_directory(self, revision):
return '/'.join([self._variant_directory('linux_packages', revision)])
def src_directory(self, revision):
return self._variant_directory('src', revision)
def apidocs_directory(self, revision):
return self._variant_directory('api-docs', revision)
def misc_directory(self, revision):
return self._variant_directory('misc', revision)
def _variant_directory(self, name, revision):
return '%s/%s' % (self.base_directory(revision), name)
# Functions for quering filenames
def dartdocs_zipfilename(self):
return 'dartdocs-gen-api.zip'
def sdk_zipfilename(self, system, arch, mode):
assert mode in Mode.ALL_MODES
return 'dartsdk-%s-%s-%s.zip' % (SYSTEM_RENAMES[system],
ARCH_RENAMES[arch], mode)
def unstripped_filename(self, system):
return 'dart.exe' if system.startswith('win') else 'dart'
class GCSNamerApiDocs(object):
def __init__(self, channel=Channel.BLEEDING_EDGE):
assert channel in Channel.ALL_CHANNELS
self.channel = channel
self.bucket = 'gs://dartlang-api-docs'
def dartdocs_dirpath(self, revision):
assert len('%s' % revision) > 0
if self.channel == Channel.BLEEDING_EDGE:
return '%s/gen-dartdocs/builds/%s' % (self.bucket, revision)
return '%s/gen-dartdocs/%s/%s' % (self.bucket, self.channel, revision)
def docs_latestpath(self, revision):
assert len('%s' % revision) > 0
return '%s/channels/%s/latest.txt' % (self.bucket, self.channel)
def run(command, env=None, shell=False, throw_on_error=True):
print("Running command: ", command)
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
shell=shell,
universal_newlines=True)
(stdout, stderr) = p.communicate()
if throw_on_error and p.returncode != 0:
print("Failed to execute '%s'. Exit code: %s." %
(command, p.returncode),
file=sys.stderr)
print("stdout: ", stdout, file=sys.stderr)
print("stderr: ", stderr, file=sys.stderr)
raise Exception("Failed to execute %s." % command)
return (stdout, stderr, p.returncode)
class GSUtil(object):
GSUTIL_PATH = None
USE_DART_REPO_VERSION = False
def _layzCalculateGSUtilPath(self):
if not GSUtil.GSUTIL_PATH:
dart_gsutil = os.path.join(DART_DIR, 'third_party', 'gsutil',
'gsutil')
if os.path.isfile(dart_gsutil):
GSUtil.GSUTIL_PATH = dart_gsutil
elif GSUtil.USE_DART_REPO_VERSION:
raise Exception("Dart repository version of gsutil required, "
"but not found.")
else:
# We did not find gsutil, look in path
possible_locations = list(os.environ['PATH'].split(os.pathsep))
for directory in possible_locations:
location = os.path.join(directory, 'gsutil')
if os.path.isfile(location):
GSUtil.GSUTIL_PATH = location
break
assert GSUtil.GSUTIL_PATH
def execute(self, gsutil_args):
self._layzCalculateGSUtilPath()
gsutil_command = [sys.executable, GSUtil.GSUTIL_PATH]
return run(gsutil_command + gsutil_args)
def upload(self,
local_path,
remote_path,
recursive=False,
multithread=False):
assert remote_path.startswith('gs://')
if multithread:
args = ['-m', 'cp']
else:
args = ['cp']
if recursive:
args += ['-R']
args += [local_path, remote_path]
self.execute(args)
def cat(self, remote_path):
assert remote_path.startswith('gs://')
args = ['cat', remote_path]
(stdout, _, _) = self.execute(args)
return stdout
def setGroupReadACL(self, remote_path, group):
args = ['acl', 'ch', '-g', '%s:R' % group, remote_path]
self.execute(args)
def setContentType(self, remote_path, content_type):
args = ['setmeta', '-h', 'Content-Type:%s' % content_type, remote_path]
self.execute(args)
def remove(self, remote_path, recursive=False):
assert remote_path.startswith('gs://')
args = ['rm']
if recursive:
args += ['-R']
args += [remote_path]
self.execute(args)
def CalculateMD5Checksum(filename):
"""Calculate the MD5 checksum for filename."""
md5 = hashlib.md5()
with open(filename, 'rb') as f:
data = f.read(65536)
while len(data) > 0:
md5.update(data)
data = f.read(65536)
return md5.hexdigest()
def CalculateSha256Checksum(filename):
"""Calculate the sha256 checksum for filename."""
sha = hashlib.sha256()
with open(filename, 'rb') as f:
data = f.read(65536)
while len(data) > 0:
sha.update(data)
data = f.read(65536)
return sha.hexdigest()
def CreateMD5ChecksumFile(filename, mangled_filename=None):
"""Create and upload an MD5 checksum file for filename."""
if not mangled_filename:
mangled_filename = os.path.basename(filename)
checksum = CalculateMD5Checksum(filename)
checksum_filename = '%s.md5sum' % filename
with open(checksum_filename, 'w') as f:
f.write('%s *%s' % (checksum, mangled_filename))
print("MD5 checksum of %s is %s" % (filename, checksum))
return checksum_filename
def CreateSha256ChecksumFile(filename, mangled_filename=None):
"""Create and upload an sha256 checksum file for filename."""
if not mangled_filename:
mangled_filename = os.path.basename(filename)
checksum = CalculateSha256Checksum(filename)
checksum_filename = '%s.sha256sum' % filename
with open(checksum_filename, 'w') as f:
f.write('%s *%s' % (checksum, mangled_filename))
print("SHA256 checksum of %s is %s" % (filename, checksum))
return checksum_filename
def GetChannelFromName(name):
"""Get the channel from the name. Bleeding edge builders don't
have a suffix."""
channel_name = name.split('-').pop()
if channel_name in Channel.ALL_CHANNELS:
return channel_name
return Channel.BLEEDING_EDGE
def GetSystemFromName(name):
"""Get the system from the name."""
for part in name.split('-'):
if part in SYSTEM_RENAMES: return SYSTEM_RENAMES[part]
raise ValueError(
"Bot name '{}' does not have a system name in it.".format(name))