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