Swift on Linux - importing third-party modules

I am writing some kind of quick entry-level code for Linux as a training exercise.

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

Now I want to do two things with this:

  • Be able to import Foo in REPL.
  • Be able to import Foo into my own fast program, possibly by linking to this shared object.

I have a feeling that both tasks are interconnected, so at the moment they are on the same issue.

For 1., I do not understand how packages become REPL "finds". I tried swift -F .build/debug -framework Foo , but I get a "no such module" error. I also tried swift -I .build/debug with the same result.

In 2. I examined swiftc --help and there are options -L and -L , however I could not find the correct way to use them:

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

I use both / or Swift 2.2 or 3.0 (instead of swift build instead of swift build for <2.2>, as there is no swift build ), but it gives the same result that I think).

Please note that I understand that swift build can automatically load and create a third-party module, but I would like to know how to enable modules on disk, as they may be my own incomplete modules.


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

I created the Bar directory 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 Bar module is supposed to provide a function called bar(arg:) .

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

Then at the top level, I created the Foo directory 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 that I swift build inside Foo , it clones the Bar and builds it. However, then I get the following error; there is 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 enough, if I do the 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 was hoping this might work.

+6
source share
2 answers

Be able to import Foo into my own fast program, possibly by contacting this shared object.

Using the example that you posted in your question after "EDIT", this seems to work fine if you use swift build . Swift Package Manager will handle all the dependencies for you, even if they are 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 does not contain .so files:

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

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

Be able to import Foo into REPL.

This part is a bit messy, but I found a solution here . To apply it to your example, you have two options:

  • Use swift build with additional 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 applies to Swift 4):

    • The updated Package.manifest will 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 build and call 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 cannot reproduce these results, I would suggest clearing any directories .build and .so files and installing a clean version of Swift (I recommend swiftenv for this).

+2
source

As stated in the documentation, Swift on Linux works with its package manager, so it’s not surprising that there are errors and lack of information. However, here's what I found while experimenting and reading the help.

If the Foo module has the libFoo.so library in /LibLocation and the Foo.swiftmodule in /ModuleLocation , then you can import and use Foo in the Swift program, name it main.swift , and then compile it by executing

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

You can also do this in REPL by running it as

 swift -I /ModuleLocation -L /LibLocation -lFoo 

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

As I said in a previous comment, your example with Foo and Bar built using swift build worked just fine for me, so I could not reproduce the problem.

BTW, in addition to reading Swift.org documentation and command line help, you can gather a lot of interesting and potentially useful information by running swift build and other commands with the -v flag. To learn about some of the hidden options available with swiftc do

 swiftc -help-hidden 
0
source

All Articles