I am writing some entry-level swift code on Linux as a learning exercise.
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.
Now I wish to do two things with this:
import Foo in the REPL.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.
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:
$ 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).
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.
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.
I created a directory Bar and Bar/Package.swift:
import PackageDescription
let package = Package(name: "Bar")
I also created Bar/Sources/bar.swift containing:
public func bar(arg: Int) -> Int {
return arg * 2
}
The intention is that module Bar provides the function called 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.
Then back at the top level I created directory Foo and Foo/Package.swift:
import PackageDescription
let package = Package(
name: "Foo",
dependencies: [ .Package(url: "../Bar", majorVersion: 1) ]
)
Note the relative path for ../Bar.
I also created 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:
$ 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.
Be able to import Foo in my own swift program, perhaps by linking with this shared object.
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):
$ 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:
$ 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.
Be able to import Foo in the REPL.
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):
$ 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:
$ ls
libBar.so Package.swift Sources
Update your Package.manifest (this is specific to Swift 4):
The updated Package.manifest would look something like this:
// 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"),
]
)
And this is how you do the build and call the 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:
$ 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).
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.
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
swiftc -I /ModuleLocation -L /LibLocation -lFoo main.swift
One can also do it in REPL by launching it as
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.
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.
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
swiftc -help-hidden
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With