blob: 3c44a198ef64ca3441bab8c088f517d1a83db258 [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
Marko Man9401bfe2020-04-15 22:16:16 +020039default_rem = "omnirom2"
cybojenix3e873402013-10-17 03:34:57 +040040# set this to the default revision to use (branch/tag name)
micky3871a865e22020-10-01 03:31:52 +020041default_rev = "android-11"
cybojenix3e873402013-10-17 03:34:57 +040042# set this to the remote that you use for projects from your team repos
43# example fetch="https://github.com/omnirom"
Marko Manba650f32020-04-17 00:29:51 +020044default_team_rem = "omnirom2"
cybojenix3e873402013-10-17 03:34:57 +040045# 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)
Felix Elsner67fdf892018-12-04 21:45:49 +010055 matches = list(filter(lambda x: re.match(re_match, x), git_data))
cybojenix3e873402013-10-17 03:34:57 +040056 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)
Felix Elsnerc1c2d752018-11-25 12:46:13 +010072 except urllib.request.HTTPError:
cybojenix3e873402013-10-17 03:34:57 +040073 print("There was an issue connecting to gerrit."
Felix Elsnerc1c2d752018-11-25 12:46:13 +010074 " Please try again in a minute")
75 except urllib.request.URLError:
cybojenix3e873402013-10-17 03:34:57 +040076 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()
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100109 except (IOError, ES.ParseError):
cybojenix3e873402013-10-17 03:34:57 +0400110 print("WARNING: error while parsing %s" % file)
111 else:
112 for project in man.findall("project"):
113 yield project
114
115
maxwen52025a82019-05-08 17:04:42 +0200116def iterate_manifests_remove_project():
117 files = []
118 for file in os.listdir(local_manifest_dir):
119 if file.endswith(".xml"):
120 files.append(os.path.join(local_manifest_dir, file))
121 files.append('.repo/manifest.xml')
122 for file in files:
123 try:
124 man = ES.parse(file)
125 man = man.getroot()
126 except (IOError, ES.ParseError):
127 print("WARNING: error while parsing %s" % file)
128 else:
129 for project in man.findall("remove-project"):
130 yield project
131
cybojenix3e873402013-10-17 03:34:57 +0400132def check_project_exists(url, revision, path):
133 for project in iterate_manifests():
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100134 if project.get("name") == url \
135 and project.get("revision") == revision \
136 and project.get("path") == path:
cybojenix3e873402013-10-17 03:34:57 +0400137 return True
138 return False
139
140
maxwen52025a82019-05-08 17:04:42 +0200141def check_remove_project_exists(url):
142 for project in iterate_manifests_remove_project():
143 if project.get("name") == url:
144 return True
145 return False
146
cybojenix3e873402013-10-17 03:34:57 +0400147def check_target_exists(directory):
148 return os.path.isdir(directory)
149
150
151# Use the indent function from http://stackoverflow.com/a/4590052
152def indent(elem, level=0):
153 i = ''.join(["\n", level*" "])
154 if len(elem):
155 if not elem.text or not elem.text.strip():
156 elem.text = ''.join([i, " "])
157 if not elem.tail or not elem.tail.strip():
158 elem.tail = i
159 for elem in elem:
160 indent(elem, level+1)
161 if not elem.tail or not elem.tail.strip():
162 elem.tail = i
163 else:
164 if level and (not elem.tail or not elem.tail.strip()):
165 elem.tail = i
166
167
168def create_manifest_project(url, directory,
169 remote=default_rem,
170 revision=default_rev):
171 project_exists = check_project_exists(url, revision, directory)
172
173 if project_exists:
174 return None
175
176 project = ES.Element("project",
177 attrib={
178 "path": directory,
179 "name": url,
180 "remote": remote,
181 "revision": revision
182 })
183 return project
184
maxwen52025a82019-05-08 17:04:42 +0200185def create_remove_project(url):
186 remove_project_exists = check_remove_project_exists(url)
187
188 if remove_project_exists:
189 return None
190
191 project = ES.Element("remove-project",
192 attrib={
193 "name": url
194 })
195 return project
cybojenix3e873402013-10-17 03:34:57 +0400196
197def append_to_manifest(project):
198 try:
199 lm = ES.parse('/'.join([local_manifest_dir, "roomservice.xml"]))
200 lm = lm.getroot()
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100201 except (IOError, ES.ParseError):
cybojenix3e873402013-10-17 03:34:57 +0400202 lm = ES.Element("manifest")
203 lm.append(project)
204 return lm
205
206
207def write_to_manifest(manifest):
208 indent(manifest)
209 raw_xml = ES.tostring(manifest).decode()
210 raw_xml = ''.join(['<?xml version="1.0" encoding="UTF-8"?>\n'
211 '<!--Please do not manually edit this file-->\n',
212 raw_xml])
213
214 with open('/'.join([local_manifest_dir, "roomservice.xml"]), 'w') as f:
215 f.write(raw_xml)
216 print("wrote the new roomservice manifest")
217
218
219def parse_device_from_manifest(device):
220 for project in iterate_manifests():
221 name = project.get('name')
222 if name.startswith("android_device_") and name.endswith(device):
223 return project.get('path')
224 return None
225
226
227def parse_device_from_folder(device):
228 search = []
maxwenc8a6b6c2017-12-05 01:37:48 +0100229 if not os.path.isdir("device"):
230 os.mkdir("device")
cybojenix3e873402013-10-17 03:34:57 +0400231 for sub_folder in os.listdir("device"):
232 if os.path.isdir("device/%s/%s" % (sub_folder, device)):
233 search.append("device/%s/%s" % (sub_folder, device))
234 if len(search) > 1:
235 print("multiple devices under the name %s. "
236 "defaulting to checking the manifest" % device)
237 location = parse_device_from_manifest(device)
238 elif len(search) == 1:
239 location = search[0]
240 else:
241 print("Your device can't be found in device sources..")
242 location = parse_device_from_manifest(device)
243 return location
244
245
246def parse_dependency_file(location):
247 dep_file = "omni.dependencies"
248 dep_location = '/'.join([location, dep_file])
249 if not os.path.isfile(dep_location):
250 print("WARNING: %s file not found" % dep_location)
251 sys.exit()
252 try:
253 with open(dep_location, 'r') as f:
254 dependencies = json.loads(f.read())
255 except ValueError:
256 raise Exception("ERROR: malformed dependency file")
257 return dependencies
258
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100259
cybojenix3e873402013-10-17 03:34:57 +0400260# if there is any conflict with existing and new
261# delete the roomservice.xml file and create new
262def check_manifest_problems(dependencies):
263 for dependency in dependencies:
264 repository = dependency.get("repository")
265 target_path = dependency.get("target_path")
266 revision = dependency.get("revision", default_rev)
cybojenix3e873402013-10-17 03:34:57 +0400267
268 # check for existing projects
269 for project in iterate_manifests():
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100270 if project.get("revision") is not None \
271 and project.get("path") is not None \
272 and project.get("path") == target_path \
273 and project.get("revision") != revision:
274 print("WARNING: detected conflict in revisions for repository ",
275 repository)
276 current_dependency = str(project.get(repository))
277 file = ES.parse('/'.join([local_manifest_dir,
278 "roomservice.xml"]))
279 file_root = file.getroot()
280 for current_project in file_root.findall('project'):
281 new_dependency = str(current_project.find('revision'))
282 if new_dependency == current_dependency:
283 file_root.remove(current_project)
284 file.write('/'.join([local_manifest_dir, "roomservice.xml"]))
285 return
286
cybojenix3e873402013-10-17 03:34:57 +0400287
288def create_dependency_manifest(dependencies):
289 projects = []
290 for dependency in dependencies:
291 repository = dependency.get("repository")
292 target_path = dependency.get("target_path")
293 revision = dependency.get("revision", default_rev)
294 remote = dependency.get("remote", default_rem)
maxwen52025a82019-05-08 17:04:42 +0200295 override = dependency.get("override", None)
maxwen2ef6de32020-02-18 12:35:40 +0100296 remove = dependency.get("remove", None)
297
298 if remove is not None:
299 #print("found remove in ", repository)
300 project = create_remove_project(remove)
maxwen52025a82019-05-08 17:04:42 +0200301 if project is not None:
302 manifest = append_to_manifest(project)
303 #print(ES.tostring(manifest).decode())
304 write_to_manifest(manifest)
maxwen2ef6de32020-02-18 12:35:40 +0100305 else:
306 if override is not None:
307 #print("found override in ", repository)
308 project = create_remove_project(override)
309 if project is not None:
310 manifest = append_to_manifest(project)
311 #print(ES.tostring(manifest).decode())
312 write_to_manifest(manifest)
cybojenix3e873402013-10-17 03:34:57 +0400313
maxwen2ef6de32020-02-18 12:35:40 +0100314 # not adding an organization should default to android_team
315 # only apply this to github
316 if remote == "github":
317 if "/" not in repository:
318 repository = '/'.join([android_team, repository])
319 project = create_manifest_project(repository,
320 target_path,
321 remote=remote,
322 revision=revision)
323 if project is not None:
324 manifest = append_to_manifest(project)
325 write_to_manifest(manifest)
326 projects.append(target_path)
cybojenix3e873402013-10-17 03:34:57 +0400327 if len(projects) > 0:
328 os.system("repo sync -f --no-clone-bundle %s" % " ".join(projects))
329
330
331def create_common_dependencies_manifest(dependencies):
332 dep_file = "omni.dependencies"
333 common_list = []
334 if dependencies is not None:
335 for dependency in dependencies:
336 try:
337 index = common_list.index(dependency['target_path'])
338 except ValueError:
339 index = None
340 if index is None:
341 common_list.append(dependency['target_path'])
342 dep_location = '/'.join([dependency['target_path'], dep_file])
343 if not os.path.isfile(dep_location):
344 sys.exit()
345 else:
346 try:
347 with open(dep_location, 'r') as f:
348 common_deps = json.loads(f.read())
349 except ValueError:
350 raise Exception("ERROR: malformed dependency file")
351
352 if common_deps is not None:
353 print("Looking for dependencies on: ",
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100354 dependency['target_path'])
cybojenix3e873402013-10-17 03:34:57 +0400355 check_manifest_problems(common_deps)
356 create_dependency_manifest(common_deps)
357 create_common_dependencies_manifest(common_deps)
358
359
360def fetch_dependencies(device):
Vachounet9ef51682019-11-23 18:56:32 +0100361 if device == "emulator":
362 location = "vendor/omni/utils/emulator"
363 else:
364 location = parse_device_from_folder(device)
cybojenix3e873402013-10-17 03:34:57 +0400365 if location is None or not os.path.isdir(location):
366 raise Exception("ERROR: could not find your device "
367 "folder location, bailing out")
368 dependencies = parse_dependency_file(location)
369 check_manifest_problems(dependencies)
370 create_dependency_manifest(dependencies)
371 create_common_dependencies_manifest(dependencies)
372 fetch_device(device)
373
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100374
cybojenix3e873402013-10-17 03:34:57 +0400375def check_device_exists(device):
376 location = parse_device_from_folder(device)
377 if location is None:
378 return False
379 return os.path.isdir(location)
380
381
382def fetch_device(device):
383 if check_device_exists(device):
Marko Man718f4c22019-07-14 12:12:25 +0100384 print("WARNING: Trying to fetch a device that's already there")
cybojenix3e873402013-10-17 03:34:57 +0400385 git_data = search_gerrit_for_device(device)
386 if git_data is not None:
387 device_url = git_data['id']
388 device_dir = parse_device_directory(device_url, device)
389 project = create_manifest_project(device_url,
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100390 device_dir,
391 remote=default_team_rem)
cybojenix3e873402013-10-17 03:34:57 +0400392 if project is not None:
393 manifest = append_to_manifest(project)
394 write_to_manifest(manifest)
395 # In case a project was written to manifest, but never synced
396 if project is not None or not check_target_exists(device_dir):
397 print("syncing the device config")
398 os.system('repo sync -f --no-clone-bundle %s' % device_dir)
399
400
401if __name__ == '__main__':
402 if not os.path.isdir(local_manifest_dir):
403 os.mkdir(local_manifest_dir)
404
405 product = sys.argv[1]
406 try:
407 device = product[product.index("_") + 1:]
408 except ValueError:
409 device = product
410
411 if len(sys.argv) > 2:
412 deps_only = sys.argv[2]
413 else:
414 deps_only = False
415
416 if not deps_only:
417 fetch_device(device)
418 fetch_dependencies(device)