* changes: [audiopolicy][enginebase] Fix schema verification Sync AUDIO_FLAG_XXX to/from string utility with enumeration [AudioPolicy][EngineConfigurable] Cleanup warn / error logs [AudioPolicy][EngineConfigurable] Fix new assistant stream regression [AudioPolicy][EngineConfigurable] Prevent regression on audio-base.h changegugelfrei
commit
9b7d89970a
File diff suppressed because it is too large
Load Diff
@ -1,186 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ComponentTypeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xsi:noNamespaceSchemaLocation="Schemas/ComponentTypeSet.xsd">
|
||||
<!-- Output devices definition as a bitfield for the supported devices per output
|
||||
profile. It must match with the output device enum parameter.
|
||||
-->
|
||||
<!--#################### GLOBAL COMPONENTS BEGIN ####################-->
|
||||
|
||||
<!--#################### GLOBAL COMPONENTS END ####################-->
|
||||
|
||||
<ComponentType Name="OutputDevicesMask" Description="32th bit is not allowed as dedicated
|
||||
for input devices detection">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
<BitParameter Name="earpiece" Size="1" Pos="0"/>
|
||||
<BitParameter Name="speaker" Size="1" Pos="1"/>
|
||||
<BitParameter Name="wired_headset" Size="1" Pos="2"/>
|
||||
<BitParameter Name="wired_headphone" Size="1" Pos="3"/>
|
||||
<BitParameter Name="bluetooth_sco" Size="1" Pos="4"/>
|
||||
<BitParameter Name="bluetooth_sco_headset" Size="1" Pos="5"/>
|
||||
<BitParameter Name="bluetooth_sco_carkit" Size="1" Pos="6"/>
|
||||
<BitParameter Name="bluetooth_a2dp" Size="1" Pos="7"/>
|
||||
<BitParameter Name="bluetooth_a2dp_headphones" Size="1" Pos="8"/>
|
||||
<BitParameter Name="bluetooth_a2dp_speaker" Size="1" Pos="9"/>
|
||||
<BitParameter Name="hdmi" Size="1" Pos="10"/>
|
||||
<BitParameter Name="angl_dock_headset" Size="1" Pos="11"/>
|
||||
<BitParameter Name="dgtl_dock_headset" Size="1" Pos="12"/>
|
||||
<BitParameter Name="usb_accessory" Size="1" Pos="13"/>
|
||||
<BitParameter Name="usb_device" Size="1" Pos="14"/>
|
||||
<BitParameter Name="remote_submix" Size="1" Pos="15"/>
|
||||
<BitParameter Name="telephony_tx" Size="1" Pos="16"/>
|
||||
<BitParameter Name="line" Size="1" Pos="17"/>
|
||||
<BitParameter Name="hdmi_arc" Size="1" Pos="18"/>
|
||||
<BitParameter Name="spdif" Size="1" Pos="19"/>
|
||||
<BitParameter Name="fm" Size="1" Pos="20"/>
|
||||
<BitParameter Name="aux_line" Size="1" Pos="21"/>
|
||||
<BitParameter Name="speaker_safe" Size="1" Pos="22"/>
|
||||
<BitParameter Name="ip" Size="1" Pos="23"/>
|
||||
<BitParameter Name="bus" Size="1" Pos="24"/>
|
||||
<BitParameter Name="proxy" Size="1" Pos="25"/>
|
||||
<BitParameter Name="usb_headset" Size="1" Pos="26"/>
|
||||
<BitParameter Name="hearing_aid" Size="1" Pos="27"/>
|
||||
<BitParameter Name="echo_canceller" Size="1" Pos="28"/>
|
||||
<BitParameter Name="stub" Size="1" Pos="30"/>
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<!-- Input devices definition as a bitfield for the supported devices per Input
|
||||
profile. It must match with the Input device enum parameter.
|
||||
-->
|
||||
<ComponentType Name="InputDevicesMask">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
<BitParameter Name="communication" Size="1" Pos="0"/>
|
||||
<BitParameter Name="ambient" Size="1" Pos="1"/>
|
||||
<BitParameter Name="builtin_mic" Size="1" Pos="2"/>
|
||||
<BitParameter Name="bluetooth_sco_headset" Size="1" Pos="3"/>
|
||||
<BitParameter Name="wired_headset" Size="1" Pos="4"/>
|
||||
<BitParameter Name="hdmi" Size="1" Pos="5"/>
|
||||
<BitParameter Name="telephony_rx" Size="1" Pos="6"/>
|
||||
<BitParameter Name="back_mic" Size="1" Pos="7"/>
|
||||
<BitParameter Name="remote_submix" Size="1" Pos="8"/>
|
||||
<BitParameter Name="anlg_dock_headset" Size="1" Pos="9"/>
|
||||
<BitParameter Name="dgtl_dock_headset" Size="1" Pos="10"/>
|
||||
<BitParameter Name="usb_accessory" Size="1" Pos="11"/>
|
||||
<BitParameter Name="usb_device" Size="1" Pos="12"/>
|
||||
<BitParameter Name="fm_tuner" Size="1" Pos="13"/>
|
||||
<BitParameter Name="tv_tuner" Size="1" Pos="14"/>
|
||||
<BitParameter Name="line" Size="1" Pos="15"/>
|
||||
<BitParameter Name="spdif" Size="1" Pos="16"/>
|
||||
<BitParameter Name="bluetooth_a2dp" Size="1" Pos="17"/>
|
||||
<BitParameter Name="loopback" Size="1" Pos="18"/>
|
||||
<BitParameter Name="ip" Size="1" Pos="19"/>
|
||||
<BitParameter Name="bus" Size="1" Pos="20"/>
|
||||
<BitParameter Name="proxy" Size="1" Pos="21"/>
|
||||
<BitParameter Name="usb_headset" Size="1" Pos="22"/>
|
||||
<BitParameter Name="bluetooth_ble" Size="1" Pos="23"/>
|
||||
<BitParameter Name="hdmi_arc" Size="1" Pos="24"/>
|
||||
<BitParameter Name="echo_reference" Size="1" Pos="25"/>
|
||||
<BitParameter Name="stub" Size="1" Pos="30"/>
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<ComponentType Name="OutputFlags"
|
||||
Description="the audio output flags serve two purposes:
|
||||
- when an AudioTrack is created they indicate a wish to be connected to an
|
||||
output stream with attributes corresponding to the specified flags.
|
||||
- when present in an output profile descriptor listed for a particular audio
|
||||
hardware module, they indicate that an output stream can be opened that
|
||||
supports the attributes indicated by the flags.
|
||||
The audio policy manager will try to match the flags in the request
|
||||
(when getOuput() is called) to an available output stream.">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
<BitParameter Name="direct" Size="1" Pos="0"/>
|
||||
<BitParameter Name="primary" Size="1" Pos="1"/>
|
||||
<BitParameter Name="fast" Size="1" Pos="2"/>
|
||||
<BitParameter Name="deep_buffer" Size="1" Pos="3"/>
|
||||
<BitParameter Name="compress_offload" Size="1" Pos="4"/>
|
||||
<BitParameter Name="non_blocking" Size="1" Pos="5"/>
|
||||
<BitParameter Name="hw_av_sync" Size="1" Pos="6"/>
|
||||
<BitParameter Name="tts" Size="1" Pos="7"/>
|
||||
<BitParameter Name="raw" Size="1" Pos="8"/>
|
||||
<BitParameter Name="sync" Size="1" Pos="9"/>
|
||||
<BitParameter Name="iec958_nonaudio" Size="1" Pos="10"/>
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<ComponentType Name="InputFlags"
|
||||
Description="The audio input flags are analogous to audio output flags.
|
||||
Currently they are used only when an AudioRecord is created,
|
||||
to indicate a preference to be connected to an input stream with
|
||||
attributes corresponding to the specified flags.">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
<BitParameter Name="fast" Size="1" Pos="0"/>
|
||||
<BitParameter Name="hw_hotword" Size="1" Pos="2"/>
|
||||
<BitParameter Name="raw" Size="1" Pos="3"/>
|
||||
<BitParameter Name="sync" Size="1" Pos="4"/>
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<ComponentType Name="InputSourcesMask" Description="The audio input source is also known
|
||||
as the use case.">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
<BitParameter Name="default" Size="1" Pos="0"/>
|
||||
<BitParameter Name="mic" Size="1" Pos="1"/>
|
||||
<BitParameter Name="voice_uplink" Size="1" Pos="2"/>
|
||||
<BitParameter Name="voice_downlink" Size="1" Pos="3"/>
|
||||
<BitParameter Name="voice_call" Size="1" Pos="4"/>
|
||||
<BitParameter Name="camcorder" Size="1" Pos="5"/>
|
||||
<BitParameter Name="voice_recognition" Size="1" Pos="6"/>
|
||||
<BitParameter Name="voice_communication" Size="1" Pos="7"/>
|
||||
<BitParameter Name="remote_submix" Size="1" Pos="8"/>
|
||||
<BitParameter Name="unprocessed" Size="1" Pos="9"/>
|
||||
<BitParameter Name="voice_performance" Size="1" Pos="10"/>
|
||||
<BitParameter Name="echo_reference" Size="1" Pos="11"/>
|
||||
<BitParameter Name="fm_tuner" Size="1" Pos="12"/>
|
||||
<BitParameter Name="hotword" Size="1" Pos="13"/>
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### STREAM COMMON TYPES BEGIN ####################-->
|
||||
|
||||
<ComponentType Name="VolumeProfileType">
|
||||
<EnumParameter Name="volume_profile" Size="32">
|
||||
<ValuePair Literal="voice_call" Numerical="0"/>
|
||||
<ValuePair Literal="system" Numerical="1"/>
|
||||
<ValuePair Literal="ring" Numerical="2"/>
|
||||
<ValuePair Literal="music" Numerical="3"/>
|
||||
<ValuePair Literal="alarm" Numerical="4"/>
|
||||
<ValuePair Literal="notification" Numerical="5"/>
|
||||
<ValuePair Literal="bluetooth_sco" Numerical="6"/>
|
||||
<ValuePair Literal="enforced_audible" Numerical="7"/>
|
||||
<ValuePair Literal="dtmf" Numerical="8"/>
|
||||
<ValuePair Literal="tts" Numerical="9"/>
|
||||
<ValuePair Literal="accessibility" Numerical="10"/>
|
||||
<ValuePair Literal="rerouting" Numerical="11"/>
|
||||
<ValuePair Literal="patch" Numerical="12"/>
|
||||
</EnumParameter>
|
||||
</ComponentType>
|
||||
|
||||
<ComponentType Name="Stream" Mapping="Stream">
|
||||
<Component Name="applicable_volume_profile" Type="VolumeProfileType"
|
||||
Description="Volume profile followed by a given stream type."/>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### STREAM COMMON TYPES END ####################-->
|
||||
|
||||
<!--#################### INPUT SOURCE COMMON TYPES BEGIN ####################-->
|
||||
|
||||
<ComponentType Name="InputSource">
|
||||
<Component Name="applicable_input_device" Type="InputDevicesMask"
|
||||
Mapping="InputSource" Description="Selected Input device"/>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### INPUT SOURCE COMMON TYPES END ####################-->
|
||||
|
||||
<!--#################### PRODUCT STRATEGY COMMON TYPES BEGIN ####################-->
|
||||
|
||||
<ComponentType Name="ProductStrategy" Mapping="ProductStrategy">
|
||||
<Component Name="selected_output_devices" Type="OutputDevicesMask"/>
|
||||
<StringParameter Name="device_address" MaxLength="256"
|
||||
Description="if any, device address associated"/>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### PRODUCT STRATEGY COMMON TYPES END ####################-->
|
||||
|
||||
</ComponentTypeSet>
|
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ComponentTypeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xsi:noNamespaceSchemaLocation="Schemas/ComponentTypeSet.xsd">
|
||||
<!-- Output devices definition as a bitfield for the supported devices per output
|
||||
profile. It must match with the output device enum parameter.
|
||||
-->
|
||||
<!--#################### GLOBAL COMPONENTS BEGIN ####################-->
|
||||
|
||||
<!--#################### GLOBAL COMPONENTS END ####################-->
|
||||
|
||||
<!-- Automatically filled from audio-base.h file -->
|
||||
<ComponentType Name="OutputDevicesMask" Description="32th bit is not allowed as dedicated for input devices detection">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<!-- Input devices definition as a bitfield for the supported devices per Input
|
||||
profile. It must match with the Input device enum parameter.
|
||||
-->
|
||||
<!-- Automatically filled from audio-base.h file -->
|
||||
<ComponentType Name="InputDevicesMask">
|
||||
<BitParameterBlock Name="mask" Size="32">
|
||||
</BitParameterBlock>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### STREAM COMMON TYPES BEGIN ####################-->
|
||||
<!-- Automatically filled from audio-base.h file. VolumeProfileType is associated to stream type -->
|
||||
<ComponentType Name="VolumeProfileType">
|
||||
<EnumParameter Name="volume_profile" Size="32">
|
||||
</EnumParameter>
|
||||
</ComponentType>
|
||||
|
||||
<ComponentType Name="Stream" Mapping="Stream">
|
||||
<Component Name="applicable_volume_profile" Type="VolumeProfileType"
|
||||
Description="Volume profile followed by a given stream type."/>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### STREAM COMMON TYPES END ####################-->
|
||||
|
||||
<!--#################### INPUT SOURCE COMMON TYPES BEGIN ####################-->
|
||||
|
||||
<ComponentType Name="InputSource">
|
||||
<Component Name="applicable_input_device" Type="InputDevicesMask"
|
||||
Mapping="InputSource" Description="Selected Input device"/>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### INPUT SOURCE COMMON TYPES END ####################-->
|
||||
|
||||
<!--#################### PRODUCT STRATEGY COMMON TYPES BEGIN ####################-->
|
||||
|
||||
<ComponentType Name="ProductStrategy" Mapping="ProductStrategy">
|
||||
<Component Name="selected_output_devices" Type="OutputDevicesMask"/>
|
||||
<StringParameter Name="device_address" MaxLength="256"
|
||||
Description="if any, device address associated"/>
|
||||
</ComponentType>
|
||||
|
||||
<!--#################### PRODUCT STRATEGY COMMON TYPES END ####################-->
|
||||
|
||||
</ComponentTypeSet>
|
@ -0,0 +1,184 @@
|
||||
#! /usr/bin/python3
|
||||
#
|
||||
# pylint: disable=line-too-long, missing-docstring, logging-format-interpolation, invalid-name
|
||||
|
||||
#
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import OrderedDict
|
||||
import xml.dom.minidom as MINIDOM
|
||||
|
||||
def parseArgs():
|
||||
argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
|
||||
structure file generator.\n\
|
||||
Exit with the number of (recoverable or not) error that occured.")
|
||||
argparser.add_argument('--androidaudiobaseheader',
|
||||
help="Android Audio Base C header file, Mandatory.",
|
||||
metavar="ANDROID_AUDIO_BASE_HEADER",
|
||||
type=argparse.FileType('r'),
|
||||
required=True)
|
||||
argparser.add_argument('--commontypesstructure',
|
||||
help="Structure XML base file. Mandatory.",
|
||||
metavar="STRUCTURE_FILE_IN",
|
||||
type=argparse.FileType('r'),
|
||||
required=True)
|
||||
argparser.add_argument('--outputfile',
|
||||
help="Structure XML file. Mandatory.",
|
||||
metavar="STRUCTURE_FILE_OUT",
|
||||
type=argparse.FileType('w'),
|
||||
required=True)
|
||||
argparser.add_argument('--verbose',
|
||||
action='store_true')
|
||||
|
||||
return argparser.parse_args()
|
||||
|
||||
|
||||
def findBitPos(decimal):
|
||||
pos = 0
|
||||
i = 1
|
||||
while i != decimal:
|
||||
i = i << 1
|
||||
pos = pos + 1
|
||||
if pos == 32:
|
||||
return -1
|
||||
return pos
|
||||
|
||||
|
||||
def generateXmlStructureFile(componentTypeDict, structureTypesFile, outputFile):
|
||||
|
||||
logging.info("Importing structureTypesFile {}".format(structureTypesFile))
|
||||
component_types_in_tree = ET.parse(structureTypesFile)
|
||||
|
||||
component_types_root = component_types_in_tree.getroot()
|
||||
|
||||
for component_types_name, values_dict in componentTypeDict.items():
|
||||
for component_type in component_types_root.findall('ComponentType'):
|
||||
if component_type.get('Name') == component_types_name:
|
||||
bitparameters_node = component_type.find("BitParameterBlock")
|
||||
if bitparameters_node is not None:
|
||||
ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
|
||||
for key, value in ordered_values.items():
|
||||
value_node = ET.SubElement(bitparameters_node, "BitParameter")
|
||||
value_node.set('Name', key)
|
||||
value_node.set('Size', "1")
|
||||
value_node.set('Pos', str(findBitPos(value)))
|
||||
|
||||
enum_parameter_node = component_type.find("EnumParameter")
|
||||
if enum_parameter_node is not None:
|
||||
ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
|
||||
for key, value in ordered_values.items():
|
||||
value_node = ET.SubElement(enum_parameter_node, "ValuePair")
|
||||
value_node.set('Literal', key)
|
||||
value_node.set('Numerical', str(value))
|
||||
|
||||
xmlstr = ET.tostring(component_types_root, encoding='utf8', method='xml')
|
||||
reparsed = MINIDOM.parseString(xmlstr)
|
||||
prettyXmlStr = reparsed.toprettyxml(indent=" ", newl='\n')
|
||||
prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()])
|
||||
outputFile.write(prettyXmlStr)
|
||||
|
||||
|
||||
def capitalizeLine(line):
|
||||
return ' '.join((w.capitalize() for w in line.split(' ')))
|
||||
|
||||
def parseAndroidAudioFile(androidaudiobaseheaderFile):
|
||||
#
|
||||
# Adaptation table between Android Enumeration prefix and Audio PFW Criterion type names
|
||||
#
|
||||
component_type_mapping_table = {
|
||||
'AUDIO_STREAM' : "VolumeProfileType",
|
||||
'AUDIO_DEVICE_OUT' : "OutputDevicesMask",
|
||||
'AUDIO_DEVICE_IN' : "InputDevicesMask"}
|
||||
|
||||
all_component_types = {
|
||||
'VolumeProfileType' : {},
|
||||
'OutputDevicesMask' : {},
|
||||
'InputDevicesMask' : {}
|
||||
}
|
||||
|
||||
#
|
||||
# _CNT, _MAX, _ALL and _NONE are prohibited values as ther are just helpers for enum users.
|
||||
#
|
||||
ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
|
||||
|
||||
criteria_pattern = re.compile(
|
||||
r"\s*(?P<type>(?:"+'|'.join(component_type_mapping_table.keys()) + "))_" \
|
||||
r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*=\s*" \
|
||||
r"(?P<values>(?:0[xX])?[0-9a-fA-F]+)")
|
||||
|
||||
logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile))
|
||||
|
||||
for line_number, line in enumerate(androidaudiobaseheaderFile):
|
||||
match = criteria_pattern.match(line)
|
||||
if match:
|
||||
logging.debug("The following line is VALID: {}:{}\n{}".format(
|
||||
androidaudiobaseheaderFile.name, line_number, line))
|
||||
|
||||
component_type_name = component_type_mapping_table[match.groupdict()['type']]
|
||||
component_type_literal = match.groupdict()['literal'].lower()
|
||||
|
||||
component_type_numerical_value = match.groupdict()['values']
|
||||
|
||||
# for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub
|
||||
if component_type_name == "InputDevicesMask":
|
||||
component_type_numerical_value = str(int(component_type_numerical_value, 0) & ~2147483648)
|
||||
if component_type_literal == "default":
|
||||
component_type_literal = "stub"
|
||||
|
||||
if component_type_name == "OutputDevicesMask":
|
||||
if component_type_literal == "default":
|
||||
component_type_literal = "stub"
|
||||
|
||||
# Remove duplicated numerical values
|
||||
if int(component_type_numerical_value, 0) in all_component_types[component_type_name].values():
|
||||
logging.info("The value {}:{} is duplicated for criterion {}, KEEPING LATEST".format(component_type_numerical_value, component_type_literal, component_type_name))
|
||||
for key in list(all_component_types[component_type_name]):
|
||||
if all_component_types[component_type_name][key] == int(component_type_numerical_value, 0):
|
||||
del all_component_types[component_type_name][key]
|
||||
|
||||
all_component_types[component_type_name][component_type_literal] = int(component_type_numerical_value, 0)
|
||||
|
||||
logging.debug("type:{}, literal:{}, values:{}.".format(component_type_name, component_type_literal, component_type_numerical_value))
|
||||
|
||||
# Transform input source in inclusive criterion
|
||||
shift = len(all_component_types['OutputDevicesMask'])
|
||||
if shift > 32:
|
||||
logging.critical("OutputDevicesMask incompatible with criterion representation on 32 bits")
|
||||
logging.info("EXIT ON FAILURE")
|
||||
exit(1)
|
||||
|
||||
for component_types in all_component_types:
|
||||
values = ','.join('{}:{}'.format(value, key) for key, value in all_component_types[component_types].items())
|
||||
logging.info("{}: <{}>".format(component_types, values))
|
||||
|
||||
return all_component_types
|
||||
|
||||
|
||||
def main():
|
||||
logging.root.setLevel(logging.INFO)
|
||||
args = parseArgs()
|
||||
route_criteria = 0
|
||||
|
||||
all_component_types = parseAndroidAudioFile(args.androidaudiobaseheader)
|
||||
|
||||
generateXmlStructureFile(all_component_types, args.commontypesstructure, args.outputfile)
|
||||
|
||||
# If this file is directly executed
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
Reference in new issue