blob: 241ea26af4c89f33b7311bb15f0bab77dab444f0 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (C) 2012-2013, The CyanogenMod Project
#
# 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 os
import sys
import urllib2
import json
import re
import netrc, base64
from xml.etree import ElementTree
product = sys.argv[1];
if len(sys.argv) > 2:
depsonly = sys.argv[2]
else:
depsonly = None
try:
device = product[product.index("_") + 1:]
except:
device = product
if not depsonly:
print "Device %s not found. Attempting to retrieve device repository from CyanogenMod Github (http://github.com/CyanogenMod)." % device
repositories = []
try:
authtuple = netrc.netrc().authenticators("api.github.com")
if authtuple:
githubauth = base64.encodestring('%s:%s' % (authtuple[0], authtuple[2])).replace('\n', '')
else:
githubauth = None
except:
githubauth = None
def add_auth(githubreq):
if githubauth:
githubreq.add_header("Authorization","Basic %s" % githubauth)
if not depsonly:
githubreq = urllib.request.Request("https://api.github.com/search/repositories?q=%s+user:CyanogenMod+in:name+fork:true" % device)
add_auth(githubreq)
try:
result = json.loads(urllib.request.urlopen(githubreq).read().decode())
except urllib.error.URLError:
print("Failed to search GitHub")
sys.exit()
except ValueError:
print("Failed to parse return data from GitHub")
sys.exit()
for res in result.get('items', []):
repositories.append(res)
local_manifests = r'.repo/local_manifests'
if not os.path.exists(local_manifests): os.makedirs(local_manifests)
def exists_in_tree(lm, repository):
for child in lm.getchildren():
if child.attrib['name'].endswith(repository):
return True
return False
# in-place prettyprint formatter
def indent(elem, level=0):
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def get_default_revision():
m = ElementTree.parse(".repo/manifest.xml")
d = m.findall('default')[0]
r = d.get('revision')
return r.replace('refs/heads/', '').replace('refs/tags/', '')
def get_from_manifest(devicename):
try:
lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
lm = lm.getroot()
except:
lm = ElementTree.Element("manifest")
for localpath in lm.findall("project"):
if re.search("android_device_.*_%s$" % device, localpath.get("name")):
return localpath.get("path")
# Devices originally from AOSP are in the main manifest...
try:
mm = ElementTree.parse(".repo/manifest.xml")
mm = mm.getroot()
except:
mm = ElementTree.Element("manifest")
for localpath in mm.findall("project"):
if re.search("android_device_.*_%s$" % device, localpath.get("name")):
return localpath.get("path")
return None
def is_in_manifest(projectname):
try:
lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
lm = lm.getroot()
except:
lm = ElementTree.Element("manifest")
for localpath in lm.findall("project"):
if localpath.get("name") == projectname:
return 1
## Search in main manifest, too
try:
lm = ElementTree.parse(".repo/manifest.xml")
lm = lm.getroot()
except:
lm = ElementTree.Element("manifest")
for localpath in lm.findall("project"):
if localpath.get("name") == projectname:
return 1
return None
def add_to_manifest(repositories, fallback_branch = None):
try:
lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
lm = lm.getroot()
except:
lm = ElementTree.Element("manifest")
for repository in repositories:
repo_name = repository['repository']
repo_target = repository['target_path']
if exists_in_tree(lm, repo_name):
print 'CyanogenMod/%s already exists' % (repo_name)
continue
print 'Adding dependency: CyanogenMod/%s -> %s' % (repo_name, repo_target)
project = ElementTree.Element("project", attrib = { "path": repo_target,
"remote": "github", "name": "CyanogenMod/%s" % repo_name })
if 'branch' in repository:
project.set('revision',repository['branch'])
elif fallback_branch:
print "Using fallback branch %s for %s" % (fallback_branch, repo_name)
project.set('revision', fallback_branch)
else:
print "Using default branch for %s" % repo_name
lm.append(project)
indent(lm, 0)
raw_xml = ElementTree.tostring(lm)
raw_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + raw_xml
f = open('.repo/local_manifests/roomservice.xml', 'w')
f.write(raw_xml)
f.close()
def fetch_dependencies(repo_path, fallback_branch = None):
print 'Looking for dependencies'
dependencies_path = repo_path + '/cm.dependencies'
syncable_repos = []
if os.path.exists(dependencies_path):
dependencies_file = open(dependencies_path, 'r')
dependencies = json.loads(dependencies_file.read())
fetch_list = []
for dependency in dependencies:
if not is_in_manifest("CyanogenMod/%s" % dependency['repository']):
fetch_list.append(dependency)
syncable_repos.append(dependency['target_path'])
dependencies_file.close()
if len(fetch_list) > 0:
print 'Adding dependencies to manifest'
add_to_manifest(fetch_list, fallback_branch)
else:
print 'Dependencies file not found, bailing out.'
if len(syncable_repos) > 0:
print 'Syncing dependencies'
os.system('repo sync %s' % ' '.join(syncable_repos))
for deprepo in syncable_repos:
fetch_dependencies(deprepo)
def has_branch(branches, revision):
return revision in [branch['name'] for branch in branches]
if depsonly:
repo_path = get_from_manifest(device)
if repo_path:
fetch_dependencies(repo_path)
else:
print "Trying dependencies-only mode on a non-existing device tree?"
sys.exit()
else:
for repository in repositories:
repo_name = repository['name']
if repo_name.startswith("android_device_") and repo_name.endswith("_" + device):
print "Found repository: %s" % repository['name']
manufacturer = repo_name.replace("android_device_", "").replace("_" + device, "")
default_revision = get_default_revision()
print "Default revision: %s" % default_revision
print "Checking branch info"
githubreq = urllib2.Request(repository['branches_url'].replace('{/branch}', ''))
add_auth(githubreq)
result = json.loads(urllib2.urlopen(githubreq).read())
## Try tags, too, since that's what releases use
if not has_branch(result, default_revision):
githubreq = urllib2.Request(repository['tags_url'].replace('{/tag}', ''))
add_auth(githubreq)
result.extend (json.loads(urllib2.urlopen(githubreq).read()))
repo_path = "device/%s/%s" % (manufacturer, device)
adding = {'repository':repo_name,'target_path':repo_path}
fallback_branch = None
if not has_branch(result, default_revision):
if os.getenv('ROOMSERVICE_BRANCHES'):
fallbacks = filter(bool, os.getenv('ROOMSERVICE_BRANCHES').split(' '))
for fallback in fallbacks:
if has_branch(result, fallback):
print "Using fallback branch: %s" % fallback
fallback_branch = fallback
break
if not fallback_branch:
print "Default revision %s not found in %s. Bailing." % (default_revision, repo_name)
print "Branches found:"
for branch in [branch['name'] for branch in result]:
print branch
print "Use the ROOMSERVICE_BRANCHES environment variable to specify a list of fallback branches."
sys.exit()
add_to_manifest([adding], fallback_branch)
print "Syncing repository to retrieve project."
os.system('repo sync %s' % repo_path)
print "Repository synced!"
fetch_dependencies(repo_path, fallback_branch)
print "Done"
sys.exit()
print "Repository for %s not found in the CyanogenMod Github repository list. If this is in error, you may need to manually add it to your local_manifests/roomservice.xml." % device