Command-line entry point.

    :param arguments: List of strings that contain the command-line arguments.
       When ``None``, the command-line arguments are looked up in ``sys.argv``
       (``sys.argv[0]`` is ignored).
    :return: This function has no return value.
    :raise SystemExit: The command-line arguments are invalid.

def main(arguments=None):
    """Command-line entry point.

    :param arguments: List of strings that contain the command-line arguments.
       When ``None``, the command-line arguments are looked up in ``sys.argv``
       (``sys.argv[0]`` is ignored).
    :return: This function has no return value.
    :raise SystemExit: The command-line arguments are invalid.
    """

    # Parse command-line arguments.
    if arguments is None:
        arguments = sys.argv[1:]
    arguments = cli.parse_args(arguments)

    # Read the procfile.
    try:
        process_types = procfile.loadfile(arguments.procfile)
    except FileNotFoundError:
        sys.stderr.write('Procfile not found at "%s".' % arguments.procfile)
        sys.exit(2)

    # Read the env file(s).
    env = {}
    if arguments.use_env:
        for path in arguments.envfiles:
            try:
                env.update(dotenvfile.loadfile(path))
            except FileNotFoundError:
                sys.stderr.write(
                    'Warning: environment file "%s" not found.\n' % path
                )

    # Determine how many processes of each type we need.
    effective_scale = {}
    requested_scale = dict(arguments.scale)
    for label in process_types:
        effective_scale[label] = requested_scale.get(
            label,
            requested_scale['*'],
        )

    # Start the event loop.
    loop = asyncio.get_event_loop()

    # Register for shutdown events (idempotent, trap once only).
    shutdown = asyncio.Future()
    def stop_respawning():
        if shutdown.done():
            return
        shutdown.set_result(None)
        loop.remove_signal_handler(signal.SIGINT)
    loop.add_signal_handler(signal.SIGINT, stop_respawning)

    # Spawn tasks.
    tasks = []
    for label, count in effective_scale.items():
        process_type = process_types[label]
        the_cmd = shlex.split(process_type['cmd'])
        the_env = merge_envs(os.environ, env, process_type['env'])
        for i in range(count):
            task = loop.create_task(run_and_respawn(
                name='%s.%i' % (label, i),
                cmd=the_cmd,
                env=the_env,
                loop=loop,
                shutdown=shutdown,
                utc=arguments.use_utc,
            ))
            tasks.append(task)

    if not tasks:
        sys.stderr.write('Nothing to run.\n')
        sys.exit(2)

    # Wait for all tasks to complete.
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()