blob: e7ba8f7614ddb2743d7968a55205cfc43c04e642 [file] [log] [blame]
Michael Bestas3952f6c2016-08-26 01:12:08 +03001#!/usr/bin/env python
2# 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
16from __future__ import print_function
17
18import base64
19import json
20import netrc
21import os
22import re
23import sys
24try:
25 # For python3
26 import urllib.error
27 import urllib.parse
28 import urllib.request
29except ImportError:
30 # For python2
31 import imp
32 import urllib2
33 import urlparse
34 urllib = imp.new_module('urllib')
35 urllib.error = urllib2
36 urllib.parse = urlparse
37 urllib.request = urllib2
38
39from xml.etree import ElementTree
40
41product = sys.argv[1]
42
43if len(sys.argv) > 2:
44 depsonly = sys.argv[2]
45else:
46 depsonly = None
47
48try:
49 device = product[product.index("_") + 1:]
50except:
51 device = product
52
53if not depsonly:
54 print("Device %s not found. Attempting to retrieve device repository from CyanogenMod Github (http://github.com/CyanogenMod)." % device)
55
56repositories = []
57
58try:
59 authtuple = netrc.netrc().authenticators("api.github.com")
60
61 if authtuple:
62 auth_string = ('%s:%s' % (authtuple[0], authtuple[2])).encode()
63 githubauth = base64.encodestring(auth_string).decode().replace('\n', '')
64 else:
65 githubauth = None
66except:
67 githubauth = None
68
69def add_auth(githubreq):
70 if githubauth:
71 githubreq.add_header("Authorization","Basic %s" % githubauth)
72
73if not depsonly:
74 githubreq = urllib.request.Request("https://api.github.com/search/repositories?q=%s+user:CyanogenMod+in:name+fork:true" % device)
75 add_auth(githubreq)
76 try:
77 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
78 except urllib.error.URLError:
79 print("Failed to search GitHub")
80 sys.exit()
81 except ValueError:
82 print("Failed to parse return data from GitHub")
83 sys.exit()
84 for res in result.get('items', []):
85 repositories.append(res)
86
87local_manifests = r'.repo/local_manifests'
88if not os.path.exists(local_manifests): os.makedirs(local_manifests)
89
90def exists_in_tree(lm, path):
91 for child in lm.getchildren():
92 if child.attrib['path'] == path:
93 return True
94 return False
95
96# in-place prettyprint formatter
97def indent(elem, level=0):
98 i = "\n" + level*" "
99 if len(elem):
100 if not elem.text or not elem.text.strip():
101 elem.text = i + " "
102 if not elem.tail or not elem.tail.strip():
103 elem.tail = i
104 for elem in elem:
105 indent(elem, level+1)
106 if not elem.tail or not elem.tail.strip():
107 elem.tail = i
108 else:
109 if level and (not elem.tail or not elem.tail.strip()):
110 elem.tail = i
111
112def get_default_revision():
113 m = ElementTree.parse(".repo/manifest.xml")
114 d = m.findall('default')[0]
115 r = d.get('revision')
116 return r.replace('refs/heads/', '').replace('refs/tags/', '')
117
118def get_from_manifest(devicename):
119 try:
120 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
121 lm = lm.getroot()
122 except:
123 lm = ElementTree.Element("manifest")
124
125 for localpath in lm.findall("project"):
126 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
127 return localpath.get("path")
128
129 # Devices originally from AOSP are in the main manifest...
130 try:
131 mm = ElementTree.parse(".repo/manifest.xml")
132 mm = mm.getroot()
133 except:
134 mm = ElementTree.Element("manifest")
135
136 for localpath in mm.findall("project"):
137 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
138 return localpath.get("path")
139
140 return None
141
142def is_in_manifest(projectpath):
143 try:
144 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
145 lm = lm.getroot()
146 except:
147 lm = ElementTree.Element("manifest")
148
149 for localpath in lm.findall("project"):
150 if localpath.get("path") == projectpath:
151 return True
152
153 ## Search in main manifest, too
154 try:
155 lm = ElementTree.parse(".repo/manifest.xml")
156 lm = lm.getroot()
157 except:
158 lm = ElementTree.Element("manifest")
159
160 for localpath in lm.findall("project"):
161 if localpath.get("path") == projectpath:
162 return True
163
164 return False
165
166def add_to_manifest(repositories, fallback_branch = None):
167 try:
168 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
169 lm = lm.getroot()
170 except:
171 lm = ElementTree.Element("manifest")
172
173 for repository in repositories:
174 repo_name = repository['repository']
175 repo_target = repository['target_path']
176 print('Checking if %s is fetched from %s' % (repo_target, repo_name))
177 if is_in_manifest(repo_target):
178 print('CyanogenMod/%s already fetched to %s' % (repo_name, repo_target))
179 continue
180
181 print('Adding dependency: CyanogenMod/%s -> %s' % (repo_name, repo_target))
182 project = ElementTree.Element("project", attrib = { "path": repo_target,
183 "remote": "github", "name": "CyanogenMod/%s" % repo_name })
184
185 if 'branch' in repository:
186 project.set('revision',repository['branch'])
187 elif fallback_branch:
188 print("Using fallback branch %s for %s" % (fallback_branch, repo_name))
189 project.set('revision', fallback_branch)
190 else:
191 print("Using default branch for %s" % repo_name)
192
193 lm.append(project)
194
195 indent(lm, 0)
196 raw_xml = ElementTree.tostring(lm).decode()
197 raw_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + raw_xml
198
199 f = open('.repo/local_manifests/roomservice.xml', 'w')
200 f.write(raw_xml)
201 f.close()
202
203def fetch_dependencies(repo_path, fallback_branch = None):
Adrian DC01bdd552016-11-29 00:24:26 +0100204 print('Looking for dependencies in %s' % repo_path)
Michael Bestas3952f6c2016-08-26 01:12:08 +0300205 dependencies_path = repo_path + '/cm.dependencies'
206 syncable_repos = []
Adrian DC01bdd552016-11-29 00:24:26 +0100207 verify_repos = []
Michael Bestas3952f6c2016-08-26 01:12:08 +0300208
209 if os.path.exists(dependencies_path):
210 dependencies_file = open(dependencies_path, 'r')
211 dependencies = json.loads(dependencies_file.read())
212 fetch_list = []
213
214 for dependency in dependencies:
215 if not is_in_manifest(dependency['target_path']):
216 fetch_list.append(dependency)
217 syncable_repos.append(dependency['target_path'])
Adrian DC01bdd552016-11-29 00:24:26 +0100218 verify_repos.append(dependency['target_path'])
219 elif re.search("android_device_.*_.*$", dependency['repository']):
220 verify_repos.append(dependency['target_path'])
Michael Bestas3952f6c2016-08-26 01:12:08 +0300221
222 dependencies_file.close()
223
224 if len(fetch_list) > 0:
225 print('Adding dependencies to manifest')
226 add_to_manifest(fetch_list, fallback_branch)
227 else:
228 print('Dependencies file not found, bailing out.')
229
230 if len(syncable_repos) > 0:
231 print('Syncing dependencies')
232 os.system('repo sync --force-sync %s' % ' '.join(syncable_repos))
233
Adrian DC01bdd552016-11-29 00:24:26 +0100234 for deprepo in verify_repos:
Michael Bestas3952f6c2016-08-26 01:12:08 +0300235 fetch_dependencies(deprepo)
236
237def has_branch(branches, revision):
238 return revision in [branch['name'] for branch in branches]
239
240if depsonly:
241 repo_path = get_from_manifest(device)
242 if repo_path:
243 fetch_dependencies(repo_path)
244 else:
245 print("Trying dependencies-only mode on a non-existing device tree?")
246
247 sys.exit()
248
249else:
250 for repository in repositories:
251 repo_name = repository['name']
252 if repo_name.startswith("android_device_") and repo_name.endswith("_" + device):
253 print("Found repository: %s" % repository['name'])
254
255 manufacturer = repo_name.replace("android_device_", "").replace("_" + device, "")
256
257 default_revision = get_default_revision()
258 print("Default revision: %s" % default_revision)
259 print("Checking branch info")
260 githubreq = urllib.request.Request(repository['branches_url'].replace('{/branch}', ''))
261 add_auth(githubreq)
262 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
263
264 ## Try tags, too, since that's what releases use
265 if not has_branch(result, default_revision):
266 githubreq = urllib.request.Request(repository['tags_url'].replace('{/tag}', ''))
267 add_auth(githubreq)
268 result.extend (json.loads(urllib.request.urlopen(githubreq).read().decode()))
269
270 repo_path = "device/%s/%s" % (manufacturer, device)
271 adding = {'repository':repo_name,'target_path':repo_path}
272
273 fallback_branch = None
274 if not has_branch(result, default_revision):
275 if os.getenv('ROOMSERVICE_BRANCHES'):
276 fallbacks = list(filter(bool, os.getenv('ROOMSERVICE_BRANCHES').split(' ')))
277 for fallback in fallbacks:
278 if has_branch(result, fallback):
279 print("Using fallback branch: %s" % fallback)
280 fallback_branch = fallback
281 break
282
283 if not fallback_branch:
284 print("Default revision %s not found in %s. Bailing." % (default_revision, repo_name))
285 print("Branches found:")
286 for branch in [branch['name'] for branch in result]:
287 print(branch)
288 print("Use the ROOMSERVICE_BRANCHES environment variable to specify a list of fallback branches.")
289 sys.exit()
290
291 add_to_manifest([adding], fallback_branch)
292
293 print("Syncing repository to retrieve project.")
294 os.system('repo sync --force-sync %s' % repo_path)
295 print("Repository synced!")
296
297 fetch_dependencies(repo_path, fallback_branch)
298 print("Done")
299 sys.exit()
300
301print("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)