Swift on Linux - 导入第三方模块

时间:2023-01-23 17:39:14

I am writing some entry-level swift code on Linux as a learning exercise.

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

As a general task, I wish to make use of a third-party Swift module in my own code. Let's call this module "Foo". The Foo module has a Package.swift file, and after running swift build in that directory, it has created .build/debug/libFoo.so.

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

Now I wish to do two things with this:

现在我想做两件事:

  1. Be able to import Foo in the REPL.
  2. 能够在REPL中导入Foo。

  3. Be able to import Foo in my own swift program, perhaps by linking with this shared object.
  4. 能够在我自己的swift程序中导入Foo,可能是通过链接这个共享对象。

I have a feeling both tasks are related, so for now they are in the same question.

我觉得这两项任务都是相关的,所以现在它们都是同一个问题。

For 1., I don't understand how Packages become 'findable' by the REPL. I tried swift -F .build/debug -framework Foo but I get the "no such module" error. I also tried swift -I .build/debug with the same result.

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

For 2., I examined swiftc --help and there are -L and -l options however I was not able to find the right way to use these:

对于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
       ^

I'm using both/either Swift 2.2 or 3.0 (used swim rather than swift build for 2.2 as there is no swift build - but it produces the same output I believe).

我正在使用Swift 2.2或3.0(使用游泳而不是快速构建2.2,因为没有快速构建 - 但它产生相同的输出我相信)。

Note that I understand swift build can automagically download and build a third-party module however I'd like to know how to incorporate on-disk modules as they may be my own work-in-progress modules.

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


EDIT: I tried a little experiment with swift3 based on a discovery that you can use local paths as the url: parameter in the Package's dependencies: list, at least for local development.

编辑:我尝试了一个基于发现的swift3的小实验,你可以使用本地路径作为Package的依赖项列表中的url:参数,至少对于本地开发。

I created a directory Bar and Bar/Package.swift:

我创建了一个目录Bar和Bar / Package.swift:

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

I also created Bar/Sources/bar.swift containing:

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

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

The intention is that module Bar provides the function called bar(arg:).

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

I did a git init, git add ., git commit -m "Initial commit." and then git tag 1.0.0 to create a tagged local git repo for this module.

我做了一个git init,git add。,git commit -m“初始提交。”然后git tag 1.0.0为这个模块创建一个标记的本地git repo。

Then back at the top level I created directory Foo and Foo/Package.swift:

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

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

Note the relative path for ../Bar.

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

I also created Foo/Sources/main.swift:

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

import Bar
print(bar(arg: 11))

Now when I swift build inside Foo, it clones Bar and builds it. However then I get the following error; no such module:

现在,当我快速构建Foo内部时,它克隆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

Oddly, if I do the exact same build command again, I get a different error:

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

$ 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

I had hoped that this might work.

我曾希望这可行。

2 个解决方案

#1


3  

Be able to import Foo in my own swift program, perhaps by linking with this shared object.

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

Using the example you posted in your question after "EDIT," this seems to work fine provided you use swift build. The Swift Package Manager will handle all of the dependencies for you, even if they're on-disk (this works on Swift 3 and 4):

