Swift on Linux - 导入第三方模块

时间:2016-08-22 04:44:26

标签: linux swift import

我正在Linux上编写一些入门级的swift代码作为学习练习。

作为一般任务,我希望在我自己的代码中使用第三方Swift模块。我们称这个模块为“Foo”。 Foo模块有一个Package.swift文件,在该目录中运行swift build之后,它创建了.build/debug/libFoo.so

现在我想做两件事:

  1. 能够在REPL中import Foo
  2. 能够在我自己的swift程序中导入Foo,可能是通过链接这个共享对象。
  3. 我觉得这两项任务都是相关的,所以现在它们都是同一个问题。

    对于1.,我不明白包裹如何被REPL“找到”。我试过swift -F .build/debug -framework Foo,但我得到了“没有这样的模块”错误。我也尝试了swift -I .build/debug同样的结果。

    对于2.,我检查了swiftc --help并且有-L-l个选项但是我无法找到使用这些选项的正确方法:

    $ swiftc main.swift -L ../foo.git/.build/debug -llibFoo.so
    main.swift:1:8: error: no such module 'Foo'
    import Foo
           ^
    

    我正在使用Swift 2.2或3.0(使用swim而不是swift build用于2.2,因为没有swift build - 但它产生相同的输出我相信)。

    请注意,我理解swift build可以自动下载和构建第三方模块,但我想知道如何合并磁盘模块,因为它们可能是我自己的工作进度模块。< / p>

    编辑:我尝试使用swift3进行一些实验,基于一个发现,你可以使用本地路径作为Package url:列表中的dependencies:参数,至少对于本地开发而言。

    我创建了一个目录BarBar/Package.swift

    import PackageDescription
    let package = Package(name: "Bar")
    

    我还创建了Bar/Sources/bar.swift,其中包含:

    public func bar(arg: Int) -> Int {
        return arg * 2
    }
    

    目的是模块Bar提供名为bar(arg:)的函数。

    我做了git initgit add .git commit -m "Initial commit.",然后git tag 1.0.0为此模块创建了一个标记为的本地 git repo。

    然后回到顶层,我创建了目录FooFoo/Package.swift

    import PackageDescription
    let package = Package(
        name: "Foo",
        dependencies: [ .Package(url: "../Bar", majorVersion: 1) ]
    )
    

    请注意../Bar的相对路径。

    我还创建了Foo/Sources/main.swift

    import Bar
    print(bar(arg: 11))
    

    现在当我在swift buildFoo时,它会克隆Bar并构建它。然而,我得到以下错误; 没有这样的模块

    $ swift build
    Compile Swift Module 'Bar' (1 sources)
    Compile Swift Module 'Foo' (1 sources)
    .../Foo/Sources/main.swift:1:8: error: no such module 'Bar'
    import Bar
           ^
    <unknown>:0: error: build had 1 command failures
    error: exit(1): .../swift-3.0-PREVIEW-4-ubuntu14.04/usr/bin/swift-build-tool -f .../Foo/.build/debug.yaml
    

    奇怪的是,如果我再次执行完全相同的构建命令,我会得到一个不同的错误:

    $ swift build
    Compile Swift Module 'Foo' (1 sources)
    Linking .build/debug/Bar
    .../Foo/Sources/main.swift:3:7: error: use of unresolved identifier 'bar'
    print(bar(arg: 11))
          ^~~
    <unknown>:0: error: build had 1 command failures
    error: exit(1): .../swift-3.0-PREVIEW-4-ubuntu14.04/usr/bin/swift-build-tool -f .../Foo/.build/debug.yaml
    

    我曾希望这可行。

2 个答案:

答案 0 :(得分:3)

  

能够在我自己的swift程序中导入Foo,可能是通过链接这个共享对象。

使用您在“编辑”之后在问题中发布的示例,如果您使用swift build,这似乎可以正常工作。 Swift Package Manager将为您处理所有依赖项,即使它们是在磁盘上(这适用于Swift 3和4):

$ cd Foo

$ swift build
Cloning /path/to/Bar
HEAD is now at 0c3fd6e Initial commit.
Resolved version: 1.0.0
Compile Swift Module 'Bar' (1 sources)
Compile Swift Module 'Foo' (1 sources)
Linking ./.build/debug/Foo

$ .build/debug/Foo
22

请注意,Foo / .build / debug不包含任何.so文件:

$ ls Foo/.build/debug
Bar.build  Bar.swiftdoc  Bar.swiftmodule  Foo  Foo.build  Foo.swiftdoc  Foo.swiftmodule  ModuleCache

我相信会使用.swiftdoc和.swiftmodule文件。

  

能够在REPL中导入Foo。

这部分有点麻烦,但我找到了解决方案here。要将其应用于您的示例,您有两个选择:

  • Use swift build with extra flags(适用于Swift 3和4):

    $ cd Bar
    
    $ swift build -Xswiftc -emit-library
    Compile Swift Module 'Bar' (1 sources)
    
    $ swift -I .build/debug -L . -lBar
      1> import Bar
      2> bar(arg: 11) 
    $R0: Int = 22
      3> 
    

    这会在当前目录中创建libBar.so:

    $ ls
    libBar.so  Package.swift  Sources
    


  • Update your Package.manifest(这是特定于Swift 4):

    1. 更新的Package.manifest看起来像这样:

      // swift-tools-version:4.0
      import PackageDescription
      
      let package = Package(
          name: "Bar",
          products: [
              .library(
                  name: "Bar",
                  type: .dynamic,
                  targets: ["Bar"]),
          ],
          targets: [
              .target(
                  name: "Bar",
                  dependencies: [],
                  path: "Sources"),
          ]
      )
      
    2. 这就是你如何进行构建并调用REPL:

      $ cd Bar
      
      $ swift build
      Compile Swift Module 'Bar' (1 sources)
      Linking ./.build/x86_64-unknown-linux/debug/libBar.so
      
      $ swift -I .build/debug -L .build/debug -lBar
        1> import Bar 
        2> bar(arg: 11) 
      $R0: Int = 22
        3>  
      
    3. 这会在.build / debug目录中创建libBar.so:

      $ ls .build/debug
      Bar.build  Bar.swiftdoc  Bar.swiftmodule  libBar.so  ModuleCache
      

如果您无法重现这些结果,我建议清理所有.build目录和.so文件,并安装一个干净版本的Swift(我建议swiftenv)。

答案 1 :(得分:1)

正如文档中所述,Linux上的Swift及其软件包管理器正在进行中,因此难怪存在缺陷和缺少信息。然而,这是我通过实验和阅读帮助找到的。

如果模块FoolibFoo.so中有/LibLocationFoo.swiftmodule中有/ModuleLocation,则可以导入并使用{{} 1)在Swift程序中,将其命名为Foo,然后通过执行

进行编译
main.swift

也可以通过将其作为

启动来在REPL中完成
swiftc -I /ModuleLocation -L /LibLocation -lFoo main.swift

即。基本上给它与swift -I /ModuleLocation -L /LibLocation -lFoo 相同的论点。顺便说一句,如果模块是使用swiftc构建的,则swift build可能与ModuleLocation相同。

正如我在之前的评论中提到的那样,使用LibLocation生成的FooBar的示例,对我来说都很合适,所以我无法重现问题。< / p> 除了阅读swift.org文档和命令行帮助之外,通过运行swift build和其他带有swift build标志的命令,可以收集大量有趣且可能有用的信息。要了解-v的一些隐藏选项,请执行

swiftc