mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
Build Observatory directly with dart2js
- Drop the `copy_observatory` and pub get steps. Use a checked in `dart2js.packages` file and build from the source tree. - Invoke dart2js directly for the build command. - Use the dart2js executable checked in to the SDK when it is available, otherwise fall back on the dart VM and dart2js source. - Manually synthesize a filered `packages` directory out of the source directories for observatory and it's dependencies - Remove transformer section of the pubspec since it's unused. Bug: #32900 Change-Id: I35a19ffc8c3e732c68b65adf4595a6a51e28b94f Reviewed-on: https://dart-review.googlesource.com/51740 Reviewed-by: Zach Anderson <zra@google.com> Commit-Queue: Nate Bosch <nbosch@google.com>
This commit is contained in:
parent
fc775b3e9b
commit
6d5e8e8f87
|
@ -8,128 +8,71 @@ import("../../build/prebuilt_dart_sdk.gni")
|
|||
import("observatory_sources.gni")
|
||||
|
||||
# Construct arguments to the observatory tool for finding pub.
|
||||
pub_build_deps = []
|
||||
pub_build_args = []
|
||||
build_deps = []
|
||||
build_args = []
|
||||
if (!prebuilt_dart_exe_works) {
|
||||
pub_build_deps += [ "../bin:dart_bootstrap($dart_host_toolchain)" ]
|
||||
build_deps += [ "../bin:dart_bootstrap($dart_host_toolchain)" ]
|
||||
|
||||
dart_out_dir = get_label_info("../bin:dart_bootstrap($dart_host_toolchain)",
|
||||
"root_out_dir")
|
||||
dart_bootstrap = rebase_path("$dart_out_dir/dart_bootstrap$executable_suffix")
|
||||
|
||||
pub_build_args = [
|
||||
build_args = [
|
||||
"--sdk=True",
|
||||
"--dart-executable",
|
||||
dart_bootstrap,
|
||||
]
|
||||
} else {
|
||||
pub_build_args = [ "--sdk=True" ]
|
||||
build_args = [ "--sdk=True" ]
|
||||
}
|
||||
|
||||
current_dir = rebase_path(".", "//")
|
||||
|
||||
# Helper build rules for packaging the Dart observatory resources.
|
||||
copy("copy_observatory") {
|
||||
action("build_observatory") {
|
||||
deps = build_deps
|
||||
|
||||
sources = rebase_path(observatory_sources, "", ".")
|
||||
outputs = [
|
||||
"$root_gen_dir/observatory_copy/{{source_root_relative_dir}}/{{source_file_part}}",
|
||||
]
|
||||
}
|
||||
|
||||
action("write_observatory_pubspec_yaml") {
|
||||
deps = [ ":copy_observatory" ] + pub_build_deps
|
||||
|
||||
inputs = [
|
||||
rebase_path("pubspec.yaml"),
|
||||
]
|
||||
inputs = sources + [ rebase_path("dart2js.packages") ]
|
||||
|
||||
script = "../../tools/observatory_tool.py"
|
||||
args =
|
||||
pub_build_args + [
|
||||
"--silent=True",
|
||||
"--directory",
|
||||
rebase_path("$root_gen_dir/observatory_copy/$current_dir/"),
|
||||
"--command",
|
||||
"rewrite",
|
||||
rebase_path("../observatory/pubspec.yaml"),
|
||||
rebase_path("$root_gen_dir/observatory_copy/$current_dir/pubspec.yaml"),
|
||||
"../../third_party/",
|
||||
rebase_path("../../third_party/"),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
"$root_gen_dir/observatory_copy/$current_dir/pubspec.yaml",
|
||||
]
|
||||
}
|
||||
|
||||
action("copy_observatory_deps") {
|
||||
deps = [ ":write_observatory_pubspec_yaml" ] + pub_build_deps
|
||||
|
||||
inputs = [
|
||||
"$root_gen_dir/observatory_copy/$current_dir/pubspec.yaml",
|
||||
]
|
||||
|
||||
script = "../../tools/observatory_tool.py"
|
||||
args = pub_build_args + [
|
||||
"--silent=True",
|
||||
"--stamp",
|
||||
rebase_path(
|
||||
"$root_gen_dir/observatory_copy/$current_dir/packages.stamp"),
|
||||
"--directory",
|
||||
rebase_path("$root_gen_dir/observatory_copy/$current_dir/"),
|
||||
"--command",
|
||||
"get",
|
||||
]
|
||||
|
||||
outputs = [
|
||||
"$root_gen_dir/observatory_copy/$current_dir/packages.stamp",
|
||||
]
|
||||
}
|
||||
|
||||
action("pub_build_observatory") {
|
||||
deps = [
|
||||
":copy_observatory",
|
||||
":copy_observatory_deps",
|
||||
] + pub_build_deps
|
||||
|
||||
sources = rebase_path(observatory_sources,
|
||||
"",
|
||||
"$root_gen_dir/observatory_copy/$current_dir")
|
||||
|
||||
inputs =
|
||||
sources + [ "$root_gen_dir/observatory_copy/$current_dir/packages.stamp" ]
|
||||
|
||||
script = "../../tools/observatory_tool.py"
|
||||
args = pub_build_args + [
|
||||
args = build_args + [
|
||||
"--silent=True",
|
||||
"--directory",
|
||||
rebase_path("$root_gen_dir/observatory_copy/$current_dir/"),
|
||||
rebase_path("."),
|
||||
"--command",
|
||||
"build",
|
||||
rebase_path("$root_out_dir/observatory/build"),
|
||||
rebase_path("web/main.dart"),
|
||||
rebase_path("$root_gen_dir/observatory/web/main.dart.js"),
|
||||
rebase_path("dart2js.packages"),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
"$root_out_dir/observatory/build/web/main.dart.js",
|
||||
"$root_gen_dir/observatory/web/main.dart.js",
|
||||
]
|
||||
}
|
||||
|
||||
action("deploy_observatory") {
|
||||
deps = [ ":pub_build_observatory" ] + pub_build_deps
|
||||
deps = [ ":build_observatory" ] + build_deps
|
||||
|
||||
script = "../../tools/observatory_tool.py"
|
||||
|
||||
inputs = [
|
||||
script,
|
||||
"$root_out_dir/observatory/build/web/main.dart.js",
|
||||
"$root_gen_dir/observatory/web/main.dart.js",
|
||||
]
|
||||
|
||||
args = pub_build_args + [
|
||||
args = build_args + [
|
||||
"--silent=True",
|
||||
"--directory",
|
||||
rebase_path("$root_out_dir/observatory"),
|
||||
"--command",
|
||||
"deploy",
|
||||
rebase_path("$root_out_dir/observatory/deployed"),
|
||||
rebase_path("web"),
|
||||
rebase_path("lib"),
|
||||
rebase_path("$root_gen_dir/observatory/web/main.dart.js"),
|
||||
rebase_path("../../third_party/observatory_pub_packages/packages"),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
|
|
24
runtime/observatory/dart2js.packages
Normal file
24
runtime/observatory/dart2js.packages
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Update if observatory dependencies change
|
||||
args:../../third_party/observatory_pub_packages/packages/args/lib
|
||||
async:../../third_party/observatory_pub_packages/packages/async/lib
|
||||
barback:../../third_party/observatory_pub_packages/packages/barback/lib
|
||||
browser:../../third_party/observatory_pub_packages/packages/browser/lib
|
||||
charcode:../../third_party/observatory_pub_packages/packages/charcode/lib
|
||||
charted:../../third_party/observatory_pub_packages/packages/charted/lib
|
||||
collection:../../third_party/observatory_pub_packages/packages/collection/lib
|
||||
csslib:../../third_party/observatory_pub_packages/packages/csslib/lib
|
||||
html:../../third_party/observatory_pub_packages/packages/html/lib
|
||||
intl:../../third_party/observatory_pub_packages/packages/intl/lib
|
||||
logging:../../third_party/observatory_pub_packages/packages/logging/lib
|
||||
matcher:../../third_party/observatory_pub_packages/packages/matcher/lib
|
||||
meta:../../third_party/observatory_pub_packages/packages/meta/lib
|
||||
observable:../../third_party/observatory_pub_packages/packages/observable/lib
|
||||
path:../../third_party/observatory_pub_packages/packages/path/lib
|
||||
pool:../../third_party/observatory_pub_packages/packages/pool/lib
|
||||
quiver:../../third_party/observatory_pub_packages/packages/quiver/lib
|
||||
source_span:../../third_party/observatory_pub_packages/packages/source_span/lib
|
||||
stack_trace:../../third_party/observatory_pub_packages/packages/stack_trace/lib
|
||||
unittest:../../third_party/observatory_pub_packages/packages/unittest/lib
|
||||
usage:../../third_party/observatory_pub_packages/packages/usage/lib
|
||||
utf:../../third_party/observatory_pub_packages/packages/utf/lib
|
||||
observatory:lib/
|
|
@ -1,60 +0,0 @@
|
|||
# Generated file DO NOT EDIT
|
||||
name: observatory
|
||||
transformers:
|
||||
- dart_to_js_script_rewriter
|
||||
- $dart2js:
|
||||
suppressWarnings: false
|
||||
commandLineOptions: [--show-package-warnings]
|
||||
dependencies:
|
||||
args: ^0.13.2
|
||||
charted: ^0.4.8
|
||||
unittest: < 0.12.0
|
||||
usage: ^1.2.0
|
||||
dart_to_js_script_rewriter: 1.0.3
|
||||
dependency_overrides:
|
||||
args:
|
||||
path: ../../third_party/observatory_pub_packages/packages/args
|
||||
async:
|
||||
path: ../../third_party/observatory_pub_packages/packages/async
|
||||
barback:
|
||||
path: ../../third_party/observatory_pub_packages/packages/barback
|
||||
browser:
|
||||
path: ../../third_party/observatory_pub_packages/packages/browser
|
||||
charcode:
|
||||
path: ../../third_party/observatory_pub_packages/packages/charcode
|
||||
charted:
|
||||
path: ../../third_party/observatory_pub_packages/packages/charted
|
||||
collection:
|
||||
path: ../../third_party/observatory_pub_packages/packages/collection
|
||||
csslib:
|
||||
path: ../../third_party/observatory_pub_packages/packages/csslib
|
||||
dart_to_js_script_rewriter:
|
||||
path: ../../third_party/observatory_pub_packages/packages/dart_to_js_script_rewriter
|
||||
html:
|
||||
path: ../../third_party/observatory_pub_packages/packages/html
|
||||
intl:
|
||||
path: ../../third_party/observatory_pub_packages/packages/intl
|
||||
logging:
|
||||
path: ../../third_party/observatory_pub_packages/packages/logging
|
||||
matcher:
|
||||
path: ../../third_party/observatory_pub_packages/packages/matcher
|
||||
meta:
|
||||
path: ../../third_party/observatory_pub_packages/packages/meta
|
||||
observable:
|
||||
path: ../../third_party/observatory_pub_packages/packages/observable
|
||||
path:
|
||||
path: ../../third_party/observatory_pub_packages/packages/path
|
||||
pool:
|
||||
path: ../../third_party/observatory_pub_packages/packages/pool
|
||||
quiver:
|
||||
path: ../../third_party/observatory_pub_packages/packages/quiver
|
||||
source_span:
|
||||
path: ../../third_party/observatory_pub_packages/packages/source_span
|
||||
stack_trace:
|
||||
path: ../../third_party/observatory_pub_packages/packages/stack_trace
|
||||
unittest:
|
||||
path: ../../third_party/observatory_pub_packages/packages/unittest
|
||||
usage:
|
||||
path: ../../third_party/observatory_pub_packages/packages/usage
|
||||
utf:
|
||||
path: ../../third_party/observatory_pub_packages/packages/utf
|
|
@ -8,18 +8,17 @@ import argparse
|
|||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import utils
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(sys.argv[0])
|
||||
DART_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
|
||||
PUB_PATH = os.path.join(DART_ROOT, 'third_party', 'pkg',
|
||||
'pub', 'bin', 'pub.dart')
|
||||
DART2JS_PATH = os.path.join(DART_ROOT, 'pkg', 'compiler', 'bin', 'dart2js.dart')
|
||||
IGNORE_PATTERNS = shutil.ignore_patterns(
|
||||
'$sdk',
|
||||
'*.concat.js',
|
||||
'*.dart',
|
||||
'*.log',
|
||||
'*.map',
|
||||
'*.precompiled.js',
|
||||
|
@ -49,7 +48,7 @@ Please see the Wiki for instructions on replacing the checked-in Dart SDK.
|
|||
|
||||
https://github.com/dart-lang/sdk/wiki/The-checked-in-SDK-in-tools
|
||||
|
||||
To use the dart_bootstrap binary please update the PubCommand function
|
||||
To use the dart_bootstrap binary please update the Build function
|
||||
in the tools/observatory_tool.py script.
|
||||
|
||||
"""
|
||||
|
@ -70,17 +69,8 @@ ERROR: Observatory failed to build. What should you do?
|
|||
# True, and return the return code.
|
||||
def RunCommand(command, always_silent=False):
|
||||
try:
|
||||
# Dart IO respects the following environment variables to configure the
|
||||
# HttpClient proxy: https://api.dartlang.org/stable/1.22.1/dart-io/HttpClient/findProxyFromEnvironment.html
|
||||
# We strip these to avoid problems with pub build and transformers.
|
||||
no_http_proxy_env = os.environ.copy()
|
||||
no_http_proxy_env.pop('http_proxy', None)
|
||||
no_http_proxy_env.pop('HTTP_PROXY', None)
|
||||
no_http_proxy_env.pop('https_proxy', None)
|
||||
no_http_proxy_env.pop('HTTPS_PROXY', None)
|
||||
subprocess.check_output(command,
|
||||
stderr=subprocess.STDOUT,
|
||||
env=no_http_proxy_env)
|
||||
stderr=subprocess.STDOUT)
|
||||
return 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
if not always_silent:
|
||||
|
@ -89,23 +79,15 @@ def RunCommand(command, always_silent=False):
|
|||
DisplayFailureMessage()
|
||||
return e.returncode
|
||||
|
||||
def CreateTimestampFile(options):
|
||||
if options.stamp != '':
|
||||
dir_name = os.path.dirname(options.stamp)
|
||||
if dir_name != '':
|
||||
if not os.path.exists(dir_name):
|
||||
os.mkdir(dir_name)
|
||||
open(options.stamp, 'w').close()
|
||||
|
||||
def BuildArguments():
|
||||
result = argparse.ArgumentParser(usage=usage)
|
||||
result.add_argument("--dart-executable", help="dart executable", default=None)
|
||||
result.add_argument("--pub-executable", help="pub executable", default=None)
|
||||
result.add_argument("--dart2js-executable", help="dart2js executable",
|
||||
default=None)
|
||||
result.add_argument("--directory", help="observatory root", default=None)
|
||||
result.add_argument("--command", help="[get, build, deploy]", default=None)
|
||||
result.add_argument("--command", help="[build, deploy]", default=None)
|
||||
result.add_argument("--silent", help="silence all output", default=None)
|
||||
result.add_argument("--sdk", help="Use prebuilt sdk", default=None)
|
||||
result.add_argument("--stamp", help="Write a stamp file", default='')
|
||||
return result
|
||||
|
||||
def ProcessOptions(options, args):
|
||||
|
@ -130,113 +112,97 @@ def ProcessOptions(options, args):
|
|||
if options.command is None or options.directory is None:
|
||||
return False
|
||||
|
||||
# Set a default value for pub_snapshot.
|
||||
options.pub_snapshot = None
|
||||
|
||||
# If we have a working pub executable, try and use that.
|
||||
# TODO(whesse): Drop the pub-executable option if it isn't used.
|
||||
if options.pub_executable is not None:
|
||||
# If a dart2js execuble was provided, try and use that.
|
||||
# TODO(whesse): Drop the dart2js-executable option if it isn't used.
|
||||
if options.dart2js_executable is not None:
|
||||
try:
|
||||
if 0 == RunCommand([options.pub_executable, '--version'],
|
||||
if 0 == RunCommand([options.dart2js_executable, '--version'],
|
||||
always_silent=True):
|
||||
return True
|
||||
except OSError as e:
|
||||
pass
|
||||
options.pub_executable = None
|
||||
options.dart2js_executable = None
|
||||
|
||||
# Use the checked in dart2js executable.
|
||||
if options.sdk and utils.CheckedInSdkCheckExecutable():
|
||||
# Use the checked in pub executable.
|
||||
options.pub_snapshot = os.path.join(utils.CheckedInSdkPath(),
|
||||
'bin',
|
||||
'snapshots',
|
||||
'pub.dart.snapshot');
|
||||
dart2js_binary = 'dart2js.bat' if utils.IsWindows() else 'dart2js'
|
||||
options.dart2js_executable = os.path.join(utils.CheckedInSdkPath(),
|
||||
'bin',
|
||||
dart2js_binary)
|
||||
try:
|
||||
if 0 == RunCommand([utils.CheckedInSdkExecutable(),
|
||||
options.pub_snapshot,
|
||||
'--version'], always_silent=True):
|
||||
if 0 == RunCommand([options.dart2js_executable, '--version'],
|
||||
always_silent=True):
|
||||
return True
|
||||
except OSError as e:
|
||||
pass
|
||||
options.pub_snapshot = None
|
||||
options.dart2js_executable = None
|
||||
|
||||
# We need a dart executable.
|
||||
# We need a dart executable and will run from source
|
||||
return (options.dart_executable is not None)
|
||||
|
||||
def ChangeDirectory(directory):
|
||||
os.chdir(directory);
|
||||
|
||||
def PubCommand(dart_executable,
|
||||
pub_executable,
|
||||
pub_snapshot,
|
||||
command,
|
||||
silent):
|
||||
if pub_executable is not None:
|
||||
executable = [pub_executable]
|
||||
elif pub_snapshot is not None:
|
||||
executable = [utils.CheckedInSdkExecutable(), pub_snapshot]
|
||||
# - Copy over the filtered web directory
|
||||
# - Merge in the .js file
|
||||
# - Copy over the filtered dependency lib directories
|
||||
# - Copy over the filtered observatory package
|
||||
def Deploy(output_dir, web_dir, observatory_lib, js_file, pub_packages_dir):
|
||||
shutil.rmtree(output_dir)
|
||||
os.makedirs(output_dir)
|
||||
|
||||
output_web_dir = os.path.join(output_dir, 'web')
|
||||
shutil.copytree(web_dir, output_web_dir, ignore=IGNORE_PATTERNS)
|
||||
os.utime(os.path.join(output_web_dir, 'index.html'), None)
|
||||
|
||||
shutil.copy(js_file, output_web_dir)
|
||||
|
||||
packages_dir = os.path.join(output_web_dir, 'packages')
|
||||
os.makedirs(packages_dir)
|
||||
for subdir in os.listdir(pub_packages_dir):
|
||||
libdir = os.path.join(pub_packages_dir, subdir, 'lib')
|
||||
if os.path.isdir(libdir):
|
||||
shutil.copytree(libdir, os.path.join(packages_dir, subdir),
|
||||
ignore=IGNORE_PATTERNS)
|
||||
shutil.copytree(observatory_lib, os.path.join(packages_dir, 'observatory'),
|
||||
ignore=IGNORE_PATTERNS)
|
||||
|
||||
def Build(dart_executable,
|
||||
dart2js_executable,
|
||||
script_path,
|
||||
output_path,
|
||||
packages_path,
|
||||
silent):
|
||||
if dart2js_executable is not None:
|
||||
command = [dart2js_executable]
|
||||
else:
|
||||
if not silent:
|
||||
DisplayBootstrapWarning()
|
||||
executable = [dart_executable, PUB_PATH]
|
||||
# Prevent the bootstrap Dart executable from running in regular
|
||||
# development flow.
|
||||
# REMOVE THE FOLLOWING LINE TO USE the dart_bootstrap binary.
|
||||
# return False
|
||||
command = [dart_executable, DART2JS_PATH]
|
||||
command += ['-DOBS_VER=' + utils.GetVersion(no_git_hash=True)]
|
||||
command += [script_path, '-o', output_path, '--packages=%s' % packages_path]
|
||||
# Add the defaults pub used
|
||||
command += ['--minify']
|
||||
if not silent:
|
||||
print >> sys.stderr, ('Running command "%s"') % (executable + command)
|
||||
return RunCommand(executable + command)
|
||||
|
||||
def Deploy(input_dir, output_dir):
|
||||
shutil.rmtree(output_dir)
|
||||
shutil.copytree(input_dir, output_dir, ignore=IGNORE_PATTERNS)
|
||||
index_file = os.path.join(output_dir, 'web', 'index.html')
|
||||
os.utime(index_file, None)
|
||||
return 0
|
||||
|
||||
def RewritePubSpec(input_path, output_path, search, replace):
|
||||
with open(input_path, 'rb') as input_file:
|
||||
input_data = input_file.read()
|
||||
input_data = input_data.replace(search, replace)
|
||||
with open(output_path, 'wb+') as output_file:
|
||||
output_file.write(input_data)
|
||||
print >> sys.stderr, 'Running command "%s"' % command
|
||||
return RunCommand(command)
|
||||
|
||||
def ExecuteCommand(options, args):
|
||||
cmd = options.command
|
||||
if (cmd == 'get'):
|
||||
# Always remove pubspec.lock before running 'pub get'.
|
||||
try:
|
||||
os.remove('pubspec.lock');
|
||||
except OSError as e:
|
||||
pass
|
||||
return PubCommand(options.dart_executable,
|
||||
options.pub_executable,
|
||||
options.pub_snapshot,
|
||||
['get', '--offline'],
|
||||
options.silent)
|
||||
elif (cmd == 'build'):
|
||||
return PubCommand(options.dart_executable,
|
||||
options.pub_executable,
|
||||
options.pub_snapshot,
|
||||
['build',
|
||||
'-DOBS_VER=' + utils.GetVersion(no_git_hash=True),
|
||||
'--output', args[0]],
|
||||
options.silent)
|
||||
if (cmd == 'build'):
|
||||
return Build(options.dart_executable,
|
||||
options.dart2js_executable,
|
||||
args[0],
|
||||
args[1],
|
||||
args[2],
|
||||
options.silent)
|
||||
elif (cmd == 'deploy'):
|
||||
Deploy('build', 'deployed')
|
||||
elif (cmd == 'rewrite'):
|
||||
RewritePubSpec(args[0], args[1], args[2], args[3])
|
||||
Deploy(args[0], args[1], args[2], args[3], args[4])
|
||||
else:
|
||||
print >> sys.stderr, ('ERROR: command "%s" not supported') % (cmd)
|
||||
return -1;
|
||||
|
||||
def main():
|
||||
# Sanity check that localhost can be resolved.
|
||||
try:
|
||||
socket.gethostbyname('localhost')
|
||||
except:
|
||||
print("The hostname 'localhost' could not be resolved. Please fix your"
|
||||
"/etc/hosts file and try again")
|
||||
return -1
|
||||
# Parse the options.
|
||||
parser = BuildArguments()
|
||||
(options, args) = parser.parse_known_args()
|
||||
|
@ -246,24 +212,16 @@ def main():
|
|||
# Calculate absolute paths before changing directory.
|
||||
if (options.dart_executable != None):
|
||||
options.dart_executable = os.path.abspath(options.dart_executable)
|
||||
if (options.pub_executable != None):
|
||||
options.pub_executable = os.path.abspath(options.pub_executable)
|
||||
if (options.pub_snapshot != None):
|
||||
options.pub_snapshot = os.path.abspath(options.pub_snapshot)
|
||||
if (options.stamp != ''):
|
||||
options.stamp = os.path.abspath(options.stamp)
|
||||
if (options.dart2js_executable != None):
|
||||
options.dart2js_executable = os.path.abspath(options.dart2js_executable)
|
||||
if len(args) == 1:
|
||||
args[0] = os.path.abspath(args[0])
|
||||
try:
|
||||
# Pub must be run from the project's root directory.
|
||||
ChangeDirectory(options.directory)
|
||||
result = ExecuteCommand(options, args)
|
||||
if result == 0:
|
||||
CreateTimestampFile(options)
|
||||
return result
|
||||
return ExecuteCommand(options, args)
|
||||
except:
|
||||
DisplayFailureMessage()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main());
|
||||
|
|
Loading…
Reference in a new issue