blob: 8cc81c546894be3020c8d117157ca603266ddec3 [file] [log] [blame]
LuK1337f8a64942024-02-14 09:06:18 +01001#!/usr/bin/env python3
cybojenix3e873402013-10-17 03:34:57 +04002
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
LuK1337728729b2024-02-14 09:22:34 +010027import urllib.request
cybojenix3e873402013-10-17 03:34:57 +040028
29# Config
30# set this to the default remote to use in repo
Marko Man9401bfe2020-04-15 22:16:16 +020031default_rem = "omnirom2"
cybojenix3e873402013-10-17 03:34:57 +040032# set this to the default revision to use (branch/tag name)
micky3872a4c2292024-09-09 15:01:33 -040033default_rev = "android-15"
cybojenix3e873402013-10-17 03:34:57 +040034# set this to the remote that you use for projects from your team repos
35# example fetch="https://github.com/omnirom"
Marko Manba650f32020-04-17 00:29:51 +020036default_team_rem = "omnirom2"
cybojenix3e873402013-10-17 03:34:57 +040037# this shouldn't change unless google makes changes
38local_manifest_dir = ".repo/local_manifests"
39# change this to your name on github (or equivalent hosting)
40android_team = "omnirom"
41# url to gerrit repository
42gerrit_url = "gerrit.omnirom.org"
43
44
45def check_repo_exists(git_data, device):
Vachounetb99cad92021-02-18 14:44:04 +010046 if device.count("_") < 2:
47 re_match = "^android_device_.*_{device}$".format(device=device)
48 else:
49 re_match = "^android_device.*_{device}$".format(device=device)
50
Felix Elsner67fdf892018-12-04 21:45:49 +010051 matches = list(filter(lambda x: re.match(re_match, x), git_data))
Vachounetb99cad92021-02-18 14:44:04 +010052
cybojenix3e873402013-10-17 03:34:57 +040053 if len(matches) != 1:
54 raise Exception("{device} not found,"
55 "exiting roomservice".format(device=device))
56
57 return git_data[matches[0]]
58
59
60def search_gerrit_for_device(device):
61 # TODO: In next gerrit release regex search with r= should be supported!
62 git_search_url = "https://{gerrit_url}/projects/?m={device}".format(
63 gerrit_url=gerrit_url,
64 device=device
65 )
66 git_req = urllib.request.Request(git_search_url)
67 try:
68 response = urllib.request.urlopen(git_req)
Felix Elsnerc1c2d752018-11-25 12:46:13 +010069 except urllib.request.HTTPError:
cybojenix3e873402013-10-17 03:34:57 +040070 print("There was an issue connecting to gerrit."
Felix Elsnerc1c2d752018-11-25 12:46:13 +010071 " Please try again in a minute")
72 except urllib.request.URLError:
cybojenix3e873402013-10-17 03:34:57 +040073 print("WARNING: No network connection available.")
74 else:
75 # Skip silly gerrit "header"
76 response.readline()
77 git_data = json.load(response)
78 device_data = check_repo_exists(git_data, device)
79 print("found the {} device repo".format(device))
80 return device_data
81
82
83def parse_device_directory(device_url, device):
Vachounetb99cad92021-02-18 14:44:04 +010084 if device.count("_") < 2:
85 pattern = "^android_device_(?P<vendor>.+)_{}$".format(device)
86 else:
87 pattern = "^android_device_{}$".format(device)
88
cybojenix3e873402013-10-17 03:34:57 +040089 match = re.match(pattern, device_url)
90
91 if match is None:
92 raise Exception("Invalid project name {}".format(device_url))
Vachounetb99cad92021-02-18 14:44:04 +010093
94 if device.count('_') < 2:
95 return "device/{vendor}/{device}".format(
96 vendor=match.group('vendor'),
97 device=device,
98 )
99 else:
100 vendor = device.split('_')[0]
101 return "device/{vendor}/{device}".format(
102 vendor=vendor,
103 device=device,
104 )
cybojenix3e873402013-10-17 03:34:57 +0400105
106
107# Thank you RaYmAn
108def iterate_manifests():
109 files = []
110 for file in os.listdir(local_manifest_dir):
111 if file.endswith(".xml"):
112 files.append(os.path.join(local_manifest_dir, file))
113 files.append('.repo/manifest.xml')
114 for file in files:
115 try:
116 man = ES.parse(file)
117 man = man.getroot()
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100118 except (IOError, ES.ParseError):
cybojenix3e873402013-10-17 03:34:57 +0400119 print("WARNING: error while parsing %s" % file)
120 else:
121 for project in man.findall("project"):
122 yield project
123
124
maxwen52025a82019-05-08 17:04:42 +0200125def iterate_manifests_remove_project():
126 files = []
127 for file in os.listdir(local_manifest_dir):
128 if file.endswith(".xml"):
129 files.append(os.path.join(local_manifest_dir, file))
130 files.append('.repo/manifest.xml')
131 for file in files:
132 try:
133 man = ES.parse(file)
134 man = man.getroot()
135 except (IOError, ES.ParseError):
136 print("WARNING: error while parsing %s" % file)
137 else:
138 for project in man.findall("remove-project"):
139 yield project
140
cybojenix3e873402013-10-17 03:34:57 +0400141def check_project_exists(url, revision, path):
142 for project in iterate_manifests():
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100143 if project.get("name") == url \
144 and project.get("revision") == revision \
145 and project.get("path") == path:
cybojenix3e873402013-10-17 03:34:57 +0400146 return True
147 return False
148
149
maxwen52025a82019-05-08 17:04:42 +0200150def check_remove_project_exists(url):
151 for project in iterate_manifests_remove_project():
152 if project.get("name") == url:
153 return True
154 return False
155
cybojenix3e873402013-10-17 03:34:57 +0400156def check_target_exists(directory):
157 return os.path.isdir(directory)
158
159
160# Use the indent function from http://stackoverflow.com/a/4590052
161def indent(elem, level=0):
162 i = ''.join(["\n", level*" "])
163 if len(elem):
164 if not elem.text or not elem.text.strip():
165 elem.text = ''.join([i, " "])
166 if not elem.tail or not elem.tail.strip():
167 elem.tail = i
168 for elem in elem:
169 indent(elem, level+1)
170 if not elem.tail or not elem.tail.strip():
171 elem.tail = i
172 else:
173 if level and (not elem.tail or not elem.tail.strip()):
174 elem.tail = i
175
176
177def create_manifest_project(url, directory,
178 remote=default_rem,
179 revision=default_rev):
180 project_exists = check_project_exists(url, revision, directory)
181
182 if project_exists:
183 return None
184
185 project = ES.Element("project",
186 attrib={
187 "path": directory,
188 "name": url,
189 "remote": remote,
190 "revision": revision
191 })
192 return project
193
maxwen52025a82019-05-08 17:04:42 +0200194def create_remove_project(url):
195 remove_project_exists = check_remove_project_exists(url)
196
197 if remove_project_exists:
198 return None
199
200 project = ES.Element("remove-project",
201 attrib={
202 "name": url
203 })
204 return project
cybojenix3e873402013-10-17 03:34:57 +0400205
206def append_to_manifest(project):
207 try:
208 lm = ES.parse('/'.join([local_manifest_dir, "roomservice.xml"]))
209 lm = lm.getroot()
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100210 except (IOError, ES.ParseError):
cybojenix3e873402013-10-17 03:34:57 +0400211 lm = ES.Element("manifest")
212 lm.append(project)
213 return lm
214
215
216def write_to_manifest(manifest):
217 indent(manifest)
218 raw_xml = ES.tostring(manifest).decode()
219 raw_xml = ''.join(['<?xml version="1.0" encoding="UTF-8"?>\n'
220 '<!--Please do not manually edit this file-->\n',
221 raw_xml])
222
223 with open('/'.join([local_manifest_dir, "roomservice.xml"]), 'w') as f:
224 f.write(raw_xml)
225 print("wrote the new roomservice manifest")
226
227
228def parse_device_from_manifest(device):
229 for project in iterate_manifests():
230 name = project.get('name')
231 if name.startswith("android_device_") and name.endswith(device):
232 return project.get('path')
233 return None
234
235
236def parse_device_from_folder(device):
237 search = []
maxwenc8a6b6c2017-12-05 01:37:48 +0100238 if not os.path.isdir("device"):
239 os.mkdir("device")
cybojenix3e873402013-10-17 03:34:57 +0400240 for sub_folder in os.listdir("device"):
241 if os.path.isdir("device/%s/%s" % (sub_folder, device)):
242 search.append("device/%s/%s" % (sub_folder, device))
243 if len(search) > 1:
244 print("multiple devices under the name %s. "
245 "defaulting to checking the manifest" % device)
246 location = parse_device_from_manifest(device)
247 elif len(search) == 1:
248 location = search[0]
249 else:
250 print("Your device can't be found in device sources..")
251 location = parse_device_from_manifest(device)
252 return location
253
254
255def parse_dependency_file(location):
256 dep_file = "omni.dependencies"
257 dep_location = '/'.join([location, dep_file])
258 if not os.path.isfile(dep_location):
259 print("WARNING: %s file not found" % dep_location)
260 sys.exit()
261 try:
262 with open(dep_location, 'r') as f:
263 dependencies = json.loads(f.read())
264 except ValueError:
265 raise Exception("ERROR: malformed dependency file")
266 return dependencies
267
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100268
cybojenix3e873402013-10-17 03:34:57 +0400269# if there is any conflict with existing and new
270# delete the roomservice.xml file and create new
271def check_manifest_problems(dependencies):
272 for dependency in dependencies:
273 repository = dependency.get("repository")
274 target_path = dependency.get("target_path")
275 revision = dependency.get("revision", default_rev)
cybojenix3e873402013-10-17 03:34:57 +0400276
277 # check for existing projects
278 for project in iterate_manifests():
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100279 if project.get("revision") is not None \
280 and project.get("path") is not None \
281 and project.get("path") == target_path \
282 and project.get("revision") != revision:
283 print("WARNING: detected conflict in revisions for repository ",
284 repository)
285 current_dependency = str(project.get(repository))
286 file = ES.parse('/'.join([local_manifest_dir,
287 "roomservice.xml"]))
288 file_root = file.getroot()
289 for current_project in file_root.findall('project'):
290 new_dependency = str(current_project.find('revision'))
291 if new_dependency == current_dependency:
292 file_root.remove(current_project)
293 file.write('/'.join([local_manifest_dir, "roomservice.xml"]))
294 return
295
cybojenix3e873402013-10-17 03:34:57 +0400296
297def create_dependency_manifest(dependencies):
298 projects = []
299 for dependency in dependencies:
300 repository = dependency.get("repository")
301 target_path = dependency.get("target_path")
302 revision = dependency.get("revision", default_rev)
303 remote = dependency.get("remote", default_rem)
maxwen52025a82019-05-08 17:04:42 +0200304 override = dependency.get("override", None)
maxwen2ef6de32020-02-18 12:35:40 +0100305 remove = dependency.get("remove", None)
306
307 if remove is not None:
308 #print("found remove in ", repository)
309 project = create_remove_project(remove)
maxwen52025a82019-05-08 17:04:42 +0200310 if project is not None:
311 manifest = append_to_manifest(project)
312 #print(ES.tostring(manifest).decode())
313 write_to_manifest(manifest)
maxwen2ef6de32020-02-18 12:35:40 +0100314 else:
315 if override is not None:
316 #print("found override in ", repository)
317 project = create_remove_project(override)
318 if project is not None:
319 manifest = append_to_manifest(project)
320 #print(ES.tostring(manifest).decode())
321 write_to_manifest(manifest)
cybojenix3e873402013-10-17 03:34:57 +0400322
maxwen2ef6de32020-02-18 12:35:40 +0100323 # not adding an organization should default to android_team
324 # only apply this to github
325 if remote == "github":
326 if "/" not in repository:
327 repository = '/'.join([android_team, repository])
328 project = create_manifest_project(repository,
329 target_path,
330 remote=remote,
331 revision=revision)
332 if project is not None:
333 manifest = append_to_manifest(project)
334 write_to_manifest(manifest)
335 projects.append(target_path)
max.weningereec135d2024-01-20 23:29:03 +0000336 if len(projects) > 0:
337 os.system("repo sync --force-sync --no-clone-bundle %s" % " ".join(projects))
cybojenix3e873402013-10-17 03:34:57 +0400338
339
340def create_common_dependencies_manifest(dependencies):
341 dep_file = "omni.dependencies"
342 common_list = []
343 if dependencies is not None:
344 for dependency in dependencies:
345 try:
346 index = common_list.index(dependency['target_path'])
347 except ValueError:
348 index = None
349 if index is None:
350 common_list.append(dependency['target_path'])
351 dep_location = '/'.join([dependency['target_path'], dep_file])
352 if not os.path.isfile(dep_location):
353 sys.exit()
354 else:
355 try:
356 with open(dep_location, 'r') as f:
357 common_deps = json.loads(f.read())
358 except ValueError:
359 raise Exception("ERROR: malformed dependency file")
360
361 if common_deps is not None:
362 print("Looking for dependencies on: ",
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100363 dependency['target_path'])
cybojenix3e873402013-10-17 03:34:57 +0400364 check_manifest_problems(common_deps)
365 create_dependency_manifest(common_deps)
366 create_common_dependencies_manifest(common_deps)
367
368
369def fetch_dependencies(device):
Vachounet9ef51682019-11-23 18:56:32 +0100370 if device == "emulator":
maxwen3a275aa2020-11-27 23:40:31 +0100371 location = "vendor/omni/utils/omni_emulator"
Vachounet9ef51682019-11-23 18:56:32 +0100372 else:
373 location = parse_device_from_folder(device)
cybojenix3e873402013-10-17 03:34:57 +0400374 if location is None or not os.path.isdir(location):
375 raise Exception("ERROR: could not find your device "
376 "folder location, bailing out")
377 dependencies = parse_dependency_file(location)
378 check_manifest_problems(dependencies)
379 create_dependency_manifest(dependencies)
380 create_common_dependencies_manifest(dependencies)
381 fetch_device(device)
382
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100383
cybojenix3e873402013-10-17 03:34:57 +0400384def check_device_exists(device):
385 location = parse_device_from_folder(device)
386 if location is None:
387 return False
388 return os.path.isdir(location)
389
390
391def fetch_device(device):
392 if check_device_exists(device):
Marko Man718f4c22019-07-14 12:12:25 +0100393 print("WARNING: Trying to fetch a device that's already there")
cybojenix3e873402013-10-17 03:34:57 +0400394 git_data = search_gerrit_for_device(device)
395 if git_data is not None:
396 device_url = git_data['id']
397 device_dir = parse_device_directory(device_url, device)
398 project = create_manifest_project(device_url,
Felix Elsnerc1c2d752018-11-25 12:46:13 +0100399 device_dir,
400 remote=default_team_rem)
cybojenix3e873402013-10-17 03:34:57 +0400401 if project is not None:
402 manifest = append_to_manifest(project)
403 write_to_manifest(manifest)
404 # In case a project was written to manifest, but never synced
max.weningereec135d2024-01-20 23:29:03 +0000405 if project is not None or not check_target_exists(device_dir):
406 print("syncing the device config")
407 os.system('repo sync --force-sync --no-clone-bundle %s' % device_dir)
cybojenix3e873402013-10-17 03:34:57 +0400408
409
410if __name__ == '__main__':
411 if not os.path.isdir(local_manifest_dir):
412 os.mkdir(local_manifest_dir)
413
414 product = sys.argv[1]
415 try:
416 device = product[product.index("_") + 1:]
417 except ValueError:
418 device = product
419
420 if len(sys.argv) > 2:
421 deps_only = sys.argv[2]
422 else:
423 deps_only = False
424
425 if not deps_only:
426 fetch_device(device)
427 fetch_dependencies(device)