CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/scripts/generate_manifest.py
Views: 1798
1
#!/usr/bin/env python
2
3
from __future__ import print_function
4
5
import copy
6
import fnmatch
7
import gen_stable
8
import gzip
9
import json
10
import os
11
import re
12
import subprocess
13
import shutil
14
import sys
15
16
if sys.version_info[0] < 3:
17
running_python3 = False
18
running_python310 = False
19
elif sys.version_info[1] < 10:
20
running_python3 = True
21
running_python310 = False
22
else:
23
running_python3 = True
24
running_python310 = True
25
26
FIRMWARE_TYPES = ["AntennaTracker", "Copter", "Plane", "Rover", "Sub", "AP_Periph", "Blimp"]
27
RELEASE_TYPES = ["beta", "beta-4.3", "latest", "stable", "stable-*", "dirty"]
28
29
# mapping for board names to brand name and manufacturer
30
brand_map = {
31
'Pixhawk4' : ('Pixhawk 4', 'Holybro'),
32
'Pixhawk4-bdshot' : ('Pixhawk 4', 'Holybro'),
33
'Pix32v5' : ('Pix32 v5', 'Holybro'),
34
'Durandal' : ('Durandal', 'Holybro'),
35
'Durandal-bdshot' : ('Durandal', 'Holybro'),
36
'PH4-mini' : ('Pixhawk 4 Mini', 'Holybro'),
37
'KakuteF4' : ('KakuteF4', 'Holybro'),
38
'KakuteF7' : ('KakuteF7', 'Holybro'),
39
'KakuteF7Mini' : ('KakuteF7Mini', 'Holybro'),
40
'KakuteF4Mini' : ('KakuteF4Mini', 'Holybro'),
41
'KakuteH7Mini' : ('KakuteH7Mini', 'Holybro'),
42
'KakuteH7Mini-Nand' : ('KakuteH7Mini-Nand', 'Holybro'),
43
'KakuteH7' : ('KakuteH7', 'Holybro'),
44
'KakuteH7-bdshot' : ('KakuteH7', 'Holybro'),
45
'KakuteH7v2' : ('KakuteH7v2', 'Holybro'),
46
'CubeBlack' : ('CubeBlack', 'Hex/ProfiCNC'),
47
'CubeYellow' : ('CubeYellow', 'Hex/ProfiCNC'),
48
'CubeOrange' : ('CubeOrange', 'Hex/ProfiCNC'),
49
'CubeOrange-bdshot' : ('CubeOrange', 'Hex/ProfiCNC'),
50
'CubePurple' : ('CubePurple', 'Hex/ProfiCNC'),
51
'CubeSolo' : ('CubeSolo', '3DR'),
52
'CubeGreen-solo' : ('CubeGreen Solo', 'Hex/ProfiCNC'),
53
'CUAVv5' : ('CUAVv5', 'CUAV'),
54
'CUAVv5-bdshot' : ('CUAVv5', 'CUAV'),
55
'CUAVv5Nano' : ('CUAVv5 Nano', 'CUAV'),
56
'CUAVv5Nano-bdshot' : ('CUAVv5 Nano', 'CUAV'),
57
'CUAV-Nora' : ('CUAV Nora', 'CUAV'),
58
'CUAV-Nora-bdshot' : ('CUAV Nora', 'CUAV'),
59
'CUAV-X7' : ('CUAV X7', 'CUAV'),
60
'CUAV-X7-bdshot' : ('CUAV X7', 'CUAV'),
61
'DrotekP3Pro' : ('Pixhawk 3 Pro', 'Drotek'),
62
'MambaF405v2' : ('Diatone Mamba F405 MK2', 'Diatone'),
63
'MatekF405' : ('Matek F405', 'Matek'),
64
'MatekF405-bdshot' : ('Matek F405', 'Matek'),
65
'MatekF405-STD' : ('Matek F405 STD', 'Matek'),
66
'MatekF405-Wing' : ('Matek F405 Wing', 'Matek'),
67
'MatekF765-SE' : ('Matek F765 SE', 'Matek'),
68
'MatekH743' : ('Matek H743', 'Matek'),
69
'MatekH743-bdshot' : ('Matek H743', 'Matek'),
70
'mini-pix' : ('MiniPix', 'Radiolink'),
71
'Pixhawk1' : ('Pixhawk1', 'mRobotics'),
72
'Pixracer' : ('PixRacer', 'mRobotics'),
73
'Pixracer-bdshot' : ('PixRacer', 'mRobotics'),
74
'mRoX21' : ('mRo X2.1', 'mRobotics'),
75
'mRoX21-777' : ('mRo X2.1-777', 'mRobotics'),
76
'mRoPixracerPro' : ('mRo PixracerPro', 'mRobotics'),
77
'mRoPixracerPro-bdshot' : ('mRo PixracerPro', 'mRobotics'),
78
'mRoControlZeroOEMH7' : ('mRo ControlZero OEM H7', 'mRobotics'),
79
'mRoNexus' : ('mRo Nexus', 'mRobotics'),
80
'TBS-Colibri-F7' : ('Colibri F7', 'TBS'),
81
'sparky2' : ('Sparky2', 'TauLabs'),
82
'mindpx-v2' : ('MindPX V2', 'AirMind'),
83
'OMNIBUSF7V2' : ('Omnibus F7 V2', 'Airbot'),
84
'omnibusf4pro' : ('Omnibus F4 Pro', 'Airbot'),
85
'omnibusf4pro-bdshot' : ('Omnibus F4 Pro', 'Airbot'),
86
'omnibusf4v6' : ('Omnibus F4 V6', 'Airbot'),
87
'OmnibusNanoV6' : ('Omnibus Nano V6', 'Airbot'),
88
'OmnibusNanoV6-bdshot' : ('Omnibus Nano V6', 'Airbot'),
89
'speedybeef4' : ('SpeedyBee F4', 'SpeedyBee'),
90
'speedybeef4v3' : ('SpeedyBee F4 v3', 'SpeedyBee'),
91
'QioTekZealotF427' : ('ZealotF427', 'QioTek'),
92
'BeastH7' : ('Beast H7 55A AIO', 'iFlight'),
93
'BeastH7v2' : ('Beast H7 v2 55A AIO', 'iFlight'),
94
'BeastF7' : ('Beast F7 45A AIO', 'iFlight'),
95
'BeastF7v2' : ('Beast F7 v2 55A AIO', 'iFlight'),
96
'MambaF405US-I2C' : ('Diatone Mamba Basic F405 MK3/MK3.5', 'Diatone'),
97
'MambaF405-2022' : ('Diatone Mamba Basic F405 MK4', 'Diatone'),
98
'MambaH743v4' : ('Diatone Mamba H743 MK4', 'Diatone'),
99
"FlywooF745" : ('Flywoo Goku GN 745 AIO', 'Flywoo'),
100
"FlywooF745Nano" : ('Flywoo Goku Hex F745', 'Flywoo'),
101
"modalai_fc-v1" : ('ModalAI FlightCore v1', 'ModalAI'),
102
'Pixhawk5X' : ('Pixhawk 5X', 'Holybro'),
103
"AIRLink" : ("Sky-Drones Technologies", "AIRLink"),
104
"SPRacingH7" : ("Seriously Pro Racing", "H7 Extreme"),
105
"SkystarsH7HD" : ("Skystars", "H743 HD"),
106
"SkystarsH7HD-bdshot" : ("Skystars", "H743 HD"),
107
"MicoAir405v2" : ("MicoAir F405 v2.1", "MicoAir"),
108
"MicoAir405Mini" : ("MicoAir F405 Mini", "MicoAir"),
109
"MicoAir743" : ("MicoAir H743 v1.3", "MicoAir"),
110
"MicoAir743-AIO" : ("MicoAir H743 AIO", "MicoAir"),
111
"MicoAir743v2" : ("MicoAir H743 v2.0", "MicoAir"),
112
"GEPRCF745BTHD": ("TAKER F745 BT","GEPRC"),
113
"GEPRC_TAKER_H743": ("TAKER H743 BT","GEPRC"),
114
}
115
116
class Firmware():
117
def __init__(self,
118
date=None,
119
platform=None,
120
vehicletype=None,
121
filepath=None,
122
git_sha=None,
123
frame=None):
124
self.atts = dict()
125
self.atts["date"] = date
126
self.atts["platform"] = platform
127
self.atts["vehicletype"] = vehicletype
128
self.atts["filepath"] = filepath
129
self.atts["git_sha"] = git_sha
130
self.atts["firmware-version-str"] = ""
131
self.atts["frame"] = frame
132
self.atts["release-type"] = None
133
self.atts["firmware-version"] = None
134
135
def __getitem__(self, what):
136
return self.atts[what]
137
138
def __setitem__(self, name, value):
139
self.atts[name] = value
140
141
142
class ManifestGenerator():
143
'''Return a JSON string describing "binary" directory contents under
144
basedir'''
145
146
def __init__(self, basedir, baseurl):
147
self.basedir = basedir
148
self.baseurl = baseurl
149
150
def frame_map(self, frame):
151
'''translate from ArduPilot frame type terminology into mavlink
152
terminology'''
153
frame_to_mavlink_dict = {
154
"quad": "QUADROTOR",
155
"hexa": "HEXAROTOR",
156
"y6": "ARDUPILOT_Y6",
157
"tri": "TRICOPTER",
158
"octa": "OCTOROTOR",
159
"octa-quad": "ARDUPILOT_OCTAQUAD",
160
"deca": "DECAROTOR",
161
"heli": "HELICOPTER",
162
"Plane": "FIXED_WING",
163
"AntennaTracker": "ANTENNA_TRACKER",
164
"Rover": "GROUND_ROVER",
165
"Sub": "SUBMARINE",
166
"AP_Periph": "CAN_PERIPHERAL",
167
}
168
if frame in frame_to_mavlink_dict:
169
return frame_to_mavlink_dict[frame]
170
171
return frame
172
173
def releasetype_map(self, releasetype):
174
'''translate from ArduPilot release type terminology into mavlink
175
terminology'''
176
if releasetype == 'stable':
177
return 'OFFICIAL'
178
return releasetype.upper()
179
180
def looks_like_binaries_directory(self, dir):
181
'''returns True if dir looks like it is a build_binaries.py output
182
directory'''
183
for entry in os.listdir(dir):
184
if entry in FIRMWARE_TYPES:
185
return True
186
return False
187
188
def git_sha_from_git_version(self, filepath):
189
'''parses get-version.txt (as emitted by build_binaries.py, returns
190
git sha from it'''
191
content = open(filepath).read()
192
sha_regex = re.compile("commit (?P<sha>[0-9a-f]+)")
193
m = sha_regex.search(content)
194
if m is None:
195
raise Exception(
196
"filepath (%s) does not contain a git sha" % (filepath,))
197
return m.group("sha")
198
199
def fwversion_from_git_version(self, filepath):
200
'''parses get-version.txt (as emitted by build_binaries.py, returns
201
git sha from it'''
202
content = open(filepath).read()
203
sha_regex = re.compile("APMVERSION: \S+\s+(\S+)")
204
m = sha_regex.search(content)
205
if m is None:
206
raise Exception(
207
"filepath (%s) does not contain an APMVERSION" % (filepath,))
208
return m.group(1)
209
210
def add_USB_IDs_PX4(self, firmware):
211
'''add USB IDs to a .px4 firmware'''
212
url = firmware['url']
213
suffix = url.split('-')[-1]
214
if suffix == "v1.px4":
215
firmware['USBID'] = ['0x26AC/0x0010']
216
firmware['board_id'] = 5
217
firmware['bootloader_str'] = ['PX4 BL FMU v1.x']
218
elif suffix in ["v2.px4", "v3.px4"]:
219
firmware['USBID'] = ['0x26AC/0x0011']
220
firmware['board_id'] = 9
221
firmware['bootloader_str'] = ['PX4 BL FMU v2.x']
222
elif suffix == "v4.px4":
223
firmware['USBID'] = ['0x26AC/0x0012']
224
firmware['board_id'] = 11
225
firmware['bootloader_str'] = ['PX4 BL FMU v4.x']
226
elif suffix == "v4pro.px4":
227
firmware['USBID'] = ['0x26AC/0x0013']
228
firmware['board_id'] = 13
229
firmware['bootloader_str'] = ['PX4 BL FMU v4.x PRO']
230
231
def add_USB_IDs_ChibiOS(self, firmware):
232
'''add USB IDs to a ChbiOS apj firmware'''
233
url = firmware['url'][len(self.baseurl)+1:]
234
apj_path = os.path.join(self.basedir, url)
235
if not os.path.exists(apj_path):
236
print("bad apj path %s" % apj_path, file=sys.stderr)
237
return
238
apj_json = json.load(open(apj_path, 'r'))
239
if 'board_id' not in apj_json:
240
print("no board_id in %s" % apj_path, file=sys.stderr)
241
return
242
if 'platform' not in firmware:
243
print("no platform for %s" % apj_path, file=sys.stderr)
244
return
245
board_id = apj_json['board_id']
246
platform = firmware['platform']
247
248
# all ChibiOS builds can have platform as bootloader_str and board_id from
249
# hwdef.dat
250
firmware['board_id'] = board_id
251
firmware['bootloader_str'] = [platform+"-BL"]
252
253
# map of vendor specific USB IDs
254
USBID_MAP = {
255
'CubeBlack': ['0x2DAE/0x1011'],
256
'CubeOrange': ['0x2DAE/0x1016', '0x2DAE/0x1017'],
257
'CubePurple': ['0x2DAE/0x1005'],
258
'CubeYellow': ['0x2DAE/0x1002'],
259
'Pixhawk4': ['0x3162/0x0047'],
260
'PH4-mini': ['0x3162/0x0049'],
261
'Durandal': ['0x3162/0x004B'],
262
'VRBrain-v51': ['0x27AC/0x1151'],
263
'VRBrain-v52': ['0x27AC/0x1152'],
264
'VRBrain-v54': ['0x27AC/0x1154'],
265
'VRCore-v10': ['0x27AC/0x1910'],
266
'VRUBrain-v51': ['0x27AC/0x1351']
267
}
268
if 'USBID' in apj_json:
269
# newer APJ files have USBID in the json data
270
firmware['USBID'] = [apj_json['USBID']]
271
elif platform in USBID_MAP:
272
firmware['USBID'] = USBID_MAP[platform]
273
else:
274
# all others use a single USB VID/PID
275
firmware['USBID'] = ['0x0483/0x5740']
276
277
if board_id == 50:
278
# special case for FMUv5, they always get the px4 bootloader IDs as an option
279
firmware['bootloader_str'].append('PX4 BL FMU v5.x')
280
firmware['USBID'].append('0x26AC/0x0032')
281
282
if board_id == 9:
283
# special case for FMUv3, they always get the px4 bootloader IDs as an option
284
firmware['bootloader_str'].append('PX4 BL FMU v2.x')
285
firmware['USBID'].append('0x26AC/0x0011')
286
287
if board_id == 11:
288
# special case for FMUv4, they always get the px4 bootloader IDs as an option
289
firmware['bootloader_str'].append('PX4 BL FMU v4.x')
290
firmware['USBID'].append('0x26AC/0x0012')
291
292
if board_id == 13:
293
# special case for FMUv4pro, they always get the px4 bootloader IDs as an option
294
firmware['bootloader_str'].append('PX4 BL FMU v4.x PRO')
295
firmware['USBID'].append('0x26AC/0x0013')
296
297
if board_id == 88:
298
# special case for MindPX-v2 boards
299
firmware['bootloader_str'].append('MindPX BL FMU v2.x')
300
firmware['USBID'].append('0x26AC/0x0030')
301
302
if board_id == 53:
303
# special case for 6X, they always get the px4 bootloader IDs as an option
304
firmware['bootloader_str'].append('PX4 BL FMU v6X.x')
305
firmware['USBID'].append('0x3185/0x0035')
306
307
if board_id == 56:
308
# special case for 6C, they always get the px4 bootloader IDs as an option
309
firmware['bootloader_str'].append('PX4 BL FMU v6C.x')
310
firmware['USBID'].append('0x3185/0x0038')
311
312
if platform in brand_map:
313
(brand_name, manufacturer) = brand_map[platform]
314
firmware['brand_name'] = brand_name
315
firmware['manufacturer'] = manufacturer
316
317
# copy over some extra information if available
318
extra_tags = [ 'image_size', 'brand_name', 'manufacturer' ]
319
for tag in extra_tags:
320
if tag in apj_json:
321
firmware[tag] = apj_json[tag]
322
323
def add_USB_IDs(self, firmware):
324
'''add USB IDs to a firmware'''
325
fmt = firmware['format']
326
if fmt == "px4":
327
self.add_USB_IDs_PX4(firmware)
328
return
329
if fmt == "apj":
330
self.add_USB_IDs_ChibiOS(firmware)
331
return
332
333
def firmware_format_for_filepath(self, filepath):
334
filename = os.path.basename(filepath)
335
if "." in filename:
336
return "".join(filename.split(".")[-1:])
337
# no extension; ensure this is an elf:
338
text = subprocess.check_output(["file", "-b", filepath])
339
if running_python3:
340
text = text.decode('ascii')
341
342
if re.match("^ELF", text):
343
return "ELF"
344
print("Unknown file type (%s)" % filepath)
345
print("Got: %s" % text)
346
return "Unknown" # should raise an error somehow
347
348
def add_firmware_data_from_dir(self,
349
dir,
350
firmware_data,
351
vehicletype,
352
releasetype="dev"):
353
'''accumulate additional information about firmwares from directory'''
354
variant_firmware_regex = re.compile("[^-]+-(?P<variant>v\d+)[.px4]")
355
if not os.path.isdir(dir):
356
return
357
try:
358
dlist = os.listdir(dir)
359
except Exception:
360
print("Error listing '%s'" % dir)
361
return
362
for platformdir in dlist:
363
if platformdir.startswith("."):
364
continue
365
some_dir = os.path.join(dir, platformdir)
366
if not os.path.isdir(some_dir):
367
continue
368
git_version_txt = os.path.join(some_dir, "git-version.txt")
369
if not os.path.exists(git_version_txt):
370
print("No file %s" % git_version_txt, file=sys.stderr)
371
continue
372
try:
373
git_sha = self.git_sha_from_git_version(git_version_txt)
374
except Exception as ex:
375
print("Failed to parse %s" % git_version_txt, ex, file=sys.stderr)
376
continue
377
try:
378
fwversion_str = self.fwversion_from_git_version(git_version_txt)
379
except Exception as ex:
380
print("Failed to parse APMVERSION %s" % git_version_txt, ex, file=sys.stderr)
381
continue
382
383
# we require a firmware-version.txt. These files have been added to
384
# old builds that didn't have them
385
firmware_version_file = os.path.join(some_dir,
386
"firmware-version.txt")
387
if not os.path.exists(firmware_version_file):
388
print("Missing %s" % firmware_version_file, file=sys.stderr)
389
continue
390
391
try:
392
firmware_version = open(firmware_version_file).read()
393
firmware_version = firmware_version.strip()
394
(_, _) = firmware_version.split("-")
395
except ValueError:
396
print("malformed firmware-version.txt at (%s)" % (firmware_version_file,), file=sys.stderr)
397
continue
398
except Exception as ex:
399
print("bad file %s" % firmware_version_file, file=sys.stderr)
400
# this exception is swallowed.... the current archive
401
# is incomplete.
402
continue
403
404
# Directory names for heli builds end in -heli
405
platform_frame_regex = re.compile("(?P<board>.+)(-(?P<frame>heli)$)")
406
m = platform_frame_regex.match(platformdir)
407
if m is not None:
408
# This is a heli build
409
platform = m.group("board") # e.g. navio
410
frame = "heli"
411
else:
412
# Non-heli build
413
frame = vehicletype # e.g. Plane
414
platform = platformdir # e.g. apm2
415
416
# also gather information from any features.txt files present:
417
features_text = None
418
features_filepath = os.path.join(some_dir, "features.txt")
419
if os.path.exists(features_filepath):
420
features_text = sorted(open(features_filepath).read().rstrip().split("\n"))
421
422
for filename in os.listdir(some_dir):
423
if filename in ["git-version.txt", "firmware-version.txt", "files.html", "features.txt"]:
424
continue
425
if filename.startswith("."):
426
continue
427
428
m = variant_firmware_regex.match(filename)
429
if m:
430
# the platform variant is
431
# encoded in the firmware filename
432
# (e.g. the "v1" in
433
# ArduCopter-v1.px4)
434
variant = m.group("variant")
435
file_platform = "-".join([platform, variant])
436
else:
437
file_platform = platform
438
439
filepath = os.path.join(some_dir, filename)
440
firmware_format = self.firmware_format_for_filepath(filepath)
441
if firmware_format not in [ "elf", "ELF", "abin", "apj", "hex", "px4", "bin" ]:
442
print("Unknown firmware format (%s)" % firmware_format)
443
444
firmware = Firmware()
445
446
# translate from supplied "release type" into both a
447
# "latest" flag and an actual release type. Also sort
448
# out which filepath we should use:
449
firmware["latest"] = 0
450
if releasetype == "dev":
451
if firmware["filepath"] is None:
452
firmware["filepath"] = filepath
453
if firmware["release-type"] is None:
454
firmware["release-type"] = "dev"
455
elif releasetype == "latest":
456
firmware["latest"] = 1
457
firmware["filepath"] = filepath
458
if firmware["release-type"] is None:
459
firmware["release-type"] = "dev"
460
else:
461
if (not firmware["latest"]):
462
firmware["filepath"] = filepath
463
firmware["release-type"] = releasetype
464
465
firmware["platform"] = file_platform
466
firmware["vehicletype"] = vehicletype
467
firmware["git_sha"] = git_sha
468
firmware["firmware-version-str"] = fwversion_str
469
firmware["frame"] = frame
470
firmware["timestamp"] = os.path.getctime(firmware["filepath"])
471
firmware["format"] = firmware_format
472
firmware["firmware-version"] = firmware_version
473
474
firmware["features"] = features_text
475
476
firmware_data.append(firmware)
477
478
def valid_release_type(self, tag):
479
'''check for valid release type'''
480
for r in RELEASE_TYPES:
481
if fnmatch.fnmatch(tag, r):
482
return True
483
return False
484
485
def parse_fw_version(self, version):
486
(version_numbers, release_type) = version.split("-")
487
(major, minor, patch) = version_numbers.split(".")
488
return (major, minor, patch, version)
489
490
def walk_directory(self, basedir):
491
'''walks directory structure created by build_binaries, returns Python
492
structure representing releases in that structure'''
493
year_month_regex = re.compile("(?P<year>\d{4})-(?P<month>\d{2})")
494
495
firmwares = []
496
497
# used to listdir basedir here, but since this is also a web
498
# document root, there's a lot of other stuff accumulated...
499
vehicletypes = FIRMWARE_TYPES
500
for vehicletype in vehicletypes:
501
try:
502
# the sort means we prefer 'stable' to 'stable-x.y.z' when they
503
# both contain the same contents
504
vdir = sorted(os.listdir(os.path.join(basedir, vehicletype)), reverse=True)
505
except OSError as e:
506
if e.errno == 2:
507
continue
508
for firstlevel in vdir:
509
if firstlevel == "files.html" or firstlevel.startswith("."):
510
# generated file which should be ignored
511
continue
512
# skip any non-directories (e.g. "files.html"):
513
if year_month_regex.match(firstlevel):
514
# this is a dated directory e.g. binaries/Copter/2016-02
515
# we do not include dated directories in the manifest ATM:
516
continue
517
518
# assume this is a release directory such as
519
# "beta", or the "latest" directory (treated as a
520
# release and handled specially later)
521
tag = firstlevel
522
if not self.valid_release_type(tag):
523
print("Unknown tag (%s) in directory (%s)" %
524
(tag, os.path.join(vdir)), file=sys.stderr)
525
continue
526
tag_path = os.path.join(basedir, vehicletype, tag)
527
if not os.path.isdir(tag_path):
528
continue
529
self.add_firmware_data_from_dir(tag_path,
530
firmwares,
531
vehicletype,
532
releasetype=tag)
533
534
# convert from ardupilot-naming conventions to common JSON format:
535
firmware_json = []
536
features_json = [] # a structure containing summarised features per firmware
537
538
for firmware in firmwares:
539
filepath = firmware["filepath"]
540
# replace the base directory with the base URL
541
urlifier = re.compile("^" + re.escape(basedir))
542
url = re.sub(urlifier, self.baseurl, filepath)
543
version_type = self.releasetype_map(firmware["release-type"])
544
some_json = dict({
545
"mav-autopilot": "ARDUPILOTMEGA",
546
"vehicletype": firmware["vehicletype"],
547
"platform": firmware["platform"],
548
"git-sha": firmware["git_sha"],
549
"url": url,
550
"mav-type": self.frame_map(firmware["frame"]),
551
"mav-firmware-version-type": version_type,
552
"mav-firmware-version-str": firmware["firmware-version-str"],
553
"latest": firmware["latest"],
554
"format": firmware["format"],
555
})
556
557
if firmware["firmware-version"]:
558
try:
559
(major, minor, patch, release_type) = self.parse_fw_version(
560
firmware["firmware-version"])
561
except Exception:
562
print("Badly formed firmware-version.txt %s" % firmware["firmware-version"], file=sys.stderr)
563
continue
564
some_json["mav-firmware-version"] = ".".join([major,
565
minor,
566
patch])
567
some_json["mav-firmware-version-major"] = major
568
some_json["mav-firmware-version-minor"] = minor
569
some_json["mav-firmware-version-patch"] = patch
570
571
self.add_USB_IDs(some_json)
572
573
#print(some_json['url'])
574
firmware_json.append(some_json)
575
576
# now the features the firmware supports...
577
try:
578
features = firmware["features"]
579
# check apj here in case we're creating bin and apj etc:
580
if (firmware["format"] == "apj" and
581
features is not None and
582
bool(firmware["latest"])):
583
x = dict({
584
"vehicletype": firmware["vehicletype"],
585
"platform": firmware["platform"],
586
"git-sha": firmware["git_sha"],
587
"latest": firmware["latest"],
588
})
589
x["features"] = features
590
features_json.append(x)
591
592
except KeyError:
593
pass
594
595
ret = {
596
"format-version": "1.0.0", # semantic versioning
597
"firmware": firmware_json
598
}
599
600
features_ret = {
601
"format-version": "1.0.0", # semantic versioning
602
"features": features_json
603
}
604
605
return ret, features_ret
606
607
def run(self):
608
'''walk directory supplied in constructor, record results in self'''
609
if not self.looks_like_binaries_directory(self.basedir):
610
print("Warning: this does not look like a binaries directory",
611
file=sys.stderr)
612
613
self.structure, self.features_structure = self.walk_directory(self.basedir)
614
615
def json(self):
616
'''returns JSON string for version information for all firmwares'''
617
if getattr(self, 'structure', None) is None:
618
self.run()
619
return json.dumps(self.structure, indent=4, separators=(',', ': '))
620
621
def json_features(self):
622
'''returns JSON string for supported features for all firmwares.
623
run() method must have been called already'''
624
return json.dumps(self.features_structure, indent=4, separators=(',', ': '))
625
626
def write_string_to_filepath(self, string, filepath):
627
'''writes the entirety of string to filepath'''
628
with open(filepath, "w") as x:
629
x.write(string)
630
631
def write_json(self, content, path):
632
'''write content to path, also creating a compress .gz version'''
633
new_json_filepath = path + ".new"
634
self.write_string_to_filepath(content, new_json_filepath)
635
# provide a pre-compressed version. For reference, a 7M manifest
636
# "gzip -9"s to 300k in 1 second, "xz -e"s to 80k in 26 seconds
637
new_json_filepath_gz = path + ".gz.new"
638
with gzip.open(new_json_filepath_gz, 'wb') as gf:
639
if running_python3:
640
content = bytes(content, 'ascii')
641
gf.write(content)
642
gf.close()
643
shutil.move(new_json_filepath, path)
644
shutil.move(new_json_filepath_gz, path + ".gz")
645
646
def write_manifest_json(self, path):
647
'''write generated JSON content to path'''
648
self.write_json(self.json(), path)
649
650
def write_features_json(self, path):
651
'''write generated features JSON content to path'''
652
self.write_json(self.json_features(), path)
653
654
def usage():
655
return '''Usage:
656
generate-manifest.py basedir'''
657
658
659
if __name__ == "__main__":
660
import argparse
661
parser = argparse.ArgumentParser(description='generate manifest.json')
662
663
parser.add_argument('--outfile', type=str, default=None, help='output file, default stdout')
664
parser.add_argument('--outfile-features-json', type=str, default=None, help='output file for features json file')
665
parser.add_argument('--baseurl', type=str, default="https://firmware.ardupilot.org", help='base binaries directory')
666
parser.add_argument('basedir', type=str, default="-", help='base binaries directory')
667
668
args = parser.parse_args()
669
670
# ensure all stable directories are created
671
gen_stable.make_all_stable(args.basedir)
672
673
generator = ManifestGenerator(args.basedir, args.baseurl)
674
generator.run()
675
676
content = generator.json()
677
if args.outfile is None:
678
print(content)
679
else:
680
generator.write_manifest_json(args.outfile)
681
682
if args.outfile_features_json is not None:
683
generator.write_features_json(args.outfile_features_json)
684
685