我正在 Linux 上编写一些入门级的 swift 代码作为学习练习。
作为一般任务,我希望在自己的代码中使用第三方 Swift 模块。我们称这个模块为“Foo”。 Foo 模块有一个 Package.swift 文件,在该目录中运行 swift build
后,它创建了 .build/debug/libFoo.so
。
现在我想用它做两件事:
- 能够在 REPL 中
导入 Foo
。 - 能够在我自己的 swift 程序中导入 Foo,或许可以通过链接到这个共享对象。
我觉得这两个任务是相关的,所以现在它们在同一个问题中。
对于 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
可以自动下载和构建第三方模块,但是我想知道如何合并磁盘上的模块,因为它们可能是我自己的工作-进度模块。
编辑:我尝试了一个 swift3 的小实验,基于一个发现,你可以使用本地路径作为包的 dependencies:
列表中的 url:
参数,至少促进本地发展。
我创建了一个目录 Bar
和 Bar/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 init
、git add .
、git commit -m "Initial commit."
然后是 git tag 1.0.0
为这个模块创建一个标记的 local git repo。
然后回到顶层,我创建了目录 Foo
和 Foo/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))
现在,当我在 Foo
中 swift build
时,它会克隆 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
我曾希望这会奏效。
最佳答案
Be able to import Foo in my own swift program, perhaps by linking with this shared object.
使用您在“编辑”之后在问题中发布的示例,如果您使用 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 文件。
Be able to import Foo in the REPL.
这部分有点乱,但我找到了解决方案 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 特有的):
更新后的 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"), ] )
这就是构建和调用 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>
这会在 .build/debug 目录中创建 libBar.so:
$ ls .build/debug Bar.build Bar.swiftdoc Bar.swiftmodule libBar.so ModuleCache
如果您无法重现这些结果,我建议清除所有 .build 目录和 .so 文件,并安装一个干净的 Swift 版本(为此我推荐 swiftenv)。
关于linux - Swift on Linux - 导入第三方模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39071564/