How to import library into swift code used in CLI? - swift

What I want to do
I want to try to get son file through API by swift library on CLI.
I am using the library, Alamofire, but I can't understand how to import swift library.
Maybe some tasks are needed before import (building? linking?) but I can't find how to document...
Could you teach me how to do it?
Problem
$ swift getAPI.swift
getAPI.swift:1:8: error: no such module 'Alamofire'
import Alamofire
^
Sourcecode
import Alamofire
func getArticles(){
Alamofire.request(.GET, "https://qiita.com/api/v2/items")
.responseJSON { response in
print(response.result.value )
}
}
getArticles()

This should help with running one-off scripts and when using the Swift REPL. However, for building simple tools for the command line, I would recommend using the Swift Package Manager since it takes care of the linking and dependency management for you.
To link your script code to the Alamofire library, the swift compiler has to know where to the library is located. By default it searches /Library/Frameworks, but we can supply options to tell it where else to look. Checking swift --help, we see the following options (among many others).
USAGE: swift [options] <inputs>
OPTIONS:
-F <value> Add directory to framework search path
-I <value> Add directory to the import search path
-L <value> Add directory to library link search path
The directory you supply must to contain the compiled binaries of the libraries you want to import.
To import .swiftmodule binaries, use -I
To import .framework binaries, use -F
For linking to libraries like libsqlite3.0.dylib, use -L
I think Carthage and CocoaPods will build frameworks, whereas the Swift Package Manager will build .swiftmodules. All three should put the binaries in very predictable locations. Different kinds of binaries may all be in the same and that's okay.
Putting it all together, if you build with SPM, your invocation might look like this:
$ swift -I .build/debug/
But if you manage dependencies with Carthage, it might look something like this:
$ swift -F ./Carthage/Build/iOS
For further reading, I found these resources useful:
krakendev.io/blog/scripting-in-swift
realm.io/news/swift-scripting/
Update: The Swift package manager can now take care of this for you as well! If you're writing a script as a package and include a bunch of dependencies, like Alamofire, you can now test them out in the REPL. Just cd into your package directory and launch it using swift run --repl. See the swift.org blog for more details.

Related

Distributing pybind11 extension linked to third party libraries