使用您在“编辑”之后在问题中发布的示例,如果您使用快速构建,这似乎工作正常。 Swift包管理器将为您处理所有依赖项,即使它们是在磁盘上(这适用于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

Note that Foo/.build/debug doesn't contain any .so files:

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

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

I believe the .swiftdoc and .swiftmodule files are used instead.

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

Be able to import Foo in the REPL.

能够在REPL中导入Foo。

This part's a bit messier, but I found the solution here. To apply it to your example, you have two options:

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

  • Use swift build with extra flags (this works on Swift 3 and 4):

    使用带有额外标志的swift构建(这适用于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> 
    

    This creates libBar.so in the current directory:

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

    $ ls
    libBar.so  Package.swift  Sources
    


  • Update your Package.manifest (this is specific to Swift 4):

    更新你的Package.manifest(这是特定于Swift 4):

    1. The updated Package.manifest would look something like this:

      更新的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. And this is how you do the build and call the REPL:

      这就是你如何进行构建并调用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>  
      

    This creates libBar.so in the .build/debug directory:

    这将在.build / debug目录中创建libBar.so:

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

If you're unable to reproduce these results, I would suggest cleaning out any .build directories and .so files, and installing a clean version of Swift (I recommend swiftenv for this).

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

#2


0  

As stated in the documentation, Swift on Linux with its Package Manager is work in progress, so no wonder there are bugs and lack of information. However, here is what I found by experimenting and reading help.

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

If the module Foo has a library, libFoo.so, in /LibLocation and Foo.swiftmodule in /ModuleLocation, then it is possible to import and use Foo in a Swift program, call it main.swift, and then compile it by doing

如果模块Foo在/ ModuleLocation中的/ LibLocation和Foo.swiftmodule中有一个库libFoo.so,则可以在Swift程序中导入和使用Foo,将其命名为main.swift,然后通过执行编译

swiftc -I /ModuleLocation -L /LibLocation -lFoo main.swift

One can also do it in REPL by launching it as

也可以通过启动它来在REPL中执行此操作

swift -I /ModuleLocation -L /LibLocation -lFoo

i.e. essentially giving it the same arguments as to swiftc. BTW, if the module was built using swift build, the ModuleLocation is likely the same as LibLocation.

即基本上给它与swiftc相同的论据。顺便说一句,如果模块是使用swift构建的,那么ModuleLocation可能与LibLocation相同。

As I mentioned in an earlier comment, your example with Foo and Bar, both built using swift build, worked for me just fine, so I could not reproduce the problem.

正如我在之前的评论中提到的,你使用swift build构建的Foo和Bar的例子对我来说很好,所以我无法重现这个问题。

BTW, in addition to reading swift.org documentation and command line help, one can glean plenty of interesting and potentially useful information by running swift build and other commands with the -v flag. To find out about some hidden options available with swiftc do

顺便说一句,除了阅读swift.org文档和命令行帮助之外,还可以通过使用-v标志运行swift build和其他命令来收集大量有趣且可能有用的信息。要了解swiftc可用的一些隐藏选项

swiftc -help-hidden

#1


3  

Be able to import Foo in my own swift program, perhaps by linking with this shared object.

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

Using the example you posted in your question after "EDIT," this seems to work fine provided you use swift build. The Swift Package Manager will handle all of the dependencies for you, even if they're on-disk (this works on Swift 3 and 4):

使用您在“编辑”之后在问题中发布的示例,如果您使用快速构建,这似乎工作正常。 Swift包管理器将为您处理所有依赖项,即使它们是在磁盘上(这适用于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

Note that Foo/.build/debug doesn't contain any .so files:

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

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

I believe the .swiftdoc and .swiftmodule files are used instead.

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

Be able to import Foo in the REPL.

能够在REPL中导入Foo。

This part's a bit messier, but I found the solution here. To apply it to your example, you have two options:

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

  • Use swift build with extra flags (this works on Swift 3 and 4):

    使用带有额外标志的swift构建(这适用于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> 
    

    This creates libBar.so in the current directory:

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

    $ ls
    libBar.so  Package.swift  Sources
    


  • Update your Package.manifest (this is specific to Swift 4):

    更新你的Package.manifest(这是特定于Swift 4):

    1. The updated Package.manifest would look something like this:

      更新的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. And this is how you do the build and call the REPL:

      这就是你如何进行构建并调用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>  
      

    This creates libBar.so in the .build/debug directory:

    这将在.build / debug目录中创建libBar.so:

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

If you're unable to reproduce these results, I would suggest cleaning out any .build directories and .so files, and installing a clean version of Swift (I recommend swiftenv for this).

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

#2


0  

As stated in the documentation, Swift on Linux with its Package Manager is work in progress, so no wonder there are bugs and lack of information. However, here is what I found by experimenting and reading help.

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

If the module Foo has a library, libFoo.so, in /LibLocation and Foo.swiftmodule in /ModuleLocation, then it is possible to import and use Foo in a Swift program, call it main.swift, and then compile it by doing

如果模块Foo在/ ModuleLocation中的/ LibLocation和Foo.swiftmodule中有一个库libFoo.so,则可以在Swift程序中导入和使用Foo,将其命名为main.swift,然后通过执行编译

swiftc -I /ModuleLocation -L /LibLocation -lFoo main.swift

One can also do it in REPL by launching it as

也可以通过启动它来在REPL中执行此操作

swift -I /ModuleLocation -L /LibLocation -lFoo

i.e. essentially giving it the same arguments as to swiftc. BTW, if the module was built using swift build, the ModuleLocation is likely the same as LibLocation.

即基本上给它与swiftc相同的论据。顺便说一句,如果模块是使用swift构建的,那么ModuleLocation可能与LibLocation相同。

As I mentioned in an earlier comment, your example with Foo and Bar, both built using swift build, worked for me just fine, so I could not reproduce the problem.

正如我在之前的评论中提到的,你使用swift build构建的Foo和Bar的例子对我来说很好,所以我无法重现这个问题。

BTW, in addition to reading swift.org documentation and command line help, one can glean plenty of interesting and potentially useful information by running swift build and other commands with the -v flag. To find out about some hidden options available with swiftc do

顺便说一句,除了阅读swift.org文档和命令行帮助之外,还可以通过使用-v标志运行swift build和其他命令来收集大量有趣且可能有用的信息。要了解swiftc可用的一些隐藏选项

swiftc -help-hidden