123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- import os
- import re
- import subprocess
- import sys
- from collections import defaultdict
- from collections import namedtuple
- Name = namedtuple('Name', 'name desc')
- Device = namedtuple('Device', 'name bus alias desc')
- Architecture = namedtuple('Architecture', 'name items default')
- TARGETS = [
- Name("alpha", "Alpha"),
- Name("arm", "ARM (aarch32)"),
- Name("aarch64", "ARM64 (aarch64)"),
- Name("avr", "AVR"),
- Name("cris", "CRIS"),
- Name("hppa", "HPPA"),
- Name("i386", "i386 (x86)"),
- Name("loongarch64", "LoongArch64"),
- Name("m68k", "m68k"),
- Name("microblaze", "Microblaze"),
- Name("microblazeel", "Microblaze (Little Endian)"),
- Name("mips", "MIPS"),
- Name("mipsel", "MIPS (Little Endian)"),
- Name("mips64", "MIPS64"),
- Name("mips64el", "MIPS64 (Little Endian)"),
- Name("nios2", "NIOS2"),
- Name("or1k", "OpenRISC"),
- Name("ppc", "PowerPC"),
- Name("ppc64", "PowerPC64"),
- Name("riscv32", "RISC-V32"),
- Name("riscv64", "RISC-V64"),
- Name("rx", "RX"),
- Name("s390x", "S390x (zSeries)"),
- Name("sh4", "SH4"),
- Name("sh4eb", "SH4 (Big Endian)"),
- Name("sparc", "SPARC"),
- Name("sparc64", "SPARC64"),
- Name("tricore", "TriCore"),
- Name("x86_64", "x86_64"),
- Name("xtensa", "Xtensa"),
- Name("xtensaeb", "Xtensa (Big Endian)")
- ]
- DEFAULTS = {
- "aarch64": "virt",
- "arm": "virt",
- "avr": "mega",
- "i386": "q35",
- "rx": "gdbsim-r5f562n7",
- "tricore": "tricore_testboard",
- "x86_64": "q35"
- }
- AUDIO_SCREAMER = Device('screamer', 'macio', '', 'Screamer (Mac99 only)')
- AUDIO_PCSPK = Device('pcspk', 'macio', '', 'PC Speaker')
- DISPLAY_TCX = Device('tcx', 'none', '', 'Sun TCX')
- DISPLAY_CG3 = Device('cg3', 'none', '', 'Sun cgthree')
- NETWORK_LANCE = Device('lance', 'none', '', 'Lance (Am7990)')
- ADD_DEVICES = {
- "ppc": {
- "Sound devices": set([
- AUDIO_SCREAMER
- ])
- },
- "ppc64": {
- "Sound devices": set([
- AUDIO_SCREAMER
- ])
- },
- "sparc": {
- "Display devices": set([
- DISPLAY_TCX,
- DISPLAY_CG3
- ]),
- "Network devices": set([
- NETWORK_LANCE
- ])
- },
- "i386": {
- "Sound devices": set([
- AUDIO_PCSPK
- ])
- },
- "x86_64": {
- "Sound devices": set([
- AUDIO_PCSPK
- ])
- },
- }
- HEADER = '''//
- // Copyright © 2022 osy. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- // !! THIS FILE IS GENERATED FROM const-gen.py, DO NOT MODIFY MANUALLY !!
- import Foundation
- '''
- def parseListing(listing):
- output = listing.splitlines()[1:]
- result = set()
- for line in output:
- idx = line.find(' ')
- if idx < 0:
- break
- name = line[0:idx]
- description = line[idx:].strip()
- result.add(Name(name, '{} ({})'.format(description, name)))
- return result
- def parseDeviceListing(defaults, listing):
- output = listing.splitlines()
- group = ''
- result = defaultdict(set, defaults)
- for line in output:
- if not line:
- continue
- if not line.startswith('name '):
- group = line.rstrip(':')
- continue
- search = re.search('^name "(?P<name>[^"]*)"(?:, bus (?P<bus>[^\s]+))?(?:, alias "(?P<alias>[^"]+)")?(?:, desc "(?P<desc>[^"]+)")?$', line)
- name = search.group('name')
- desc = search.group('desc')
- if not desc:
- desc = name
- else:
- desc = '{} ({})'.format(desc, name)
- item = Device(name, search.group('bus'), search.group('alias'), desc)
- result[group].add(item)
- return result
- def parseCpu(listing):
- def parseMips(line):
- search = re.search('^(?P<arch>\S+)\s+\'(?P<name>.+)\'.*', line)
- return Name(search.group('name'), search.group('name'))
- def parseSingle(line):
- name = line.strip()
- return Name(name, name)
- def parseSparc(line):
- search = re.search('^(?P<arch>\S+)\s+(?P<name>.+)\s+IU\s+(?P<iu>\S+)\s+FPU\s+(?P<fpu>\S+)\s+MMU\s+(?P<mmu>\S+)\s+NWINS\s+(?P<nwins>\d+).*$', line)
- return Name(search.group('name'), search.group('name'))
- def parseStandard(line):
- search = re.search('^(?P<arch>\S+)\s+(?P<name>\S+)\s+(?P<desc>.*)?$', line)
- name = search.group('name')
- desc = search.group('desc').strip()
- desc = ' '.join(desc.split())
- if not desc or desc.startswith('(alias'):
- desc = name
- else:
- desc = '{} ({})'.format(desc, name)
- return Name(name, desc)
- def parseSparcFlags(line):
- if line.startswith('Default CPU feature flags') or line.startswith('Available CPU feature flags'):
- flags = line.split(':')[1].strip()
- return [Name(flag, flag) for flag in flags.split(' ')]
- elif line.startswith('Numerical features'):
- return []
- else:
- return None
- def parseS390Flags(line):
- if line.endswith(':'):
- return []
- else:
- flag = line.split(' ')[0]
- return [Name(flag, flag)]
- def parseX86Flags(line):
- flags = []
- for flag in line.split(' '):
- if flag:
- flags.append(Name(flag, flag))
- return flags
- output = enumerate(listing.splitlines())
- cpus = [Name('default', 'Default')]
- flags = []
- if next(output, None) == None:
- return (cpus, flags)
- for (index, line) in output:
- if not line:
- break
- if len(line.strip().split(' ')) == 1:
- cpu = parseSingle(line)
- elif line.startswith('Sparc'):
- cpu = parseSparc(line)
- elif line.startswith('MIPS'):
- cpu = parseMips(line)
- elif parseSparcFlags(line) != None:
- flags += parseSparcFlags(line)
- continue
- else:
- cpu = parseStandard(line)
- if cpu.name != 'default':
- cpus.append(cpu)
- header = next(output, None)
- if header == None:
- return (cpus, flags)
- for (index, line) in output:
- if header[1] == 'Recognized CPUID flags:':
- flags += parseX86Flags(line)
- elif header[1] == 'Recognized feature flags:':
- flags += parseS390Flags(line)
- flags = set(flags) # de-duplicate
- return (cpus, flags)
- def sortItems(items):
- return sorted(items, key=lambda item: item.desc if item.desc else item.name)
- def getMachines(target, qemu_path):
- output = subprocess.check_output([qemu_path, '-machine', 'help']).decode('utf-8')
- return parseListing(output)
- def getDefaultMachine(target, machines):
- if target in DEFAULTS:
- return DEFAULTS[target]
- for machine in machines:
- if "default" in machine.desc:
- return machine.name
- return machines[0].name
- def getDevices(target, qemu_path):
- output = subprocess.check_output([qemu_path, '-device', 'help']).decode('utf-8')
- devices = parseDeviceListing(ADD_DEVICES[target.name] if target.name in ADD_DEVICES else {}, output)
- return devices
- def getCpus(target, qemu_path):
- output = subprocess.check_output([qemu_path, '-cpu', 'help']).decode('utf-8')
- return parseCpu(output)
- def sanitizeName(name):
- sanitized = re.sub('[^0-9a-zA-Z]+', '_', name)
- if len(sanitized) == 0:
- sanitized = '_empty'
- if sanitized[0].isdigit():
- sanitized = '_' + sanitized
- if sanitized in ['default']:
- sanitized = '`' + sanitized + '`'
- return sanitized
- def generateEmptyEnum(name):
- output = f'typealias {name} = AnyQEMUConstant\n'
- output += f'\n'
- return output
- def generateEnum(name, values, prettyValues, baseName='QEMUConstant', defaultValue=None):
- if len(values) == 0:
- return generateEmptyEnum(name)
- output = f'enum {name}: String, CaseIterable, {baseName} {{\n'
- for value in values:
- sanitized = sanitizeName(value)
- if sanitized == value:
- output += f' case {value}\n'
- else:
- output += f' case {sanitized} = "{value}"\n'
- output += '\n'
- if defaultValue:
- sanitized = sanitizeName(defaultValue)
- output += f' static var `default`: {name} {{\n'
- output += f' .{sanitized}\n'
- output += f' }}\n'
- output += f'\n'
- output += f' var prettyValue: String {{\n'
- output += f' switch self {{\n'
- for value, valuePretty in zip(values, prettyValues):
- sanitized = sanitizeName(value)
- if value in ['default']:
- output += f' case .{sanitized}: return NSLocalizedString("{valuePretty}", comment: "QEMUConstantGenerated")\n'
- else:
- output += f' case .{sanitized}: return "{valuePretty}"\n'
- output += f' }}\n'
- output += f' }}\n'
- output += f'}}\n'
- output += f'\n'
- return output
- def generateArchitectureAtlas(architectures, types):
- output = f'extension QEMUArchitecture {{\n'
- for k, v in types.items():
- output += f' var {v}: any {k}.Type {{\n'
- output += f' switch self {{\n'
- for a in architectures:
- a = sanitizeName(a)
- output += f' case .{a}: return {k}_{a}.self\n'
- output += f' }}\n'
- output += f' }}\n'
- output += f'\n'
- output += f'}}\n'
- output += f'\n'
- return output
- def generateEnumForeachArchitecture(name, targetItems, defaults={}):
- output = ''
- for target in targetItems:
- arch = target.name
- className = name + '_' + arch
- sortedItems = sortItems(target.items)
- values = [item.name for item in sortedItems]
- prettyValues = [item.desc for item in sortedItems]
- default = defaults[arch] if arch in defaults else None
- output += generateEnum(className, values, prettyValues, name, default)
- return output
- def generate(targets, cpus, cpuFlags, machines, displayDevices, networkDevices, soundDevices, serialDevices):
- targetKeys = [item.name for item in targets]
- output = HEADER
- output += generateEnum('QEMUArchitecture', targetKeys, [item.desc for item in targets])
- output += generateEnumForeachArchitecture('QEMUCPU', cpus)
- output += generateEnumForeachArchitecture('QEMUCPUFlag', cpuFlags)
- output += generateEnumForeachArchitecture('QEMUTarget', machines, {machine.name: machine.default for machine in machines})
- output += generateEnumForeachArchitecture('QEMUDisplayDevice', displayDevices)
- output += generateEnumForeachArchitecture('QEMUNetworkDevice', networkDevices)
- output += generateEnumForeachArchitecture('QEMUSoundDevice', soundDevices)
- output += generateEnumForeachArchitecture('QEMUSerialDevice', serialDevices)
- output += generateArchitectureAtlas(targetKeys, {
- 'QEMUCPU': 'cpuType',
- 'QEMUCPUFlag': 'cpuFlagType',
- 'QEMUTarget': 'targetType',
- 'QEMUDisplayDevice': 'displayDeviceType',
- 'QEMUNetworkDevice': 'networkDeviceType',
- 'QEMUSoundDevice': 'soundDeviceType',
- 'QEMUSerialDevice': 'serialDeviceType',
- })
- return output
- def transformDisplayCards(displayCards):
- def transform(item):
- if item.name.endswith('-gl') or '-gl-' in item.name:
- item = Device(item.name, item.bus, item.alias, item.desc + ' (GPU Supported)')
- return item
- return set(map(transform, displayCards))
- def main(argv):
- base = argv[1]
- allMachines = []
- allCpus = []
- allCpuFlags = []
- allDisplayCards = []
- allSoundCards = []
- allNetworkCards = []
- allSerialCards = []
- # parse outputs
- for target in TARGETS:
- path = '{}/{}-softmmu/qemu-system-{}'.format(base, target.name, target.name)
- if not os.path.exists(path):
- path = '{}/qemu-system-{}'.format(base, target.name)
- if not os.path.exists(path):
- raise "Invalid path."
- machines = sortItems(getMachines(target, path))
- default = getDefaultMachine(target.name, machines)
- allMachines.append(Architecture(target.name, machines, default))
- devices = getDevices(target, path)
- displayCards = transformDisplayCards(devices["Display devices"])
- allDisplayCards.append(Architecture(target.name, displayCards, None))
- allNetworkCards.append(Architecture(target.name, devices["Network devices"], None))
- nonHdaDevices = [device for device in devices["Sound devices"] if device.bus != 'HDA']
- allSoundCards.append(Architecture(target.name, nonHdaDevices, None))
- serialDevices = [device for device in devices["Input devices"] if 'serial' in device.name]
- allSerialCards.append(Architecture(target.name, serialDevices, None))
- cpus, flags = getCpus(target, path)
- allCpus.append(Architecture(target.name, cpus, 0))
- allCpuFlags.append(Architecture(target.name, flags, 0))
- # generate constants
- print(generate(TARGETS, allCpus, allCpuFlags, allMachines, allDisplayCards, allNetworkCards, allSoundCards, allSerialCards))
- if __name__ == "__main__":
- main(sys.argv)
|