【问题标题】:Is there a way to have a Swift script use multiple files有没有办法让 Swift 脚本使用多个文件
【发布时间】:2015-03-20 01:55:27
【问题描述】:

我正在尝试使用 Swift(不是 Xcode 项目)编写脚本。要清楚,我的文件的第一行是

 #!/usr/bin/swift

我只是从命令行调用它。

但是,我不知道如何让该脚本使用另一个 .swift 文件中的代码。它不会从同一个目录中获取它,并且我无法看到import

支持吗?

【问题讨论】:

  • 相关问题在这里stackoverflow.com/questions/27872589/…。 – 我假设这还不可能。
  • 我想这一定是可能的,因为 XCode 可以做到(通过将正确的参数传递给编译器)。但显然到目前为止没有人弄清楚。例如,在 Playground 中,您也可以包含多个文件(在某处有答案)。但这又是在 XCode 中。
  • 如果我想编译,我可以用swiftc -o ./myscript main.swift b.swift 来编译——这可能是唯一的解决方案(我可以围绕它写一个 bash——呃)
  • 这个稍微好点。对我来说,我有一些不会有太大变化的 swift 文件和一个更像是脚本的文件。所以,我可以swiftc -emit-library -olibutils.dylib a.swift b.swift c.swift 然后将#!/usr/bin/swift -I . -lutils 放在我的脚本顶部。这些选项都不是答案,但也许这会对某人有所帮助

标签: swift command-line


【解决方案1】:

我使用 Marián 解决方案的变体:

cat A.swift B.swift main.swift | swift -

【讨论】:

  • 请记住,文件的顺序很重要,所以在大多数情况下,如果 main 引用 A 和 B 中的类,则需要最后定义。
  • 问题 - “swift -”中的连字符是什么意思?
  • 连字符代表“从标准输入读取源代码”:unix.stackexchange.com/a/16364
  • 看不懂,能不能详细解释一下。
【解决方案2】:

我目前的解决方案是一个简单的shell脚本,将所有文件连接成一个并执行连接的文件:

TMPFILE=`mktemp /tmp/Project.swift.XXXXXX` || exit 1
trap "rm -f $TMPFILE" EXIT 
cat *.swift > $TMPFILE
swift $TMPFILE

