Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/Tools/scripts/generate_manifest.py
Views: 1798
#!/usr/bin/env python12from __future__ import print_function34import copy5import fnmatch6import gen_stable7import gzip8import json9import os10import re11import subprocess12import shutil13import sys1415if sys.version_info[0] < 3:16running_python3 = False17running_python310 = False18elif sys.version_info[1] < 10:19running_python3 = True20running_python310 = False21else:22running_python3 = True23running_python310 = True2425FIRMWARE_TYPES = ["AntennaTracker", "Copter", "Plane", "Rover", "Sub", "AP_Periph", "Blimp"]26RELEASE_TYPES = ["beta", "beta-4.3", "latest", "stable", "stable-*", "dirty"]2728# mapping for board names to brand name and manufacturer29brand_map = {30'Pixhawk4' : ('Pixhawk 4', 'Holybro'),31'Pixhawk4-bdshot' : ('Pixhawk 4', 'Holybro'),32'Pix32v5' : ('Pix32 v5', 'Holybro'),33'Durandal' : ('Durandal', 'Holybro'),34'Durandal-bdshot' : ('Durandal', 'Holybro'),35'PH4-mini' : ('Pixhawk 4 Mini', 'Holybro'),36'KakuteF4' : ('KakuteF4', 'Holybro'),37'KakuteF7' : ('KakuteF7', 'Holybro'),38'KakuteF7Mini' : ('KakuteF7Mini', 'Holybro'),39'KakuteF4Mini' : ('KakuteF4Mini', 'Holybro'),40'KakuteH7Mini' : ('KakuteH7Mini', 'Holybro'),41'KakuteH7Mini-Nand' : ('KakuteH7Mini-Nand', 'Holybro'),42'KakuteH7' : ('KakuteH7', 'Holybro'),43'KakuteH7-bdshot' : ('KakuteH7', 'Holybro'),44'KakuteH7v2' : ('KakuteH7v2', 'Holybro'),45'CubeBlack' : ('CubeBlack', 'Hex/ProfiCNC'),46'CubeYellow' : ('CubeYellow', 'Hex/ProfiCNC'),47'CubeOrange' : ('CubeOrange', 'Hex/ProfiCNC'),48'CubeOrange-bdshot' : ('CubeOrange', 'Hex/ProfiCNC'),49'CubePurple' : ('CubePurple', 'Hex/ProfiCNC'),50'CubeSolo' : ('CubeSolo', '3DR'),51'CubeGreen-solo' : ('CubeGreen Solo', 'Hex/ProfiCNC'),52'CUAVv5' : ('CUAVv5', 'CUAV'),53'CUAVv5-bdshot' : ('CUAVv5', 'CUAV'),54'CUAVv5Nano' : ('CUAVv5 Nano', 'CUAV'),55'CUAVv5Nano-bdshot' : ('CUAVv5 Nano', 'CUAV'),56'CUAV-Nora' : ('CUAV Nora', 'CUAV'),57'CUAV-Nora-bdshot' : ('CUAV Nora', 'CUAV'),58'CUAV-X7' : ('CUAV X7', 'CUAV'),59'CUAV-X7-bdshot' : ('CUAV X7', 'CUAV'),60'DrotekP3Pro' : ('Pixhawk 3 Pro', 'Drotek'),61'MambaF405v2' : ('Diatone Mamba F405 MK2', 'Diatone'),62'MatekF405' : ('Matek F405', 'Matek'),63'MatekF405-bdshot' : ('Matek F405', 'Matek'),64'MatekF405-STD' : ('Matek F405 STD', 'Matek'),65'MatekF405-Wing' : ('Matek F405 Wing', 'Matek'),66'MatekF765-SE' : ('Matek F765 SE', 'Matek'),67'MatekH743' : ('Matek H743', 'Matek'),68'MatekH743-bdshot' : ('Matek H743', 'Matek'),69'mini-pix' : ('MiniPix', 'Radiolink'),70'Pixhawk1' : ('Pixhawk1', 'mRobotics'),71'Pixracer' : ('PixRacer', 'mRobotics'),72'Pixracer-bdshot' : ('PixRacer', 'mRobotics'),73'mRoX21' : ('mRo X2.1', 'mRobotics'),74'mRoX21-777' : ('mRo X2.1-777', 'mRobotics'),75'mRoPixracerPro' : ('mRo PixracerPro', 'mRobotics'),76'mRoPixracerPro-bdshot' : ('mRo PixracerPro', 'mRobotics'),77'mRoControlZeroOEMH7' : ('mRo ControlZero OEM H7', 'mRobotics'),78'mRoNexus' : ('mRo Nexus', 'mRobotics'),79'TBS-Colibri-F7' : ('Colibri F7', 'TBS'),80'sparky2' : ('Sparky2', 'TauLabs'),81'mindpx-v2' : ('MindPX V2', 'AirMind'),82'OMNIBUSF7V2' : ('Omnibus F7 V2', 'Airbot'),83'omnibusf4pro' : ('Omnibus F4 Pro', 'Airbot'),84'omnibusf4pro-bdshot' : ('Omnibus F4 Pro', 'Airbot'),85'omnibusf4v6' : ('Omnibus F4 V6', 'Airbot'),86'OmnibusNanoV6' : ('Omnibus Nano V6', 'Airbot'),87'OmnibusNanoV6-bdshot' : ('Omnibus Nano V6', 'Airbot'),88'speedybeef4' : ('SpeedyBee F4', 'SpeedyBee'),89'speedybeef4v3' : ('SpeedyBee F4 v3', 'SpeedyBee'),90'QioTekZealotF427' : ('ZealotF427', 'QioTek'),91'BeastH7' : ('Beast H7 55A AIO', 'iFlight'),92'BeastH7v2' : ('Beast H7 v2 55A AIO', 'iFlight'),93'BeastF7' : ('Beast F7 45A AIO', 'iFlight'),94'BeastF7v2' : ('Beast F7 v2 55A AIO', 'iFlight'),95'MambaF405US-I2C' : ('Diatone Mamba Basic F405 MK3/MK3.5', 'Diatone'),96'MambaF405-2022' : ('Diatone Mamba Basic F405 MK4', 'Diatone'),97'MambaH743v4' : ('Diatone Mamba H743 MK4', 'Diatone'),98"FlywooF745" : ('Flywoo Goku GN 745 AIO', 'Flywoo'),99"FlywooF745Nano" : ('Flywoo Goku Hex F745', 'Flywoo'),100"modalai_fc-v1" : ('ModalAI FlightCore v1', 'ModalAI'),101'Pixhawk5X' : ('Pixhawk 5X', 'Holybro'),102"AIRLink" : ("Sky-Drones Technologies", "AIRLink"),103"SPRacingH7" : ("Seriously Pro Racing", "H7 Extreme"),104"SkystarsH7HD" : ("Skystars", "H743 HD"),105"SkystarsH7HD-bdshot" : ("Skystars", "H743 HD"),106"MicoAir405v2" : ("MicoAir F405 v2.1", "MicoAir"),107"MicoAir405Mini" : ("MicoAir F405 Mini", "MicoAir"),108"MicoAir743" : ("MicoAir H743 v1.3", "MicoAir"),109"MicoAir743-AIO" : ("MicoAir H743 AIO", "MicoAir"),110"MicoAir743v2" : ("MicoAir H743 v2.0", "MicoAir"),111"GEPRCF745BTHD": ("TAKER F745 BT","GEPRC"),112"GEPRC_TAKER_H743": ("TAKER H743 BT","GEPRC"),113}114115class Firmware():116def __init__(self,117date=None,118platform=None,119vehicletype=None,120filepath=None,121git_sha=None,122frame=None):123self.atts = dict()124self.atts["date"] = date125self.atts["platform"] = platform126self.atts["vehicletype"] = vehicletype127self.atts["filepath"] = filepath128self.atts["git_sha"] = git_sha129self.atts["firmware-version-str"] = ""130self.atts["frame"] = frame131self.atts["release-type"] = None132self.atts["firmware-version"] = None133134def __getitem__(self, what):135return self.atts[what]136137def __setitem__(self, name, value):138self.atts[name] = value139140141class ManifestGenerator():142'''Return a JSON string describing "binary" directory contents under143basedir'''144145def __init__(self, basedir, baseurl):146self.basedir = basedir147self.baseurl = baseurl148149def frame_map(self, frame):150'''translate from ArduPilot frame type terminology into mavlink151terminology'''152frame_to_mavlink_dict = {153"quad": "QUADROTOR",154"hexa": "HEXAROTOR",155"y6": "ARDUPILOT_Y6",156"tri": "TRICOPTER",157"octa": "OCTOROTOR",158"octa-quad": "ARDUPILOT_OCTAQUAD",159"deca": "DECAROTOR",160"heli": "HELICOPTER",161"Plane": "FIXED_WING",162"AntennaTracker": "ANTENNA_TRACKER",163"Rover": "GROUND_ROVER",164"Sub": "SUBMARINE",165"AP_Periph": "CAN_PERIPHERAL",166}167if frame in frame_to_mavlink_dict:168return frame_to_mavlink_dict[frame]169170return frame171172def releasetype_map(self, releasetype):173'''translate from ArduPilot release type terminology into mavlink174terminology'''175if releasetype == 'stable':176return 'OFFICIAL'177return releasetype.upper()178179def looks_like_binaries_directory(self, dir):180'''returns True if dir looks like it is a build_binaries.py output181directory'''182for entry in os.listdir(dir):183if entry in FIRMWARE_TYPES:184return True185return False186187def git_sha_from_git_version(self, filepath):188'''parses get-version.txt (as emitted by build_binaries.py, returns189git sha from it'''190content = open(filepath).read()191sha_regex = re.compile("commit (?P<sha>[0-9a-f]+)")192m = sha_regex.search(content)193if m is None:194raise Exception(195"filepath (%s) does not contain a git sha" % (filepath,))196return m.group("sha")197198def fwversion_from_git_version(self, filepath):199'''parses get-version.txt (as emitted by build_binaries.py, returns200git sha from it'''201content = open(filepath).read()202sha_regex = re.compile("APMVERSION: \S+\s+(\S+)")203m = sha_regex.search(content)204if m is None:205raise Exception(206"filepath (%s) does not contain an APMVERSION" % (filepath,))207return m.group(1)208209def add_USB_IDs_PX4(self, firmware):210'''add USB IDs to a .px4 firmware'''211url = firmware['url']212suffix = url.split('-')[-1]213if suffix == "v1.px4":214firmware['USBID'] = ['0x26AC/0x0010']215firmware['board_id'] = 5216firmware['bootloader_str'] = ['PX4 BL FMU v1.x']217elif suffix in ["v2.px4", "v3.px4"]:218firmware['USBID'] = ['0x26AC/0x0011']219firmware['board_id'] = 9220firmware['bootloader_str'] = ['PX4 BL FMU v2.x']221elif suffix == "v4.px4":222firmware['USBID'] = ['0x26AC/0x0012']223firmware['board_id'] = 11224firmware['bootloader_str'] = ['PX4 BL FMU v4.x']225elif suffix == "v4pro.px4":226firmware['USBID'] = ['0x26AC/0x0013']227firmware['board_id'] = 13228firmware['bootloader_str'] = ['PX4 BL FMU v4.x PRO']229230def add_USB_IDs_ChibiOS(self, firmware):231'''add USB IDs to a ChbiOS apj firmware'''232url = firmware['url'][len(self.baseurl)+1:]233apj_path = os.path.join(self.basedir, url)234if not os.path.exists(apj_path):235print("bad apj path %s" % apj_path, file=sys.stderr)236return237apj_json = json.load(open(apj_path, 'r'))238if 'board_id' not in apj_json:239print("no board_id in %s" % apj_path, file=sys.stderr)240return241if 'platform' not in firmware:242print("no platform for %s" % apj_path, file=sys.stderr)243return244board_id = apj_json['board_id']245platform = firmware['platform']246247# all ChibiOS builds can have platform as bootloader_str and board_id from248# hwdef.dat249firmware['board_id'] = board_id250firmware['bootloader_str'] = [platform+"-BL"]251252# map of vendor specific USB IDs253USBID_MAP = {254'CubeBlack': ['0x2DAE/0x1011'],255'CubeOrange': ['0x2DAE/0x1016', '0x2DAE/0x1017'],256'CubePurple': ['0x2DAE/0x1005'],257'CubeYellow': ['0x2DAE/0x1002'],258'Pixhawk4': ['0x3162/0x0047'],259'PH4-mini': ['0x3162/0x0049'],260'Durandal': ['0x3162/0x004B'],261'VRBrain-v51': ['0x27AC/0x1151'],262'VRBrain-v52': ['0x27AC/0x1152'],263'VRBrain-v54': ['0x27AC/0x1154'],264'VRCore-v10': ['0x27AC/0x1910'],265'VRUBrain-v51': ['0x27AC/0x1351']266}267if 'USBID' in apj_json:268# newer APJ files have USBID in the json data269firmware['USBID'] = [apj_json['USBID']]270elif platform in USBID_MAP:271firmware['USBID'] = USBID_MAP[platform]272else:273# all others use a single USB VID/PID274firmware['USBID'] = ['0x0483/0x5740']275276if board_id == 50:277# special case for FMUv5, they always get the px4 bootloader IDs as an option278firmware['bootloader_str'].append('PX4 BL FMU v5.x')279firmware['USBID'].append('0x26AC/0x0032')280281if board_id == 9:282# special case for FMUv3, they always get the px4 bootloader IDs as an option283firmware['bootloader_str'].append('PX4 BL FMU v2.x')284firmware['USBID'].append('0x26AC/0x0011')285286if board_id == 11:287# special case for FMUv4, they always get the px4 bootloader IDs as an option288firmware['bootloader_str'].append('PX4 BL FMU v4.x')289firmware['USBID'].append('0x26AC/0x0012')290291if board_id == 13:292# special case for FMUv4pro, they always get the px4 bootloader IDs as an option293firmware['bootloader_str'].append('PX4 BL FMU v4.x PRO')294firmware['USBID'].append('0x26AC/0x0013')295296if board_id == 88:297# special case for MindPX-v2 boards298firmware['bootloader_str'].append('MindPX BL FMU v2.x')299firmware['USBID'].append('0x26AC/0x0030')300301if board_id == 53:302# special case for 6X, they always get the px4 bootloader IDs as an option303firmware['bootloader_str'].append('PX4 BL FMU v6X.x')304firmware['USBID'].append('0x3185/0x0035')305306if board_id == 56:307# special case for 6C, they always get the px4 bootloader IDs as an option308firmware['bootloader_str'].append('PX4 BL FMU v6C.x')309firmware['USBID'].append('0x3185/0x0038')310311if platform in brand_map:312(brand_name, manufacturer) = brand_map[platform]313firmware['brand_name'] = brand_name314firmware['manufacturer'] = manufacturer315316# copy over some extra information if available317extra_tags = [ 'image_size', 'brand_name', 'manufacturer' ]318for tag in extra_tags:319if tag in apj_json:320firmware[tag] = apj_json[tag]321322def add_USB_IDs(self, firmware):323'''add USB IDs to a firmware'''324fmt = firmware['format']325if fmt == "px4":326self.add_USB_IDs_PX4(firmware)327return328if fmt == "apj":329self.add_USB_IDs_ChibiOS(firmware)330return331332def firmware_format_for_filepath(self, filepath):333filename = os.path.basename(filepath)334if "." in filename:335return "".join(filename.split(".")[-1:])336# no extension; ensure this is an elf:337text = subprocess.check_output(["file", "-b", filepath])338if running_python3:339text = text.decode('ascii')340341if re.match("^ELF", text):342return "ELF"343print("Unknown file type (%s)" % filepath)344print("Got: %s" % text)345return "Unknown" # should raise an error somehow346347def add_firmware_data_from_dir(self,348dir,349firmware_data,350vehicletype,351releasetype="dev"):352'''accumulate additional information about firmwares from directory'''353variant_firmware_regex = re.compile("[^-]+-(?P<variant>v\d+)[.px4]")354if not os.path.isdir(dir):355return356try:357dlist = os.listdir(dir)358except Exception:359print("Error listing '%s'" % dir)360return361for platformdir in dlist:362if platformdir.startswith("."):363continue364some_dir = os.path.join(dir, platformdir)365if not os.path.isdir(some_dir):366continue367git_version_txt = os.path.join(some_dir, "git-version.txt")368if not os.path.exists(git_version_txt):369print("No file %s" % git_version_txt, file=sys.stderr)370continue371try:372git_sha = self.git_sha_from_git_version(git_version_txt)373except Exception as ex:374print("Failed to parse %s" % git_version_txt, ex, file=sys.stderr)375continue376try:377fwversion_str = self.fwversion_from_git_version(git_version_txt)378except Exception as ex:379print("Failed to parse APMVERSION %s" % git_version_txt, ex, file=sys.stderr)380continue381382# we require a firmware-version.txt. These files have been added to383# old builds that didn't have them384firmware_version_file = os.path.join(some_dir,385"firmware-version.txt")386if not os.path.exists(firmware_version_file):387print("Missing %s" % firmware_version_file, file=sys.stderr)388continue389390try:391firmware_version = open(firmware_version_file).read()392firmware_version = firmware_version.strip()393(_, _) = firmware_version.split("-")394except ValueError:395print("malformed firmware-version.txt at (%s)" % (firmware_version_file,), file=sys.stderr)396continue397except Exception as ex:398print("bad file %s" % firmware_version_file, file=sys.stderr)399# this exception is swallowed.... the current archive400# is incomplete.401continue402403# Directory names for heli builds end in -heli404platform_frame_regex = re.compile("(?P<board>.+)(-(?P<frame>heli)$)")405m = platform_frame_regex.match(platformdir)406if m is not None:407# This is a heli build408platform = m.group("board") # e.g. navio409frame = "heli"410else:411# Non-heli build412frame = vehicletype # e.g. Plane413platform = platformdir # e.g. apm2414415# also gather information from any features.txt files present:416features_text = None417features_filepath = os.path.join(some_dir, "features.txt")418if os.path.exists(features_filepath):419features_text = sorted(open(features_filepath).read().rstrip().split("\n"))420421for filename in os.listdir(some_dir):422if filename in ["git-version.txt", "firmware-version.txt", "files.html", "features.txt"]:423continue424if filename.startswith("."):425continue426427m = variant_firmware_regex.match(filename)428if m:429# the platform variant is430# encoded in the firmware filename431# (e.g. the "v1" in432# ArduCopter-v1.px4)433variant = m.group("variant")434file_platform = "-".join([platform, variant])435else:436file_platform = platform437438filepath = os.path.join(some_dir, filename)439firmware_format = self.firmware_format_for_filepath(filepath)440if firmware_format not in [ "elf", "ELF", "abin", "apj", "hex", "px4", "bin" ]:441print("Unknown firmware format (%s)" % firmware_format)442443firmware = Firmware()444445# translate from supplied "release type" into both a446# "latest" flag and an actual release type. Also sort447# out which filepath we should use:448firmware["latest"] = 0449if releasetype == "dev":450if firmware["filepath"] is None:451firmware["filepath"] = filepath452if firmware["release-type"] is None:453firmware["release-type"] = "dev"454elif releasetype == "latest":455firmware["latest"] = 1456firmware["filepath"] = filepath457if firmware["release-type"] is None:458firmware["release-type"] = "dev"459else:460if (not firmware["latest"]):461firmware["filepath"] = filepath462firmware["release-type"] = releasetype463464firmware["platform"] = file_platform465firmware["vehicletype"] = vehicletype466firmware["git_sha"] = git_sha467firmware["firmware-version-str"] = fwversion_str468firmware["frame"] = frame469firmware["timestamp"] = os.path.getctime(firmware["filepath"])470firmware["format"] = firmware_format471firmware["firmware-version"] = firmware_version472473firmware["features"] = features_text474475firmware_data.append(firmware)476477def valid_release_type(self, tag):478'''check for valid release type'''479for r in RELEASE_TYPES:480if fnmatch.fnmatch(tag, r):481return True482return False483484def parse_fw_version(self, version):485(version_numbers, release_type) = version.split("-")486(major, minor, patch) = version_numbers.split(".")487return (major, minor, patch, version)488489def walk_directory(self, basedir):490'''walks directory structure created by build_binaries, returns Python491structure representing releases in that structure'''492year_month_regex = re.compile("(?P<year>\d{4})-(?P<month>\d{2})")493494firmwares = []495496# used to listdir basedir here, but since this is also a web497# document root, there's a lot of other stuff accumulated...498vehicletypes = FIRMWARE_TYPES499for vehicletype in vehicletypes:500try:501# the sort means we prefer 'stable' to 'stable-x.y.z' when they502# both contain the same contents503vdir = sorted(os.listdir(os.path.join(basedir, vehicletype)), reverse=True)504except OSError as e:505if e.errno == 2:506continue507for firstlevel in vdir:508if firstlevel == "files.html" or firstlevel.startswith("."):509# generated file which should be ignored510continue511# skip any non-directories (e.g. "files.html"):512if year_month_regex.match(firstlevel):513# this is a dated directory e.g. binaries/Copter/2016-02514# we do not include dated directories in the manifest ATM:515continue516517# assume this is a release directory such as518# "beta", or the "latest" directory (treated as a519# release and handled specially later)520tag = firstlevel521if not self.valid_release_type(tag):522print("Unknown tag (%s) in directory (%s)" %523(tag, os.path.join(vdir)), file=sys.stderr)524continue525tag_path = os.path.join(basedir, vehicletype, tag)526if not os.path.isdir(tag_path):527continue528self.add_firmware_data_from_dir(tag_path,529firmwares,530vehicletype,531releasetype=tag)532533# convert from ardupilot-naming conventions to common JSON format:534firmware_json = []535features_json = [] # a structure containing summarised features per firmware536537for firmware in firmwares:538filepath = firmware["filepath"]539# replace the base directory with the base URL540urlifier = re.compile("^" + re.escape(basedir))541url = re.sub(urlifier, self.baseurl, filepath)542version_type = self.releasetype_map(firmware["release-type"])543some_json = dict({544"mav-autopilot": "ARDUPILOTMEGA",545"vehicletype": firmware["vehicletype"],546"platform": firmware["platform"],547"git-sha": firmware["git_sha"],548"url": url,549"mav-type": self.frame_map(firmware["frame"]),550"mav-firmware-version-type": version_type,551"mav-firmware-version-str": firmware["firmware-version-str"],552"latest": firmware["latest"],553"format": firmware["format"],554})555556if firmware["firmware-version"]:557try:558(major, minor, patch, release_type) = self.parse_fw_version(559firmware["firmware-version"])560except Exception:561print("Badly formed firmware-version.txt %s" % firmware["firmware-version"], file=sys.stderr)562continue563some_json["mav-firmware-version"] = ".".join([major,564minor,565patch])566some_json["mav-firmware-version-major"] = major567some_json["mav-firmware-version-minor"] = minor568some_json["mav-firmware-version-patch"] = patch569570self.add_USB_IDs(some_json)571572#print(some_json['url'])573firmware_json.append(some_json)574575# now the features the firmware supports...576try:577features = firmware["features"]578# check apj here in case we're creating bin and apj etc:579if (firmware["format"] == "apj" and580features is not None and581bool(firmware["latest"])):582x = dict({583"vehicletype": firmware["vehicletype"],584"platform": firmware["platform"],585"git-sha": firmware["git_sha"],586"latest": firmware["latest"],587})588x["features"] = features589features_json.append(x)590591except KeyError:592pass593594ret = {595"format-version": "1.0.0", # semantic versioning596"firmware": firmware_json597}598599features_ret = {600"format-version": "1.0.0", # semantic versioning601"features": features_json602}603604return ret, features_ret605606def run(self):607'''walk directory supplied in constructor, record results in self'''608if not self.looks_like_binaries_directory(self.basedir):609print("Warning: this does not look like a binaries directory",610file=sys.stderr)611612self.structure, self.features_structure = self.walk_directory(self.basedir)613614def json(self):615'''returns JSON string for version information for all firmwares'''616if getattr(self, 'structure', None) is None:617self.run()618return json.dumps(self.structure, indent=4, separators=(',', ': '))619620def json_features(self):621'''returns JSON string for supported features for all firmwares.622run() method must have been called already'''623return json.dumps(self.features_structure, indent=4, separators=(',', ': '))624625def write_string_to_filepath(self, string, filepath):626'''writes the entirety of string to filepath'''627with open(filepath, "w") as x:628x.write(string)629630def write_json(self, content, path):631'''write content to path, also creating a compress .gz version'''632new_json_filepath = path + ".new"633self.write_string_to_filepath(content, new_json_filepath)634# provide a pre-compressed version. For reference, a 7M manifest635# "gzip -9"s to 300k in 1 second, "xz -e"s to 80k in 26 seconds636new_json_filepath_gz = path + ".gz.new"637with gzip.open(new_json_filepath_gz, 'wb') as gf:638if running_python3:639content = bytes(content, 'ascii')640gf.write(content)641gf.close()642shutil.move(new_json_filepath, path)643shutil.move(new_json_filepath_gz, path + ".gz")644645def write_manifest_json(self, path):646'''write generated JSON content to path'''647self.write_json(self.json(), path)648649def write_features_json(self, path):650'''write generated features JSON content to path'''651self.write_json(self.json_features(), path)652653def usage():654return '''Usage:655generate-manifest.py basedir'''656657658if __name__ == "__main__":659import argparse660parser = argparse.ArgumentParser(description='generate manifest.json')661662parser.add_argument('--outfile', type=str, default=None, help='output file, default stdout')663parser.add_argument('--outfile-features-json', type=str, default=None, help='output file for features json file')664parser.add_argument('--baseurl', type=str, default="https://firmware.ardupilot.org", help='base binaries directory')665parser.add_argument('basedir', type=str, default="-", help='base binaries directory')666667args = parser.parse_args()668669# ensure all stable directories are created670gen_stable.make_all_stable(args.basedir)671672generator = ManifestGenerator(args.basedir, args.baseurl)673generator.run()674675content = generator.json()676if args.outfile is None:677print(content)678else:679generator.write_manifest_json(args.outfile)680681if args.outfile_features_json is not None:682generator.write_features_json(args.outfile_features_json)683684685