I'm working on a pybind11 extension written in C++ but I'm having a hard time understanding how should it be distributed.
The project links to a number of third party libraries (e.g. libpng, glew etc.).
The project builds fine with CMAKE and it generates a .so file. Now I am not sure what is the right way of installing this extension. The extension seems to work, as if I try copy the file into the python lib directories it is picked up (I can import it, and it works correctly). However, this is clearly not the way to go I think.
I also tried the setuptools route (from https://pybind11.readthedocs.io/en/stable/compiling.html) by creating a setup.py files like this:
import sys
# Available at setup time due to pyproject.toml
from pybind11 import get_cmake_dir
from pybind11.setup_helpers import Pybind11Extension, build_ext
from setuptools import setup
from glob import glob
files = sorted(glob("*.cpp"))
__version__ = "0.0.1"
ext_modules = [
Pybind11Extension("mylib",
files,
# Example: passing in the version to the compiled code
define_macros = [('VERSION_INFO', __version__)],
),
]
setup(
name="mylib",
version=__version__,
author="fab",
author_email="fab#fab",
url="https://github.com/pybind/python_example",
description="mylib",
long_description="",
ext_modules=ext_modules,
extras_require={"test": "pytest"},
cmdclass={"build_ext": build_ext},
zip_safe=False,
python_requires=">=3.7",
)
and now I can build the extension by simply calling
pip3 install
however it looks like all the links are broken because whenever I try importing the extension in Python I get linkage errors, as if setuptools does not link correctly the extension with the 3rd party libs. For instance errors in linking with libpng as in:
>>> import mylib
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: /home/fabrizio/.local/lib/python3.8/site-packages/mylib.cpython-38-x86_64-linux-gnu.so: undefined symbol: png_sig_cmp
However I have no clue how to add this link info to setuptools, and don't even know if that's possible (it should be the setuptools equivalent of CMAKE's target_link_libraries).
I am really at a loss after weeks of reading documentation, forum threads and failed attempts. If anyone is able to point me in the right way or to clear some of the fog it would be really appreciated!
Thanks!
Fab
/home/fabrizio/.local/lib/python3.8/site-packages/mylib.cpython-38-x86_64-linux-gnu.so: undefined symbol: png_sig_cmp
This line pretty much says it clearly. Your local shared object file .so can't find the libpng.so against which it is linked.
You can confirm this by running:
ldd /home/fabrizio/.local/lib/python3.8/site-packages/mylib.cpython-38-x86_64-linux-gnu.so
There is no equivalent of target_link_libraries() in setuptools. Because that wouldn't make any sense. The library is already built and you've already linked it. This is your system more or less telling you that it can't find the libraries it needs. And those most likely need to be installed.
This is also one of the reasons why Linux distributions provide their own package managers and why you should use the developer packages provided by said distributions.
So how do you fix this? Well your .so file needs to find the other .so files against which you linked to understand how this works I will refer you to this link.
My main guess is based on the fact that when you manually copy the files it works - That during the build process you probably specify the rpath to a local directory. Hence what you most likely need to do is specify to your setuptools that it needs to copy those files when installing.

Cleanly handling /usr/local/ with Swift package manager and libevent

I have 2 dependencies in my project libevent and libressl. Both of which are installed locally ( respectively under /usr/local/include and /usr/local/opt/libressl/include )
What I am looking for to achieve is for SPM to automatically understand to search in those directories.
I know I can pass flags to swift build to achieve this; but my ultimate goal is that I can properly generate xcode projects from the command line without having to constantly add custom build flags in Xcode.
I'm pretty sure it is possible, since I do not have to enter the custom settings for PostgreSQL.
Swift-tools version is at 4.0.x
Package.swift for reference:
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "CEvent",
providers: [
.brew(["libevent"]),
.apt(["libevent-dev"])
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "CEvent",
targets: ["CEvent"]),
],
dependencies: [
],
targets: [
.target(
name: "CEvent",
dependencies: []
),
]
)
Module map:
module CEvent [system] {
header "shim.h"
link "event"
export *
}
And my current build script ( build.sh ):
#!/usr/local/bin/fish
swift build -Xcc -O0 -Xcc -fblocks -Xswiftc -lbcrypt -Xswiftc -I/usr/local/include -Xswiftc -L/usr/local/lib -Xswiftc -ltls -Xswiftc -lcrypto -Xswiftc -lssl -Xswiftc -L/usr/local/opt/postgresql/lib -Xswi$
As for the reason that I want this. If I add/update/remove dependencies in swift I want to generate a new xcode project, and not have to fix its settings on respective build machines; as well as apt/ubuntu /usr/lib instead.
What you've found out, and documented in your answer, is a good start but not the full story. Yes, SwiftPM uses pkg-config to determine where certain libraries are installed. Yes, SwiftPM uses the pkgConfig name which it'll pass on to pkg-config. However the search paths are a bit more involved. On macOS it uses the following list as a base search path:
/usr/local/lib/pkgconfig
/usr/local/share/pkgconfig
/usr/lib/pkgconfig
/usr/share/pkgconfig
PKG_CONFIG_PATH environment variable
However SwiftPM doesn't use the pkg-config command, but instead parses .pc files directly. By setting the pkgConfig parameter on your package, it knows what filename to look for in the paths listed above. And, for the example in your answer, the story stops here. If there's a libevent.pc file found it parses that file, and any flags returned are passed on to the compiler and linker.
However if you were to define package providers, e.g.:
providers: [
.Brew("libsodium"),
.Apt("libsodium-dev")
]
Then SwiftPM adds additional search paths depending on the package provider for the platform it is building for. Continuing the example of macOS, SwiftPM will run brew --prefix. If this returns a path, the following path is added as a additional search path:
[brewPrefix]/opt/[packageName]/lib/pkgconfig
In my example of libsodium, SwiftPM is now able to infer the location of the library without requiring brew link or symlinks at all. In my verbose build output it lists the libsodium library path in my cellar: -L/usr/local/Cellar/libsodium/1.0.11/lib.
Alright so the thing I ignored from analysing other projects ( IBM-Swift/CLibpq in particular ) seems to be making use of the tool pkg-config which is not something I personally ever touched before.
pkg-config looks in /usr/lib/pkgconfig /usr/share/pkgconfig and the local variants for config files used in during the build process.
Inside Package.swift, after the name parameter you need to insert something for example:
let package = Package(
name: "CEvent",
pkgConfig: "libevent",
Some caveats I discovered with this:
The bcrypt library I am using does not have a full fletched install or build inside the makefile so I compiled it using the new options found in swift4 PM instead found here: BCrypt example on github and the Swift docs for more help here: SPM API Redesign
LibreSSL found in Homebrew will not install its pkgconfig on the system; so it is easiest or in my eyes best maintenance wise to either manually add it or to compile LibreSSL-portable from source.
Overall great learning experience for me today.

SwiftPM: How to setup Swift module.map referring to two connected C libraries

I'm trying to build a Swift Package Manager system package (a module.modulemap)
making available two system C libraries where one includes the other.
That is, one (say libcurl) is a base module and the other C library is including
that (like so: #include "libcurl.h"). On the regular C side this works, because
the makefiles pass in proper -I flags and all is good (and I could presumably
do the same in SPM, but I'd like to avoid extra flags to SPM).
So what I came up with is this module map:
module CBase [system] {
header "/usr/include/curl.h"
link "curl"
export *
}
module CMyLib [system] {
use CBase
header "/usr/include/mylib.h"
link "mylib"
export *
}
I got importing CBase in a Swift package working fine.
But when I try to import CMyLib, the compiler complains:
error: 'curl.h' file not found
Which is kinda understandable because the compiler doesn't know where to look
(though I assumed that use CBase would help).
Is there a way to get this to work w/o having to add -Xcc -I flags to the
build process?
Update 1: To a degree this is covered in
Swift SR-145
and
SE-0063: SwiftPM System Module Search Paths.
The recommendation is to use the Package.swift pkgConfig setting. This seems to work OK for my specific setup. However, it is a chicken and egg if there is no .pc file. I tried embedding an own .pc file in the package, but the system package directory isn't added to the PKG_CONFIG_PATH (and hence won't be considered during the compilation of a dependent module). So the question stands: how to accomplish that in an environment where there libs are installed, but w/o a .pc file (just header and lib).

Importing other libraries when using Swift as a scripting language

Heyo. I'm using Swift to make a simple web crawler for fun and practice. I made an Project.swift file and added it to a folder on my desktop. I now want to add SwiftyJSON to my project. I tried putting SwiftyJSON.swift in the same folder and adding import SwiftyJSON on top, but this did nothing. No import statement does not work at all. Is there any way to do this except pasting the whole file to the bottom of my project.swift file, or should I just stick to Python?
You have to start from the file named main.swift - that's your application entry point:
// main.swift
import Foundation
let data = "{\"message\" : \"Hello World\"}".dataUsingEncoding(NSUTF8StringEncoding)
let myJson = JSON(data:data!)
print(myJson["message"])
So you don't have to use include, but you'll need to specify all external dependencies when compiling:
swiftc SwiftyJSON.swift main.swift
./main
If you're using XCode Command Line Tool template, main.swift will be created for you by default, and you'd be able add more .swift files to your project and just use them, no need to use import.

Swift REPL: how to import, load, evaluate, or require a .swift file?

In the Swift REPL, how to import (a.k.a. load, evaluate, require) a typical text *.swift file?
I want to use the code from this file: ~/src/Foo.swift
Syntax like this doesn't work: import ~/src/Foo.swift
For comparison:
An equivalent solution in the Swift REPL for a framework is: import Foundation
An equivalent solution in the Ruby REPL for a *.ruby file is: require "~/src/foo"
These are similar questions that are /not/ what I'm asking:
How to use/make a Swift command-line script, executable, module, etc.
How to use/make an XCode playground, project, library, framework, etc.
How to launch the REPL with a pre-existing list of files.
You need to use -I, so if your modulename.swiftmodule file is in ~/mymodules than launch swift with
swift -I ~/mymodules
and then you will be able to import your module
import module name
Should be that easy
In swift,you can't import a typical *.swift file.
For Import Declaration can only be the following syntax:
“
‌ import-declaration → attributesopt import import-kindopt import-path
import-kind → typealias| struct| class| enum| protocol| var| func
‌ import-path → import-path-identifier| import-path-identifier.import-path
‌ import-path-identifier → identifier| operator”
From: Apple Inc. “The Swift Programming Language (Swift 2)”。 iBooks.
which can be described as these formats:
import module
import import kind module.symbol name
import module.submodule
import head.swift is incompatible with import import-kind module.symbol-name
Usually compile the files you want to import as a framework.Then it can be regarded as a module. use -F framework_directory/ to specify 3rd-party frameworks' search path.
Create a file. For example:
// test.swift
import headtest
print("Hello World")
open your terminal
goto the directory where you create the file.
execute command line
swift -F headtest test.swift
And done.
Simply insert the shebang line at the top of your script:
#!/usr/bin/env xcrun swift
You can copy/paste the source code into the repl and execute it.
Not ideal, obviously, but sometimes useful.
Now Swift REPL supports packages. We can do this by the following steps:
In Xcode, select menu File > New > Package. Choose a name for the package, for example MyLibrary.
Copy your codes or .swift files to the Sources/MyLibrary/ directory in your package.
Remember to make your interface public.
In the command line, go to the package directory and run REPL
Like this
cd MyLibrary/
swift run --repl
In the REPL, import your library
Like this
import MyLibrary
Now you can your codes in the REPL.
It looks like it's not possible to import file and get Xcode to use REPL for it using specifications you gave. I think you can still do it creating a proper framework, but it's not exactly what you was looking.