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