blob: 241ea26af4c89f33b7311bb15f0bab77dab444f0 [file] [log] [blame]
Koushik Dutta780fb5f2011-11-26 18:51:42 -08001#!/usr/bin/env python
Diogo Ferreira0d109172012-03-18 21:18:29 +00002# Copyright (C) 2012-2013, The CyanogenMod Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Koushik Dutta780fb5f2011-11-26 18:51:42 -080016import os
17import sys
18import urllib2
19import json
Diogo Ferreira0d109172012-03-18 21:18:29 +000020import re
21import netrc, base64
Koushik Dutta780fb5f2011-11-26 18:51:42 -080022from xml.etree import ElementTree
23
24product = sys.argv[1];
Diogo Ferreira0d109172012-03-18 21:18:29 +000025
26if len(sys.argv) > 2:
27 depsonly = sys.argv[2]
28else:
29 depsonly = None
30
31try:
32 device = product[product.index("_") + 1:]
33except:
34 device = product
35
36if not depsonly:
37 print "Device %s not found. Attempting to retrieve device repository from CyanogenMod Github (http://github.com/CyanogenMod)." % device
Koushik Dutta780fb5f2011-11-26 18:51:42 -080038
39repositories = []
40
Diogo Ferreira0d109172012-03-18 21:18:29 +000041try:
42 authtuple = netrc.netrc().authenticators("api.github.com")
43
44 if authtuple:
45 githubauth = base64.encodestring('%s:%s' % (authtuple[0], authtuple[2])).replace('\n', '')
46 else:
47 githubauth = None
48except:
49 githubauth = None
50
51def add_auth(githubreq):
52 if githubauth:
53 githubreq.add_header("Authorization","Basic %s" % githubauth)
54
Matt Moweree84e3f2014-10-31 21:02:37 -050055if not depsonly:
Matt Mower28cb6ba2015-01-02 00:08:48 -060056 githubreq = urllib.request.Request("https://api.github.com/search/repositories?q=%s+user:CyanogenMod+in:name+fork:true" % device)
Diogo Ferreira0d109172012-03-18 21:18:29 +000057 add_auth(githubreq)
Matt Moweree84e3f2014-10-31 21:02:37 -050058 try:
Anthony King365b1e22015-01-08 11:39:12 -060059 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
60 except urllib.error.URLError:
61 print("Failed to search GitHub")
Matt Moweree84e3f2014-10-31 21:02:37 -050062 sys.exit()
Anthony King365b1e22015-01-08 11:39:12 -060063 except ValueError:
64 print("Failed to parse return data from GitHub")
Matt Moweree84e3f2014-10-31 21:02:37 -050065 sys.exit()
Anthony King365b1e22015-01-08 11:39:12 -060066 for res in result.get('items', []):
Diogo Ferreira0d109172012-03-18 21:18:29 +000067 repositories.append(res)
Koushik Dutta780fb5f2011-11-26 18:51:42 -080068
Diogo Ferreira0d109172012-03-18 21:18:29 +000069local_manifests = r'.repo/local_manifests'
70if not os.path.exists(local_manifests): os.makedirs(local_manifests)
Koushik Dutta780fb5f2011-11-26 18:51:42 -080071
Diogo Ferreira0d109172012-03-18 21:18:29 +000072def exists_in_tree(lm, repository):
73 for child in lm.getchildren():
74 if child.attrib['name'].endswith(repository):
75 return True
76 return False
77
78# in-place prettyprint formatter
79def indent(elem, level=0):
80 i = "\n" + level*" "
81 if len(elem):
82 if not elem.text or not elem.text.strip():
83 elem.text = i + " "
84 if not elem.tail or not elem.tail.strip():
85 elem.tail = i
86 for elem in elem:
87 indent(elem, level+1)
88 if not elem.tail or not elem.tail.strip():
89 elem.tail = i
90 else:
91 if level and (not elem.tail or not elem.tail.strip()):
92 elem.tail = i
93
94def get_default_revision():
95 m = ElementTree.parse(".repo/manifest.xml")
96 d = m.findall('default')[0]
97 r = d.get('revision')
98 return r.replace('refs/heads/', '').replace('refs/tags/', '')
99
100def get_from_manifest(devicename):
101 try:
102 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
103 lm = lm.getroot()
104 except:
105 lm = ElementTree.Element("manifest")
106
107 for localpath in lm.findall("project"):
108 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
109 return localpath.get("path")
110
111 # Devices originally from AOSP are in the main manifest...
112 try:
113 mm = ElementTree.parse(".repo/manifest.xml")
114 mm = mm.getroot()
115 except:
116 mm = ElementTree.Element("manifest")
117
118 for localpath in mm.findall("project"):
119 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
120 return localpath.get("path")
121
122 return None
123
124def is_in_manifest(projectname):
125 try:
126 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
127 lm = lm.getroot()
128 except:
129 lm = ElementTree.Element("manifest")
130
131 for localpath in lm.findall("project"):
132 if localpath.get("name") == projectname:
133 return 1
134
135 ## Search in main manifest, too
136 try:
137 lm = ElementTree.parse(".repo/manifest.xml")
138 lm = lm.getroot()
139 except:
140 lm = ElementTree.Element("manifest")
141
142 for localpath in lm.findall("project"):
143 if localpath.get("name") == projectname:
144 return 1
145
146 return None
147
148def add_to_manifest(repositories, fallback_branch = None):
149 try:
150 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
151 lm = lm.getroot()
152 except:
153 lm = ElementTree.Element("manifest")
154
155 for repository in repositories:
156 repo_name = repository['repository']
157 repo_target = repository['target_path']
158 if exists_in_tree(lm, repo_name):
159 print 'CyanogenMod/%s already exists' % (repo_name)
160 continue
161
162 print 'Adding dependency: CyanogenMod/%s -> %s' % (repo_name, repo_target)
163 project = ElementTree.Element("project", attrib = { "path": repo_target,
164 "remote": "github", "name": "CyanogenMod/%s" % repo_name })
165
166 if 'branch' in repository:
167 project.set('revision',repository['branch'])
168 elif fallback_branch:
169 print "Using fallback branch %s for %s" % (fallback_branch, repo_name)
170 project.set('revision', fallback_branch)
171 else:
172 print "Using default branch for %s" % repo_name
173
Koushik Dutta780fb5f2011-11-26 18:51:42 -0800174 lm.append(project)
Koushik Dutta780fb5f2011-11-26 18:51:42 -0800175
Diogo Ferreira0d109172012-03-18 21:18:29 +0000176 indent(lm, 0)
177 raw_xml = ElementTree.tostring(lm)
178 raw_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + raw_xml
Koushik Dutta780fb5f2011-11-26 18:51:42 -0800179
Diogo Ferreira0d109172012-03-18 21:18:29 +0000180 f = open('.repo/local_manifests/roomservice.xml', 'w')
181 f.write(raw_xml)
182 f.close()
183
184def fetch_dependencies(repo_path, fallback_branch = None):
185 print 'Looking for dependencies'
186 dependencies_path = repo_path + '/cm.dependencies'
187 syncable_repos = []
188
189 if os.path.exists(dependencies_path):
190 dependencies_file = open(dependencies_path, 'r')
191 dependencies = json.loads(dependencies_file.read())
192 fetch_list = []
193
194 for dependency in dependencies:
195 if not is_in_manifest("CyanogenMod/%s" % dependency['repository']):
196 fetch_list.append(dependency)
197 syncable_repos.append(dependency['target_path'])
198
199 dependencies_file.close()
200
201 if len(fetch_list) > 0:
202 print 'Adding dependencies to manifest'
203 add_to_manifest(fetch_list, fallback_branch)
204 else:
205 print 'Dependencies file not found, bailing out.'
206
207 if len(syncable_repos) > 0:
208 print 'Syncing dependencies'
209 os.system('repo sync %s' % ' '.join(syncable_repos))
210
211 for deprepo in syncable_repos:
212 fetch_dependencies(deprepo)
213
214def has_branch(branches, revision):
215 return revision in [branch['name'] for branch in branches]
216
217if depsonly:
218 repo_path = get_from_manifest(device)
219 if repo_path:
220 fetch_dependencies(repo_path)
221 else:
222 print "Trying dependencies-only mode on a non-existing device tree?"
223
224 sys.exit()
225
226else:
227 for repository in repositories:
228 repo_name = repository['name']
229 if repo_name.startswith("android_device_") and repo_name.endswith("_" + device):
230 print "Found repository: %s" % repository['name']
231
232 manufacturer = repo_name.replace("android_device_", "").replace("_" + device, "")
233
234 default_revision = get_default_revision()
235 print "Default revision: %s" % default_revision
236 print "Checking branch info"
237 githubreq = urllib2.Request(repository['branches_url'].replace('{/branch}', ''))
238 add_auth(githubreq)
239 result = json.loads(urllib2.urlopen(githubreq).read())
240
241 ## Try tags, too, since that's what releases use
242 if not has_branch(result, default_revision):
243 githubreq = urllib2.Request(repository['tags_url'].replace('{/tag}', ''))
244 add_auth(githubreq)
245 result.extend (json.loads(urllib2.urlopen(githubreq).read()))
246
247 repo_path = "device/%s/%s" % (manufacturer, device)
248 adding = {'repository':repo_name,'target_path':repo_path}
249
250 fallback_branch = None
251 if not has_branch(result, default_revision):
252 if os.getenv('ROOMSERVICE_BRANCHES'):
253 fallbacks = filter(bool, os.getenv('ROOMSERVICE_BRANCHES').split(' '))
254 for fallback in fallbacks:
255 if has_branch(result, fallback):
256 print "Using fallback branch: %s" % fallback
257 fallback_branch = fallback
258 break
259
260 if not fallback_branch:
261 print "Default revision %s not found in %s. Bailing." % (default_revision, repo_name)
262 print "Branches found:"
263 for branch in [branch['name'] for branch in result]:
264 print branch
265 print "Use the ROOMSERVICE_BRANCHES environment variable to specify a list of fallback branches."
266 sys.exit()
267
268 add_to_manifest([adding], fallback_branch)
269
270 print "Syncing repository to retrieve project."
271 os.system('repo sync %s' % repo_path)
272 print "Repository synced!"
273
274 fetch_dependencies(repo_path, fallback_branch)
275 print "Done"
276 sys.exit()
277
278print "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