#!/usr/bin/env python3
# split-cmdline - Split swift compiler command lines ------------*- python -*-
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
# ----------------------------------------------------------------------------
#
# Split swift compiler command lines into multiple lines.
#
# Reads the command line from stdin and outputs the split line to stdout.
# Example:
#
# $ swiftc -c hello.swift -### | split-cmdline
# /path-to-swift/bin/swift \
#   -frontend \
#   -c \
#   -primary-file hello.swift \
#   -target x86_64-apple-macosx10.9 \
#   -enable-objc-interop \
#   -color-diagnostics \
#   -module-name hello \
#   -o hello.o
#
# Example usage in vim:
# *) make sure that split-cmdline is in the $PATH
# *) copy-paste the swift command line the text buffer
# *) select the command line
# *) go to the command prompt (= press ':')
# :'<,'>!split-cmdline
#
# ----------------------------------------------------------------------------

import os
import re
import shlex
import sys


def main():
    for line in sys.stdin:
        first = True
        remaining_arg_params = 0
        # Handle escaped spaces
        args = shlex.split(line)
        cmd_name = args[0]
        for arg in args:
            first, remaining_arg_params = print_arg(arg, first, remaining_arg_params, cmd_name)

        # Print 2 new lines after each command line
        print("\n")


def print_arg(arg, first, remaining_arg_params, cmd_name):
    if arg == "":
        return first, remaining_arg_params
    if not first:
        # Print option arguments in the same line
        print(" " if remaining_arg_params else " \\\n  ", end="")

    # Expand @ option files
    m = re.match(r"^@(\S+(\.resp))$", arg)
    if m:
        cmd_file = m.group(1)
        cmd_first = False
        if os.path.isfile(cmd_file):
            cmd_first = True
            with open(cmd_file) as f:
                for ln in f.readlines():
                    cmd_args = shlex.split(ln)
                    for cmd_arg in cmd_args:
                        cmd_first, remaining_arg_params = print_arg(cmd_arg, cmd_first, remaining_arg_params, cmd_name)
        else:
            print(arg, end="")
        return cmd_first, remaining_arg_params

    m = re.match(r"^@(\S+(\.txt|FileList))$", arg)
    if m:
        cmd_file = m.group(1)
        if os.path.isfile(cmd_file):
            with open(cmd_file) as f:
                for ln in f.readlines():
                    for name in ln.rstrip().split(";"):
                        print(name + " \\")
        return True, remaining_arg_params

    if " " in arg or ";" in arg:
        print('"' + arg + '"', end="")
    else:
        print(arg, end="")

    # A hard-coded list of options which expect a parameter
    if arg in [
        # This list is generated by scraping --help menus from running:
        #
        #   $ ( \
        #     swift --help;                 \
        #     swift build --help;           \
        #     swift run --help;             \
        #     swift repl --help;            \
        #     swift test --help;            \
        #     swift package --help;         \
        #     swiftc --help;                \
        #     xcrun swift-frontend --help;  \
        #     xcrun swift-api-digester --help; \
        #     clang --help;                 \
        #   ) | rg -o -r '$3 $2' -- ' ((-[\w-]+),)? (-[\w-]+) <' \
        #     | sed 's/ *$//' | tr ' ' '\n' \
        #     | sort | uniq \
        #     | grep -v -e '-c$' \
        #     | sed -E 's/(.*)/"\1",/'
        "--analyzer-output",
        "--build-system",
        "--cache-path",
        "--config-path",
        "--configuration",
        "--default-registry-url",
        "--experimental-traits",
        "--explicit-target-dependency-import-check",
        "--filter",
        "--jobs",
        "--manifest-cache",
        "--netrc-file",
        "--num-workers",
        "--package-path",
        "--pkg-config-path",
        "--product",
        "--resolver-fingerprint-checking",
        "--resolver-signing-entity-checking",
        "--sanitize",
        "--scratch-path",
        "--sdk",
        "--security-path",
        "--skip",
        "--specifier",
        "--swift-sdk",
        "--swift-sdks-path",
        "--target",
        "--toolchain",
        "--triple",
        "--xunit-output",
        "-B",
        "-D",
        "-F",
        "-Fsystem",
        "-G",
        "-I",
        "-Isystem",
        "-L",
        "-MF",
        "-MJ",
        "-MQ",
        "-MT",
        "-T",
        "-U",
        "-Xanalyzer",
        "-Xarch_device",
        "-Xarch_host",
        "-Xassembler",
        "-Xcc",
        "-Xclang",
        "-Xcuda-fatbinary",
        "-Xcuda-ptxas",
        "-Xcxx",
        "-Xlinker",
        "-Xopenmp-target",
        "-Xpreprocessor",
        "-Xswiftc",
        "-access-notes-path",
        "-allowable-client",
        "-api-diff-data-dir",
        "-api-diff-data-file",
        "-arcmt-migrate-report-output",
        "-assert-config",
        "-autolink-library",
        "-b",
        "-backup-module-interface-path",
        "-blocklist-file",
        "-build-id",
        "-cache-replay-prefix-map",
        "-cas-path",
        "-cas-plugin-option",
        "-cas-plugin-path",
        "-cc1-args",
        "-clang-build-session-file",
        "-clang-scanner-module-cache-path",
        "-clang-target",
        "-clang-target-variant",
        "-compare-to-baseline-path",
        "-const-gather-protocols-file",
        "-coverage-prefix-map",
        "-cxx-isystem",
        "-darwin-target-variant",
        "-darwin-target-variant-triple",
        "-debug-info-format",
        "-debug-module-path",
        "-debug-prefix-map",
        "-define-availability",
        "-dependency-dot",
        "-dependency-file",
        "-dependency-scan-cache-path",
        "-diagnostic-documentation-path",
        "-diagnostic-style",
        "-digester-breakage-allowlist-path",
        "-digester-mode",
        "-dsym-dir",
        "-dump-api-path",
        "-dump-migration-states-dir",
        "-dump-scope-maps",
        "-dumpdir",
        "-e",
        "-embed-tbd-for-module",
        "-emit-abi-descriptor-path",
        "-emit-api-descriptor-path",
        "-emit-clang-header-path",
        "-emit-const-values-path",
        "-emit-dependencies-path",
        "-emit-digester-baseline-path",
        "-emit-fixits-path",
        "-emit-loaded-module-trace-path",
        "-emit-migrated-file-path",
        "-emit-module-dependencies-path",
        "-emit-module-doc-path",
        "-emit-module-interface-path",
        "-emit-module-path",
        "-emit-module-semantic-info-path",
        "-emit-module-serialize-diagnostics-path",
        "-emit-module-source-info-path",
        "-emit-module-summary-path",
        "-emit-objc-header-path",
        "-emit-reference-dependencies-path",
        "-emit-remap-file-path",
        "-emit-tbd-path",
        "-enable-experimental-feature",
        "-enable-upcoming-feature",
        "-explain-module-dependency",
        "-explain-module-dependency-detailed",
        "-explicit-swift-module-map-file",
        "-export-as",
        "-external-plugin-path",
        "-fdepscan-share-identifier",
        "-file-compilation-dir",
        "-file-prefix-map",
        "-filelist",
        "-fmodules-user-build-path",
        "-framework",
        "-group-info-path",
        "-iapinotes-modules",
        "-iapinotes-path",
        "-idirafter",
        "-iframework",
        "-iframeworkwithsysroot",
        "-imacros",
        "-in-process-plugin-server-path",
        "-include",
        "-include-pch",
        "-index-file-path",
        "-index-store-path",
        "-index-unit-output-path",
        "-index-unit-output-path-filelist",
        "-iprefix",
        "-iquote",
        "-isysroot",
        "-isystem",
        "-isystem-after",
        "-ivfsoverlay",
        "-ivfsstatcache",
        "-iwithprefix",
        "-iwithprefixbefore",
        "-iwithsysroot",
        "-j",
        "-l",
        "-libc",
        "-load-plugin-executable",
        "-load-plugin-library",
        "-load-resolved-plugin",
        "-locale",
        "-localization-path",
        "-lto-library",
        "-meabi",
        "-mllvm",
        "-mmlir",
        "-module",
        "-module-abi-name",
        "-module-alias",
        "-module-cache-path",
        "-module-can-import",
        "-module-dependency-dir",
        "-module-link-name",
        "-module-name",
        "-mthread-model",
        "-num-threads",
        "-o",
        "-output-file-map",
        "-output-filelist",
        "-package-name",
        "-placeholder-dependency-module-map-file",
        "-plugin-path",
        "-prebuilt-module-cache-path",
        "-primary-file",
        "-primary-filelist",
        "-project-name",
        "-require-explicit-availability-target",
        "-runtime-compatibility-version",
        "-s",
        "-save-optimization-record-passes",
        "-save-optimization-record-path",
        "-scanner-prefix-map",
        "-scanner-prefix-map-sdk",
        "-scanner-prefix-map-toolchain",
        "-sdk",
        "-serialize-breaking-changes-path",
        "-serialize-diagnostics",
        "-serialize-diagnostics-path",
        "-serialized-path-obfuscate",
        "-supplementary-output-file-map",
        "-swift-isa-ptrauth-mode",
        "-swift-ptrauth-mode",
        "-swift-version",
        "-sysroot",
        "-target",
        "-target-cpu",
        "-target-min-inlining-version",
        "-target-variant",
        "-tbd-compatibility-version",
        "-tbd-current-version",
        "-tbd-install_name",
        "-tools-directory",
        "-use-interface-for-module",
        "-user-module-version",
        "-verify-additional-file",
        "-verify-additional-prefix",
        "-verify-generic-signatures",
        "-vfsoverlay",
        "-visualc-tools-root",
        "-visualc-tools-version",
        "-windows-sdk-root",
        "-windows-sdk-version",
        "-working-directory",
        "-x",
        "-z",
    ] + [
        # This list contains options that do not appear in --help menus:
        "-Xfrontend",
        "-Xllvm",
        "-add_ast_path",
        "-arch",
        "-bad-file-descriptor-retry-count",
        "-emit-ir",
        "-emit-sil",
        "-emit-symbol-graph-dir",
        "-ferror-limit",
        "-fileno",
        "-fmodule-feature",
        "-import-bridging-header",
        "-import-objc-header",
        "-internal-externc-isystem",
        "-internal-import-bridging-header",
        "-library-level",
        "-macosx_version_min",
        "-pic-level",
        "-resource-dir",
        "-rpath",
        "-stack-protector",
        "-stack-protector-buffer-size",
        "-syslibroot",
        "-target-abi",
        "-target-feature",
        "-target-linker-version",
        "-target-sdk-name",
        "-target-sdk-version",
        "-target-variant-sdk-version",
        "-triple",
        "-typecheck-module-from-interface",
    ]:
        remaining_arg_params = 1
    elif arg in [
        "-swift-module-cross-import",
    ]:
        remaining_arg_params = 2
    elif arg in [
        "-module-can-import-version",
    ]:
        remaining_arg_params = 3
    # Heuristic: options ending in -path expect an argument
    elif arg != "-finclude-tree-preserve-pch-path" and arg.startswith("-") and arg.endswith("-path"):
        remaining_arg_params = 1
    elif arg == "-c" and (cmd_name == "swift" or os.path.basename(cmd_name) == "clang"):
        remaining_arg_params = 1
    elif remaining_arg_params:
        remaining_arg_params -= 1
    return False, remaining_arg_params


if __name__ == "__main__":
    main()