【讨论】:

    【解决方案3】:

    有更好的方法!

    #!/usr/bin/swift -frontend -interpret -enable-source-import -I.
    
    import other_file  // this imports other_file.swift in the same folder
    
    funcFromOtherFile()
    

    如果你想从ExampleFolder 导入文件,就像这样:

    #!/usr/bin/swift -frontend -interpret -enable-source-import -I./ExampleFolder
    
    import other_file  // this imports ./ExampleFolder/other_file.swift
    
    funcFromOtherFile()
    

    【讨论】:

    • 这会很棒!但是当我使用它并尝试导入 Foundation 时,我得到了cannot load underlying module for 'CoreFoundation',您知道如何解决这个问题吗?
    • 我认为现在整个解决方案都被打破了。耻辱。
    【解决方案4】:

    this answer 的启发,我编写了一个简单的Python 脚本来复制gcc-esque 的编译过程。你可以find the gist here

    核心思想是 (1) 将所有 Swift 文件合并为一个,(2) 添加一些样板代码(如果适用),然后 (3) 将合并后的文本写入一个临时文件并让 Swift 执行该文件。

    #!/usr/bin/env python3
    
    """
    Simple bootstrap script for quickly prototyping
    Swift-based macOS applications.
    
    usage: swift-run.py [-h] [-c SWIFT_COMPILER] [-a ARGS] [-m MAIN] [-s SAVE]
                        file [file ...]
    
    positional arguments:
      file                  list of files to run
    
    optional arguments:
      -h, --help            show this help message and exit
      -c SWIFT_COMPILER, --swift-compiler SWIFT_COMPILER
                            swift compiler path (default: swift)
      -a ARGS, --args ARGS  swift compiler args (default: )
      -m MAIN, --main MAIN  main SwiftUI view (default:
                            ContentView().frame(maxWidth: .infinity, maxHeight:
                            .infinity))
      -s SAVE, --save SAVE  saves the joined swift files + boilerplate to this
                            file (default: )
    
    """
    
    import argparse
    import os
    import subprocess
    import tempfile
    from typing import List
    
    DEFAULTS = {
        "main_view": "ContentView().frame(maxWidth: .infinity, maxHeight: .infinity)",
        "swift_args": "",
        "swift_exec": "swift",
    }
    
    
    def join_files(files: List[str], boilerplate: str = ""):
        all_text = ""
    
        for file in files:
            with open(file, "r") as f:
                all_text += f.read() + "\n\n"
    
        all_text += boilerplate
        return all_text
    
    
    def exec_text(text: str, swift_exec: str = "", swift_args: str = "", script_out: str = None):
        with tempfile.TemporaryDirectory() as tmp_dir:
            if script_out is None or script_out.strip() == "":
                script_out = os.path.join(tmp_dir, "main.swift")
            
            with open(script_out, "w") as f:
                f.write(text)
    
            cmd = f"{swift_exec} {swift_args} {script_out}"
            print(cmd)
    
            subprocess.run(
                cmd.split(),
                check=True,
                capture_output=True,
            )
    
    
    def get_boilerplate(main_view: str = "ContentView()"):
        return (
            """
    // Run any SwiftUI view as a Mac app.
    
    import Cocoa
    import SwiftUI
    
    NSApplication.shared.run {
        """
            + main_view
            + """
    }
    
    extension NSApplication {
        public func run<V: View>(@ViewBuilder view: () -> V) {
            let appDelegate = AppDelegate(view())
            NSApp.setActivationPolicy(.regular)
            mainMenu = customMenu
            delegate = appDelegate
            run()
        }
    }
    
    // Inspired by https://www.cocoawithlove.com/2010/09/minimalist-cocoa-programming.html
    
    extension NSApplication {
        var customMenu: NSMenu {
            let appMenu = NSMenuItem()
            appMenu.submenu = NSMenu()
            let appName = ProcessInfo.processInfo.processName
            appMenu.submenu?.addItem(NSMenuItem(title: "About \(appName)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: ""))
            appMenu.submenu?.addItem(NSMenuItem.separator())
            let services = NSMenuItem(title: "Services", action: nil, keyEquivalent: "")
            self.servicesMenu = NSMenu()
            services.submenu = self.servicesMenu
            appMenu.submenu?.addItem(services)
            appMenu.submenu?.addItem(NSMenuItem.separator())
            appMenu.submenu?.addItem(NSMenuItem(title: "Hide \(appName)", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h"))
            let hideOthers = NSMenuItem(title: "Hide Others", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h")
            hideOthers.keyEquivalentModifierMask = [.command, .option]
            appMenu.submenu?.addItem(hideOthers)
            appMenu.submenu?.addItem(NSMenuItem(title: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: ""))
            appMenu.submenu?.addItem(NSMenuItem.separator())
            appMenu.submenu?.addItem(NSMenuItem(title: "Quit \(appName)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
            
            let windowMenu = NSMenuItem()
            windowMenu.submenu = NSMenu(title: "Window")
            windowMenu.submenu?.addItem(NSMenuItem(title: "Minmize", action: #selector(NSWindow.miniaturize(_:)), keyEquivalent: "m"))
            windowMenu.submenu?.addItem(NSMenuItem(title: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: ""))
            windowMenu.submenu?.addItem(NSMenuItem.separator())
            windowMenu.submenu?.addItem(NSMenuItem(title: "Show All", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: "m"))
            
            let mainMenu = NSMenu(title: "Main Menu")
            mainMenu.addItem(appMenu)
            mainMenu.addItem(windowMenu)
            return mainMenu
        }
    }
    
    class AppDelegate<V: View>: NSObject, NSApplicationDelegate, NSWindowDelegate {
        init(_ contentView: V) {
            self.contentView = contentView
            
        }
        var window: NSWindow!
        var hostingView: NSView?
        var contentView: V
        
        func applicationDidFinishLaunching(_ notification: Notification) {
            window = NSWindow(
                contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
                styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
                backing: .buffered, defer: false)
            window.center()
            window.setFrameAutosaveName("Main Window")
            hostingView = NSHostingView(rootView: contentView)
            window.contentView = hostingView
            window.makeKeyAndOrderFront(nil)
            window.delegate = self
            NSApp.activate(ignoringOtherApps: true)
        }
    }
    """
        )
    
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
        parser.add_argument(
            "files", metavar="file", type=str, nargs="+", help="list of files to run"
        )
        parser.add_argument(
            "-c",
            "--swift-compiler",
            help="swift compiler path",
            type=str,
            default=DEFAULTS["swift_exec"],
        )
        parser.add_argument(
            "-a",
            "--args",
            help="swift compiler args",
            type=str,
            default=DEFAULTS["swift_args"],
        )
        parser.add_argument(
            "-m",
            "--main",
            help="main SwiftUI view",
            type=str,
            default=DEFAULTS["main_view"],
        )
        parser.add_argument(
            "-s",
            "--save",
            help="saves the joined swift files + boilerplate to this file",
            type=str,
            default="",
        )
    
        args = parser.parse_args()
        print(args)
    
        exec_text(
            join_files(args.files, boilerplate=get_boilerplate(args.main)),
            swift_exec=args.swift_compiler,
            swift_args=args.args,
            script_out=args.save
        )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-04
      • 2017-06-08
      • 1970-01-01
      • 2023-02-22
      • 2017-07-06
      • 2011-04-20
      • 2020-08-17
      • 2022-11-19
      相关资源
      最近更新 更多