blob: f34963489f1c95d2bfaba8c4ca3c04befc7c418b [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
Chirayu Desai55b9b642013-04-30 17:08:17 +053016from __future__ import print_function
17
18import base64
Koushik Dutta780fb5f2011-11-26 18:51:42 -080019import json
Chirayu Desai55b9b642013-04-30 17:08:17 +053020import netrc
21import os
Diogo Ferreira0d109172012-03-18 21:18:29 +000022import re
Chirayu Desai55b9b642013-04-30 17:08:17 +053023import 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
Koushik Dutta780fb5f2011-11-26 18:51:42 -080039from xml.etree import ElementTree
40
41product = sys.argv[1];
Diogo Ferreira0d109172012-03-18 21:18:29 +000042
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:
Chirayu Desai55b9b642013-04-30 17:08:17 +053054 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 -080055
56repositories = []
57
Diogo Ferreira0d109172012-03-18 21:18:29 +000058try:
59 authtuple = netrc.netrc().authenticators("api.github.com")
60
61 if authtuple:
62 githubauth = base64.encodestring('%s:%s' % (authtuple[0], authtuple[2])).replace('\n', '')
63 else:
64 githubauth = None
65except:
66 githubauth = None
67
68def add_auth(githubreq):
69 if githubauth:
70 githubreq.add_header("Authorization","Basic %s" % githubauth)
71
Matt Moweree84e3f2014-10-31 21:02:37 -050072if not depsonly:
Matt Mower28cb6ba2015-01-02 00:08:48 -060073 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 +000074 add_auth(githubreq)
Matt Moweree84e3f2014-10-31 21:02:37 -050075 try:
Anthony King365b1e22015-01-08 11:39:12 -060076 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
77 except urllib.error.URLError:
78 print("Failed to search GitHub")
Matt Moweree84e3f2014-10-31 21:02:37 -050079 sys.exit()
Anthony King365b1e22015-01-08 11:39:12 -060080 except ValueError:
81 print("Failed to parse return data from GitHub")
Matt Moweree84e3f2014-10-31 21:02:37 -050082 sys.exit()
Anthony King365b1e22015-01-08 11:39:12 -060083 for res in result.get('items', []):
Diogo Ferreira0d109172012-03-18 21:18:29 +000084 repositories.append(res)
Koushik Dutta780fb5f2011-11-26 18:51:42 -080085
Diogo Ferreira0d109172012-03-18 21:18:29 +000086local_manifests = r'.repo/local_manifests'
87if not os.path.exists(local_manifests): os.makedirs(local_manifests)
Koushik Dutta780fb5f2011-11-26 18:51:42 -080088
Diogo Ferreira0d109172012-03-18 21:18:29 +000089def exists_in_tree(lm, repository):
90 for child in lm.getchildren():
91 if child.attrib['name'].endswith(repository):
92 return True
93 return False
94
95# in-place prettyprint formatter
96def indent(elem, level=0):
97 i = "\n" + level*" "
98 if len(elem):
99 if not elem.text or not elem.text.strip():
100 elem.text = i + " "
101 if not elem.tail or not elem.tail.strip():
102 elem.tail = i
103 for elem in elem:
104 indent(elem, level+1)
105 if not elem.tail or not elem.tail.strip():
106 elem.tail = i
107 else:
108 if level and (not elem.tail or not elem.tail.strip()):
109 elem.tail = i
110
111def get_default_revision():
112 m = ElementTree.parse(".repo/manifest.xml")
113 d = m.findall('default')[0]
114 r = d.get('revision')
115 return r.replace('refs/heads/', '').replace('refs/tags/', '')
116
117def get_from_manifest(devicename):
118 try:
119 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
120 lm = lm.getroot()
121 except:
122 lm = ElementTree.Element("manifest")
123
124 for localpath in lm.findall("project"):
125 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
126 return localpath.get("path")
127
128 # Devices originally from AOSP are in the main manifest...
129 try:
130 mm = ElementTree.parse(".repo/manifest.xml")
131 mm = mm.getroot()
132 except:
133 mm = ElementTree.Element("manifest")
134
135 for localpath in mm.findall("project"):
136 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
137 return localpath.get("path")
138
139 return None
140
141def is_in_manifest(projectname):
142 try:
143 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
144 lm = lm.getroot()
145 except:
146 lm = ElementTree.Element("manifest")
147
148 for localpath in lm.findall("project"):
149 if localpath.get("name") == projectname:
150 return 1
151
152 ## Search in main manifest, too
153 try:
154 lm = ElementTree.parse(".repo/manifest.xml")
155 lm = lm.getroot()
156 except:
157 lm = ElementTree.Element("manifest")
158
159 for localpath in lm.findall("project"):
160 if localpath.get("name") == projectname:
161 return 1
162
163 return None
164
165def add_to_manifest(repositories, fallback_branch = None):
166 try:
167 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
168 lm = lm.getroot()
169 except:
170 lm = ElementTree.Element("manifest")
171
172 for repository in repositories:
173 repo_name = repository['repository']
174 repo_target = repository['target_path']
175 if exists_in_tree(lm, repo_name):
Chirayu Desai55b9b642013-04-30 17:08:17 +0530176 print('CyanogenMod/%s already exists' % (repo_name))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000177 continue
178
Chirayu Desai55b9b642013-04-30 17:08:17 +0530179 print('Adding dependency: CyanogenMod/%s -> %s' % (repo_name, repo_target))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000180 project = ElementTree.Element("project", attrib = { "path": repo_target,
181 "remote": "github", "name": "CyanogenMod/%s" % repo_name })
182
183 if 'branch' in repository:
184 project.set('revision',repository['branch'])
185 elif fallback_branch:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530186 print("Using fallback branch %s for %s" % (fallback_branch, repo_name))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000187 project.set('revision', fallback_branch)
188 else:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530189 print("Using default branch for %s" % repo_name)
Diogo Ferreira0d109172012-03-18 21:18:29 +0000190
Koushik Dutta780fb5f2011-11-26 18:51:42 -0800191 lm.append(project)
Koushik Dutta780fb5f2011-11-26 18:51:42 -0800192
Diogo Ferreira0d109172012-03-18 21:18:29 +0000193 indent(lm, 0)
Chirayu Desai55b9b642013-04-30 17:08:17 +0530194 raw_xml = ElementTree.tostring(lm).decode()
Diogo Ferreira0d109172012-03-18 21:18:29 +0000195 raw_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + raw_xml
Koushik Dutta780fb5f2011-11-26 18:51:42 -0800196
Diogo Ferreira0d109172012-03-18 21:18:29 +0000197 f = open('.repo/local_manifests/roomservice.xml', 'w')
198 f.write(raw_xml)
199 f.close()
200
201def fetch_dependencies(repo_path, fallback_branch = None):
Chirayu Desai55b9b642013-04-30 17:08:17 +0530202 print('Looking for dependencies')
Diogo Ferreira0d109172012-03-18 21:18:29 +0000203 dependencies_path = repo_path + '/cm.dependencies'
204 syncable_repos = []
205
206 if os.path.exists(dependencies_path):
207 dependencies_file = open(dependencies_path, 'r')
208 dependencies = json.loads(dependencies_file.read())
209 fetch_list = []
210
211 for dependency in dependencies:
212 if not is_in_manifest("CyanogenMod/%s" % dependency['repository']):
213 fetch_list.append(dependency)
214 syncable_repos.append(dependency['target_path'])
215
216 dependencies_file.close()
217
218 if len(fetch_list) > 0:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530219 print('Adding dependencies to manifest')
Diogo Ferreira0d109172012-03-18 21:18:29 +0000220 add_to_manifest(fetch_list, fallback_branch)
221 else:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530222 print('Dependencies file not found, bailing out.')
Diogo Ferreira0d109172012-03-18 21:18:29 +0000223
224 if len(syncable_repos) > 0:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530225 print('Syncing dependencies')
Diogo Ferreira0d109172012-03-18 21:18:29 +0000226 os.system('repo sync %s' % ' '.join(syncable_repos))
227
228 for deprepo in syncable_repos:
229 fetch_dependencies(deprepo)
230
231def has_branch(branches, revision):
232 return revision in [branch['name'] for branch in branches]
233
234if depsonly:
235 repo_path = get_from_manifest(device)
236 if repo_path:
237 fetch_dependencies(repo_path)
238 else:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530239 print("Trying dependencies-only mode on a non-existing device tree?")
Diogo Ferreira0d109172012-03-18 21:18:29 +0000240
241 sys.exit()
242
243else:
244 for repository in repositories:
245 repo_name = repository['name']
246 if repo_name.startswith("android_device_") and repo_name.endswith("_" + device):
Chirayu Desai55b9b642013-04-30 17:08:17 +0530247 print("Found repository: %s" % repository['name'])
Diogo Ferreira0d109172012-03-18 21:18:29 +0000248
249 manufacturer = repo_name.replace("android_device_", "").replace("_" + device, "")
250
251 default_revision = get_default_revision()
Chirayu Desai55b9b642013-04-30 17:08:17 +0530252 print("Default revision: %s" % default_revision)
253 print("Checking branch info")
254 githubreq = urllib.request.Request(repository['branches_url'].replace('{/branch}', ''))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000255 add_auth(githubreq)
Chirayu Desai55b9b642013-04-30 17:08:17 +0530256 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
Diogo Ferreira0d109172012-03-18 21:18:29 +0000257
258 ## Try tags, too, since that's what releases use
259 if not has_branch(result, default_revision):
Chirayu Desai55b9b642013-04-30 17:08:17 +0530260 githubreq = urllib.request.Request(repository['tags_url'].replace('{/tag}', ''))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000261 add_auth(githubreq)
Chirayu Desai55b9b642013-04-30 17:08:17 +0530262 result.extend (json.loads(urllib.request.urlopen(githubreq).read().decode()))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000263
264 repo_path = "device/%s/%s" % (manufacturer, device)
265 adding = {'repository':repo_name,'target_path':repo_path}
266
267 fallback_branch = None
268 if not has_branch(result, default_revision):
269 if os.getenv('ROOMSERVICE_BRANCHES'):
Chirayu Desai55b9b642013-04-30 17:08:17 +0530270 fallbacks = list(filter(bool, os.getenv('ROOMSERVICE_BRANCHES').split(' ')))
Diogo Ferreira0d109172012-03-18 21:18:29 +0000271 for fallback in fallbacks:
272 if has_branch(result, fallback):
Chirayu Desai55b9b642013-04-30 17:08:17 +0530273 print("Using fallback branch: %s" % fallback)
Diogo Ferreira0d109172012-03-18 21:18:29 +0000274 fallback_branch = fallback
275 break
276
277 if not fallback_branch:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530278 print("Default revision %s not found in %s. Bailing." % (default_revision, repo_name))
279 print("Branches found:")
Diogo Ferreira0d109172012-03-18 21:18:29 +0000280 for branch in [branch['name'] for branch in result]:
Chirayu Desai55b9b642013-04-30 17:08:17 +0530281 print(branch)
282 print("Use the ROOMSERVICE_BRANCHES environment variable to specify a list of fallback branches.")
Diogo Ferreira0d109172012-03-18 21:18:29 +0000283 sys.exit()
284
285 add_to_manifest([adding], fallback_branch)
286
Chirayu Desai55b9b642013-04-30 17:08:17 +0530287 print("Syncing repository to retrieve project.")
Diogo Ferreira0d109172012-03-18 21:18:29 +0000288 os.system('repo sync %s' % repo_path)
Chirayu Desai55b9b642013-04-30 17:08:17 +0530289 print("Repository synced!")
Diogo Ferreira0d109172012-03-18 21:18:29 +0000290
291 fetch_dependencies(repo_path, fallback_branch)
Chirayu Desai55b9b642013-04-30 17:08:17 +0530292 print("Done")
Diogo Ferreira0d109172012-03-18 21:18:29 +0000293 sys.exit()
294
Chirayu Desai55b9b642013-04-30 17:08:17 +0530295print("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)