blob: f77203043035c1b13a81c7feb5775a95f597271c [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):
Vachounetb99cad92021-02-18 14:44:04 +010054 if device.count("_") < 2:
55 re_match = "^android_device_.*_{device}$".format(device=device)
56 else:
57 re_match = "^android_device.*_{device}$".format(device=device)
58
Felix Elsner67fdf892018-12-04 21:45:49 +010059 matches = list(filter(lambda x: re.match(re_match, x), git_data))
Vachounetb99cad92021-02-18 14:44:04 +010060
cybojenix3e873402013-10-17 03:34:57 +040061 if len(matches) != 1:
62 raise Exception("{device} not found,"
63 "exiting roomservice".format(device=device))
64
65 return git_data[matches[0]]
66
67
68def search_gerrit_for_device(device):
69 # TODO: In next gerrit release regex search with r= should be supported!
70 git_search_url = "https://{gerrit_url}/projects/?m={device}".format(
71 gerrit_url=gerrit_url,
72 device=device
73 )
74 git_req = urllib.request.Request(git_search_url)
75 try:
76 response = urllib.request.urlopen(git_req)
Felix Elsnerc1c2d752018-11-25 12:46:13 +010077 except urllib.request.HTTPError:
cybojenix3e873402013-10-17 03:34:57 +040078 print("There was an issue connecting to gerrit."
Felix Elsnerc1c2d752018-11-25 12:46:13 +010079 " Please try again in a minute")
80 except urllib.request.URLError:
cybojenix3e873402013-10-17 03:34:57 +040081 print("WARNING: No network connection available.")
82 else:
83 # Skip silly gerrit "header"
84 response.readline()
85 git_data = json.load(response)
86 device_data = check_repo_exists(git_data, device)
87 print("found the {} device repo".format(device))
88 return device_data
89
90
91def parse_device_directory(device_url, device):
Vachounetb99cad92021-02-18 14:44:04 +010092 if device.count("_") < 2:
93 pattern = "^android_device_(?P<vendor>.+)_{}$".format(device)
94 else:
95 pattern = "^android_device_{}$".format(device)
96
cybojenix3e873402013-10-17 03:34:57 +040097 match = re.match(pattern, device_url)
98
99 if match is None:
100 raise Exception("Invalid project name {}".format(device_url))
Vachounetb99cad92021-02-18 14:44:04 +0100101
102 if device.count('_') < 2:
103 return "device/{vendor}/{device}".format(
104 vendor=match.group('vendor'),
105 device=device,
106 )
107 else:
108 vendor = device.split('_')[0]
109 return "device/{vendor}/{device}".format(
110 vendor=vendor,
111 device=device,
112 )
cybojenix3e873402013-10-17 03:34:57 +0400113
114
115# Thank you RaYmAn
116def iterate_manifests():
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()
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100126 except (IOError, ES.ParseError):
cybojenix3e873402013-10-17 03:34:57 +0400127 print("WARNING: error while parsing %s" % file)
128 else:
129 for project in man.findall("project"):
130 yield project
131
132
maxwen52025a82019-05-08 17:04:42 +0200133def iterate_manifests_remove_project():
134 files = []
135 for file in os.listdir(local_manifest_dir):
136 if file.endswith(".xml"):
137 files.append(os.path.join(local_manifest_dir, file))
138 files.append('.repo/manifest.xml')
139 for file in files:
140 try:
141 man = ES.parse(file)
142 man = man.getroot()
143 except (IOError, ES.ParseError):
144 print("WARNING: error while parsing %s" % file)
145 else:
146 for project in man.findall("remove-project"):
147 yield project
148
cybojenix3e873402013-10-17 03:34:57 +0400149def check_project_exists(url, revision, path):
150 for project in iterate_manifests():
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100151 if project.get("name") == url \
152 and project.get("revision") == revision \
153 and project.get("path") == path:
cybojenix3e873402013-10-17 03:34:57 +0400154 return True
155 return False
156
157
maxwen52025a82019-05-08 17:04:42 +0200158def check_remove_project_exists(url):
159 for project in iterate_manifests_remove_project():
160 if project.get("name") == url:
161 return True
162 return False
163
cybojenix3e873402013-10-17 03:34:57 +0400164def check_target_exists(directory):
165 return os.path.isdir(directory)
166
167
168# Use the indent function from http://stackoverflow.com/a/4590052
169def indent(elem, level=0):
170 i = ''.join(["\n", level*" "])
171 if len(elem):
172 if not elem.text or not elem.text.strip():
173 elem.text = ''.join([i, " "])
174 if not elem.tail or not elem.tail.strip():
175 elem.tail = i
176 for elem in elem:
177 indent(elem, level+1)
178 if not elem.tail or not elem.tail.strip():
179 elem.tail = i
180 else:
181 if level and (not elem.tail or not elem.tail.strip()):
182 elem.tail = i
183
184
185def create_manifest_project(url, directory,
186 remote=default_rem,
187 revision=default_rev):
188 project_exists = check_project_exists(url, revision, directory)
189
190 if project_exists:
191 return None
192
193 project = ES.Element("project",
194 attrib={
195 "path": directory,
196 "name": url,
197 "remote": remote,
198 "revision": revision
199 })
200 return project
201
maxwen52025a82019-05-08 17:04:42 +0200202def create_remove_project(url):
203 remove_project_exists = check_remove_project_exists(url)
204
205 if remove_project_exists:
206 return None
207
208 project = ES.Element("remove-project",
209 attrib={
210 "name": url
211 })
212 return project
cybojenix3e873402013-10-17 03:34:57 +0400213
214def append_to_manifest(project):
215 try:
216 lm = ES.parse('/'.join([local_manifest_dir, "roomservice.xml"]))
217 lm = lm.getroot()
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100218 except (IOError, ES.ParseError):
cybojenix3e873402013-10-17 03:34:57 +0400219 lm = ES.Element("manifest")
220 lm.append(project)
221 return lm
222
223
224def write_to_manifest(manifest):
225 indent(manifest)
226 raw_xml = ES.tostring(manifest).decode()
227 raw_xml = ''.join(['<?xml version="1.0" encoding="UTF-8"?>\n'
228 '<!--Please do not manually edit this file-->\n',
229 raw_xml])
230
231 with open('/'.join([local_manifest_dir, "roomservice.xml"]), 'w') as f:
232 f.write(raw_xml)
233 print("wrote the new roomservice manifest")
234
235
236def parse_device_from_manifest(device):
237 for project in iterate_manifests():
238 name = project.get('name')
239 if name.startswith("android_device_") and name.endswith(device):
240 return project.get('path')
241 return None
242
243
244def parse_device_from_folder(device):
245 search = []
maxwenc8a6b6c2017-12-05 01:37:48 +0100246 if not os.path.isdir("device"):
247 os.mkdir("device")
cybojenix3e873402013-10-17 03:34:57 +0400248 for sub_folder in os.listdir("device"):
249 if os.path.isdir("device/%s/%s" % (sub_folder, device)):
250 search.append("device/%s/%s" % (sub_folder, device))
251 if len(search) > 1:
252 print("multiple devices under the name %s. "
253 "defaulting to checking the manifest" % device)
254 location = parse_device_from_manifest(device)
255 elif len(search) == 1:
256 location = search[0]
257 else:
258 print("Your device can't be found in device sources..")
259 location = parse_device_from_manifest(device)
260 return location
261
262
263def parse_dependency_file(location):
264 dep_file = "omni.dependencies"
265 dep_location = '/'.join([location, dep_file])
266 if not os.path.isfile(dep_location):
267 print("WARNING: %s file not found" % dep_location)
268 sys.exit()
269 try:
270 with open(dep_location, 'r') as f:
271 dependencies = json.loads(f.read())
272 except ValueError:
273 raise Exception("ERROR: malformed dependency file")
274 return dependencies
275
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100276
cybojenix3e873402013-10-17 03:34:57 +0400277# if there is any conflict with existing and new
278# delete the roomservice.xml file and create new
279def check_manifest_problems(dependencies):
280 for dependency in dependencies:
281 repository = dependency.get("repository")
282 target_path = dependency.get("target_path")
283 revision = dependency.get("revision", default_rev)
cybojenix3e873402013-10-17 03:34:57 +0400284
285 # check for existing projects
286 for project in iterate_manifests():
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100287 if project.get("revision") is not None \
288 and project.get("path") is not None \
289 and project.get("path") == target_path \
290 and project.get("revision") != revision:
291 print("WARNING: detected conflict in revisions for repository ",
292 repository)
293 current_dependency = str(project.get(repository))
294 file = ES.parse('/'.join([local_manifest_dir,
295 "roomservice.xml"]))
296 file_root = file.getroot()
297 for current_project in file_root.findall('project'):
298 new_dependency = str(current_project.find('revision'))
299 if new_dependency == current_dependency:
300 file_root.remove(current_project)
301 file.write('/'.join([local_manifest_dir, "roomservice.xml"]))
302 return
303
cybojenix3e873402013-10-17 03:34:57 +0400304
305def create_dependency_manifest(dependencies):
306 projects = []
307 for dependency in dependencies:
308 repository = dependency.get("repository")
309 target_path = dependency.get("target_path")
310 revision = dependency.get("revision", default_rev)
311 remote = dependency.get("remote", default_rem)
maxwen52025a82019-05-08 17:04:42 +0200312 override = dependency.get("override", None)
maxwen2ef6de32020-02-18 12:35:40 +0100313 remove = dependency.get("remove", None)
314
315 if remove is not None:
316 #print("found remove in ", repository)
317 project = create_remove_project(remove)
maxwen52025a82019-05-08 17:04:42 +0200318 if project is not None:
319 manifest = append_to_manifest(project)
320 #print(ES.tostring(manifest).decode())
321 write_to_manifest(manifest)
maxwen2ef6de32020-02-18 12:35:40 +0100322 else:
323 if override is not None:
324 #print("found override in ", repository)
325 project = create_remove_project(override)
326 if project is not None:
327 manifest = append_to_manifest(project)
328 #print(ES.tostring(manifest).decode())
329 write_to_manifest(manifest)
cybojenix3e873402013-10-17 03:34:57 +0400330
maxwen2ef6de32020-02-18 12:35:40 +0100331 # not adding an organization should default to android_team
332 # only apply this to github
333 if remote == "github":
334 if "/" not in repository:
335 repository = '/'.join([android_team, repository])
336 project = create_manifest_project(repository,
337 target_path,
338 remote=remote,
339 revision=revision)
340 if project is not None:
341 manifest = append_to_manifest(project)
342 write_to_manifest(manifest)
343 projects.append(target_path)
cybojenix3e873402013-10-17 03:34:57 +0400344 if len(projects) > 0:
maxwen9361cd02021-01-24 16:49:45 +0100345 os.system("repo sync --force-sync --no-clone-bundle %s" % " ".join(projects))
cybojenix3e873402013-10-17 03:34:57 +0400346
347
348def create_common_dependencies_manifest(dependencies):
349 dep_file = "omni.dependencies"
350 common_list = []
351 if dependencies is not None:
352 for dependency in dependencies:
353 try:
354 index = common_list.index(dependency['target_path'])
355 except ValueError:
356 index = None
357 if index is None:
358 common_list.append(dependency['target_path'])
359 dep_location = '/'.join([dependency['target_path'], dep_file])
360 if not os.path.isfile(dep_location):
361 sys.exit()
362 else:
363 try:
364 with open(dep_location, 'r') as f:
365 common_deps = json.loads(f.read())
366 except ValueError:
367 raise Exception("ERROR: malformed dependency file")
368
369 if common_deps is not None:
370 print("Looking for dependencies on: ",
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100371 dependency['target_path'])
cybojenix3e873402013-10-17 03:34:57 +0400372 check_manifest_problems(common_deps)
373 create_dependency_manifest(common_deps)
374 create_common_dependencies_manifest(common_deps)
375
376
377def fetch_dependencies(device):
Vachounet9ef51682019-11-23 18:56:32 +0100378 if device == "emulator":
maxwen3a275aa2020-11-27 23:40:31 +0100379 location = "vendor/omni/utils/omni_emulator"
Vachounet9ef51682019-11-23 18:56:32 +0100380 else:
381 location = parse_device_from_folder(device)
cybojenix3e873402013-10-17 03:34:57 +0400382 if location is None or not os.path.isdir(location):
383 raise Exception("ERROR: could not find your device "
384 "folder location, bailing out")
385 dependencies = parse_dependency_file(location)
386 check_manifest_problems(dependencies)
387 create_dependency_manifest(dependencies)
388 create_common_dependencies_manifest(dependencies)
389 fetch_device(device)
390
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100391
cybojenix3e873402013-10-17 03:34:57 +0400392def check_device_exists(device):
393 location = parse_device_from_folder(device)
394 if location is None:
395 return False
396 return os.path.isdir(location)
397
398
399def fetch_device(device):
400 if check_device_exists(device):
Marko Man718f4c22019-07-14 12:12:25 +0100401 print("WARNING: Trying to fetch a device that's already there")
cybojenix3e873402013-10-17 03:34:57 +0400402 git_data = search_gerrit_for_device(device)
403 if git_data is not None:
404 device_url = git_data['id']
405 device_dir = parse_device_directory(device_url, device)
406 project = create_manifest_project(device_url,
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100407 device_dir,
408 remote=default_team_rem)
cybojenix3e873402013-10-17 03:34:57 +0400409 if project is not None:
410 manifest = append_to_manifest(project)
411 write_to_manifest(manifest)
412 # In case a project was written to manifest, but never synced
413 if project is not None or not check_target_exists(device_dir):
414 print("syncing the device config")
maxwen9361cd02021-01-24 16:49:45 +0100415 os.system('repo sync --force-sync --no-clone-bundle %s' % device_dir)
cybojenix3e873402013-10-17 03:34:57 +0400416
417
418if __name__ == '__main__':
419 if not os.path.isdir(local_manifest_dir):
420 os.mkdir(local_manifest_dir)
421
422 product = sys.argv[1]
423 try:
424 device = product[product.index("_") + 1:]
425 except ValueError:
426 device = product
427
428 if len(sys.argv) > 2:
429 deps_only = sys.argv[2]
430 else:
431 deps_only = False
432
433 if not deps_only:
434 fetch_device(device)
435 fetch_dependencies(device)