blob: 9e43bd8a9f36622c7a671324c86d48fb1b3c4e0a [file] [log] [blame]
cybojenix3e873402013-10-17 03:34:57 +04001#!/usr/bin/env python
2
3# Copyright (C) 2013 Cybojenix <anthonydking@gmail.com>
4# Copyright (C) 2013 The OmniROM Project
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19from __future__ import print_function
20import json
21import sys
22import os
23import os.path
24import re
25from xml.etree import ElementTree as ES
26# Use the urllib importer from the Cyanogenmod roomservice
27try:
28 # For python3
29 import urllib.request
30except ImportError:
31 # For python2
32 import imp
33 import urllib2
34 urllib = imp.new_module('urllib')
35 urllib.request = urllib2
36
37# Config
38# set this to the default remote to use in repo
39default_rem = "omnirom"
40# set this to the default revision to use (branch/tag name)
41default_rev = "android-8.0"
42# set this to the remote that you use for projects from your team repos
43# example fetch="https://github.com/omnirom"
44default_team_rem = "omnirom"
45# this shouldn't change unless google makes changes
46local_manifest_dir = ".repo/local_manifests"
47# change this to your name on github (or equivalent hosting)
48android_team = "omnirom"
49# url to gerrit repository
50gerrit_url = "gerrit.omnirom.org"
51
52
53def check_repo_exists(git_data, device):
54 re_match = "^android_device_.*_{device}$".format(device=device)
55 matches = filter(lambda x: re.match(re_match, x), git_data)
56 if len(matches) != 1:
57 raise Exception("{device} not found,"
58 "exiting roomservice".format(device=device))
59
60 return git_data[matches[0]]
61
62
63def search_gerrit_for_device(device):
64 # TODO: In next gerrit release regex search with r= should be supported!
65 git_search_url = "https://{gerrit_url}/projects/?m={device}".format(
66 gerrit_url=gerrit_url,
67 device=device
68 )
69 git_req = urllib.request.Request(git_search_url)
70 try:
71 response = urllib.request.urlopen(git_req)
72 except urllib.request.HTTPError as e:
73 print("There was an issue connecting to gerrit."
74 " Please try again in a minute")
75 except urllib.request.URLError as e:
76 print("WARNING: No network connection available.")
77 else:
78 # Skip silly gerrit "header"
79 response.readline()
80 git_data = json.load(response)
81 device_data = check_repo_exists(git_data, device)
82 print("found the {} device repo".format(device))
83 return device_data
84
85
86def parse_device_directory(device_url, device):
87 pattern = "^android_device_(?P<vendor>.+)_{}$".format(device)
88 match = re.match(pattern, device_url)
89
90 if match is None:
91 raise Exception("Invalid project name {}".format(device_url))
92 return "device/{vendor}/{device}".format(
93 vendor=match.group('vendor'),
94 device=device,
95 )
96
97
98# Thank you RaYmAn
99def iterate_manifests():
100 files = []
101 for file in os.listdir(local_manifest_dir):
102 if file.endswith(".xml"):
103 files.append(os.path.join(local_manifest_dir, file))
104 files.append('.repo/manifest.xml')
105 for file in files:
106 try:
107 man = ES.parse(file)
108 man = man.getroot()
109 except IOError, ES.ParseError:
110 print("WARNING: error while parsing %s" % file)
111 else:
112 for project in man.findall("project"):
113 yield project
114
115
116def check_project_exists(url, revision, path):
117 for project in iterate_manifests():
118 if project.get("name") == url and project.get("revision") == revision and project.get("path") == path:
119 return True
120 return False
121
122
123def check_target_exists(directory):
124 return os.path.isdir(directory)
125
126
127# Use the indent function from http://stackoverflow.com/a/4590052
128def indent(elem, level=0):
129 i = ''.join(["\n", level*" "])
130 if len(elem):
131 if not elem.text or not elem.text.strip():
132 elem.text = ''.join([i, " "])
133 if not elem.tail or not elem.tail.strip():
134 elem.tail = i
135 for elem in elem:
136 indent(elem, level+1)
137 if not elem.tail or not elem.tail.strip():
138 elem.tail = i
139 else:
140 if level and (not elem.tail or not elem.tail.strip()):
141 elem.tail = i
142
143
144def create_manifest_project(url, directory,
145 remote=default_rem,
146 revision=default_rev):
147 project_exists = check_project_exists(url, revision, directory)
148
149 if project_exists:
150 return None
151
152 project = ES.Element("project",
153 attrib={
154 "path": directory,
155 "name": url,
156 "remote": remote,
157 "revision": revision
158 })
159 return project
160
161
162def append_to_manifest(project):
163 try:
164 lm = ES.parse('/'.join([local_manifest_dir, "roomservice.xml"]))
165 lm = lm.getroot()
166 except IOError, ES.ParseError:
167 lm = ES.Element("manifest")
168 lm.append(project)
169 return lm
170
171
172def write_to_manifest(manifest):
173 indent(manifest)
174 raw_xml = ES.tostring(manifest).decode()
175 raw_xml = ''.join(['<?xml version="1.0" encoding="UTF-8"?>\n'
176 '<!--Please do not manually edit this file-->\n',
177 raw_xml])
178
179 with open('/'.join([local_manifest_dir, "roomservice.xml"]), 'w') as f:
180 f.write(raw_xml)
181 print("wrote the new roomservice manifest")
182
183
184def parse_device_from_manifest(device):
185 for project in iterate_manifests():
186 name = project.get('name')
187 if name.startswith("android_device_") and name.endswith(device):
188 return project.get('path')
189 return None
190
191
192def parse_device_from_folder(device):
193 search = []
194 for sub_folder in os.listdir("device"):
195 if os.path.isdir("device/%s/%s" % (sub_folder, device)):
196 search.append("device/%s/%s" % (sub_folder, device))
197 if len(search) > 1:
198 print("multiple devices under the name %s. "
199 "defaulting to checking the manifest" % device)
200 location = parse_device_from_manifest(device)
201 elif len(search) == 1:
202 location = search[0]
203 else:
204 print("Your device can't be found in device sources..")
205 location = parse_device_from_manifest(device)
206 return location
207
208
209def parse_dependency_file(location):
210 dep_file = "omni.dependencies"
211 dep_location = '/'.join([location, dep_file])
212 if not os.path.isfile(dep_location):
213 print("WARNING: %s file not found" % dep_location)
214 sys.exit()
215 try:
216 with open(dep_location, 'r') as f:
217 dependencies = json.loads(f.read())
218 except ValueError:
219 raise Exception("ERROR: malformed dependency file")
220 return dependencies
221
222# if there is any conflict with existing and new
223# delete the roomservice.xml file and create new
224def check_manifest_problems(dependencies):
225 for dependency in dependencies:
226 repository = dependency.get("repository")
227 target_path = dependency.get("target_path")
228 revision = dependency.get("revision", default_rev)
229 remote = dependency.get("remote", default_rem)
230
231 # check for existing projects
232 for project in iterate_manifests():
233 if project.get("path") == target_path and project.get("revision") != revision:
234 print("WARNING: detected conflict in revisions for repository ", repository)
235 current_dependency = str(project.get(repository))
236 file = ES.parse('/'.join([local_manifest_dir, "roomservice.xml"]))
237 file_root = file.getroot()
238 for current_project in file_root.findall('project'):
239 new_dependency = str(current_project.find('revision'))
240 if new_dependency == current_dependency:
241 file_root.remove(current_project)
242 file.write('/'.join([local_manifest_dir, "roomservice.xml"]))
243 return
244
245def create_dependency_manifest(dependencies):
246 projects = []
247 for dependency in dependencies:
248 repository = dependency.get("repository")
249 target_path = dependency.get("target_path")
250 revision = dependency.get("revision", default_rev)
251 remote = dependency.get("remote", default_rem)
252
253 # not adding an organization should default to android_team
254 # only apply this to github
255 if remote == "github":
256 if "/" not in repository:
257 repository = '/'.join([android_team, repository])
258 project = create_manifest_project(repository,
259 target_path,
260 remote=remote,
261 revision=revision)
262 if project is not None:
263 manifest = append_to_manifest(project)
264 write_to_manifest(manifest)
265 projects.append(target_path)
266 if len(projects) > 0:
267 os.system("repo sync -f --no-clone-bundle %s" % " ".join(projects))
268
269
270def create_common_dependencies_manifest(dependencies):
271 dep_file = "omni.dependencies"
272 common_list = []
273 if dependencies is not None:
274 for dependency in dependencies:
275 try:
276 index = common_list.index(dependency['target_path'])
277 except ValueError:
278 index = None
279 if index is None:
280 common_list.append(dependency['target_path'])
281 dep_location = '/'.join([dependency['target_path'], dep_file])
282 if not os.path.isfile(dep_location):
283 sys.exit()
284 else:
285 try:
286 with open(dep_location, 'r') as f:
287 common_deps = json.loads(f.read())
288 except ValueError:
289 raise Exception("ERROR: malformed dependency file")
290
291 if common_deps is not None:
292 print("Looking for dependencies on: ",
293 dependency['target_path'])
294 check_manifest_problems(common_deps)
295 create_dependency_manifest(common_deps)
296 create_common_dependencies_manifest(common_deps)
297
298
299def fetch_dependencies(device):
300 location = parse_device_from_folder(device)
301 if location is None or not os.path.isdir(location):
302 raise Exception("ERROR: could not find your device "
303 "folder location, bailing out")
304 dependencies = parse_dependency_file(location)
305 check_manifest_problems(dependencies)
306 create_dependency_manifest(dependencies)
307 create_common_dependencies_manifest(dependencies)
308 fetch_device(device)
309
310def check_device_exists(device):
311 location = parse_device_from_folder(device)
312 if location is None:
313 return False
314 return os.path.isdir(location)
315
316
317def fetch_device(device):
318 if check_device_exists(device):
319 print("WARNING: Trying to fetch a device that's already there")
320 git_data = search_gerrit_for_device(device)
321 if git_data is not None:
322 device_url = git_data['id']
323 device_dir = parse_device_directory(device_url, device)
324 project = create_manifest_project(device_url,
325 device_dir,
326 remote=default_team_rem)
327 if project is not None:
328 manifest = append_to_manifest(project)
329 write_to_manifest(manifest)
330 # In case a project was written to manifest, but never synced
331 if project is not None or not check_target_exists(device_dir):
332 print("syncing the device config")
333 os.system('repo sync -f --no-clone-bundle %s' % device_dir)
334
335
336if __name__ == '__main__':
337 if not os.path.isdir(local_manifest_dir):
338 os.mkdir(local_manifest_dir)
339
340 product = sys.argv[1]
341 try:
342 device = product[product.index("_") + 1:]
343 except ValueError:
344 device = product
345
346 if len(sys.argv) > 2:
347 deps_only = sys.argv[2]
348 else:
349 deps_only = False
350
351 if not deps_only:
352 fetch_device(device)
353 fetch_dependencies(device)