Linux Hello world in Swift
Intro
Swift is one of cool modern languages, that appeared recently on the scene. Purpose of Swift is replace C/ObjectC/C++ on MacOS. In same time its works on Linux. Support of Swift for Linux is not that great there is lack of many libraries and all Siwft power opens on Macos, but its still some fun language to play. Main swift page provides only Macos and Ubuntu as main supported platforms. Anything else you may have troubles to get working. Currently supported architectures are intel64 and arm64. Lets get into this journey of running Swift on Linux.
Installing Swift
Ubuntu
Installing on Ubuntu is most easiest part, download from main page and done. https://swift.org/download/
1 2 | wget -c https://swift.org/builds/swift-5.1.4-release/ubuntu1804/swift-5.1.4-RELEASE/swift-5.1.4-RELEASE-ubuntu18.04.tar.gz
tar -xvf swift-5.1.4-RELEASE-ubuntu18.04.tar.gz
|
Archlinux
Installing from main swift page, not able to compile because of missing GCC flags. So easiest way to install is from AUR https://aur.archlinux.org/packages/swift-bin/ Compiling other swift package from AUR have same issues, related to not able to compile source.
1 2 3 4 5 | wget -c https://aur.archlinux.org/cgit/aur.git/snapshot/swift-bin.tar.gz
tar -xvf swift-bin.tar.gz
cd siwft-bin
makepkg
sudo pacman -U
|
Compile Swift
All this examples given for Swift version 5.1
1 2 3 4 | mkdir HelloWorld
cd ./HelloWorld/
swift package init --type executable
swift run
|
Compiled file is located in
1 | ./.build/debug/HelloWorld
|
Static Swift Compilation
Swift static compilation compiles in all Swift runtime and uses default Linux libraries.
1 | swift build -c release -Xswiftc -static-stdlib
|
File is located in ./.build/x86_64-unknown-linux/release/HelloWorld
1 2 3 4 5 6 7 8 9 10 11 12 13 | ls -lah ./.build/x86_64-unknown-linux/release/HelloWorld
-rwxr-xr-x 1 fam fam 34M Mar 1 18:29 ./.build/x86_64-unknown-linux/release/HelloWorld
ldd ./.build/x86_64-unknown-linux/release/HelloWorld
linux-vdso.so.1 (0x00007ffebcd17000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007efc5a6d4000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007efc5a6b2000)
libatomic.so.1 => /usr/lib/libatomic.so.1 (0x00007efc5a6a8000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007efc5a4bf000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007efc5a379000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007efc5a35f000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007efc5a197000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007efc5c624000)
|
Swift on ARM64
Swift installation manual for RasPi4
https://swift-arm.com/install-swift/
1 2 | curl -s https://packagecloud.io/install/repositories/swift-arm/release/script.deb.sh | sudo bash
sudo apt-get install swift5
|
Now steps from beginning could be followed. And result will be same.
Interfacing to C
Swift is based on clang/llvm and able to parse C language and create interface to Swift. Its eliminates need to write glue/binding library for Swift and simplifies C integration.
1 2 3 | mkdir Cint0
cd Cint0
swift package init --type executable
|
Define C header with functions to be binded code.h
1 2 3 4 5 6 7 8 9 10 11 | #ifndef __CCODE_H
#define __CCODE_H
#include <stdlib.h>
#include <stdio.h>
int one();
int two();
int calc(int a, int b);
#endif
|
Function implementation
code.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include "include/code.h"
int one()
{
printf("First\n");
return 1;
}
int two()
{
printf("Second\n");
return 2;
}
int calc(int a, int b)
{
return a+b;
}
|
main.swift
1 2 3 4 5 6 7 8 9 | import ccode;
print("Start program")
print(one());
print(ccode.two());
print(calc(12,12))
print("End program")
|
C source and header files are ready. Swift code that using C code is written. So its time to define package and build everything. Package.swift defines what is included in source and dependencies. So add target "Cint0" with dependencies on "ccode" that is our C code. And then add extra target that will compile code "ccode" and doesn't depend on anything. ... and build.
Package.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Cint0",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name:"Cint0",
dependencies: ["ccode"]),
.testTarget(
name: "Cint0Tests",
dependencies: ["Cint0"]),
.target(
name:"ccode",
dependencies: []),
]
)
|
SDL2 example
Binding SDL2 library
To use C SDL2 headers from Swift we need to create bindings. This also shows how to bind C code to Swift
1 2 3 | mkdir CSDL2
cd CSDL2
swift package init --type system-module
|
Resulting files are
$ swift package init --type system-module
Creating system-module package: CSDL2
Creating Package.swift
Creating README.md
Creating .gitignore
Creating module.modulemap
Create Headers directory and add there file CSDL2-Header.h
1 | #include <SDL2/SDL.h>
|
Package.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "CSDL2",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "CSDL2",
targets: ["CSDL2"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "CSDL2",
dependencies: []),
.testTarget(
name: "CSDL2Tests",
dependencies: ["CSDL2"]),
]
)
|
Here is main thing, to tell Swift with headers to make accessible module.modulemap
module CSDL2 {
header "Headers/CSDL2-Header.h"
link "SDL2"
export *
}
Now its time to test CSDL2 package
SDL2 test with bindings
SDL2 test
1 2 3 | mkdir SDLtest
cd ./SDLtest
swift package init --type executable
|
Small SDL2 example in C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <SDL2/SDL.h>
#define SCREEN_WIDTH 200
#define SCREEN_HEIGHT 200
char quit = 0;
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
int main()
{
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow(
"WEBASM",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH, SCREEN_HEIGHT,
SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff);
int quit=0;
while(0 == quit)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
{
quit = 1;
break;
}
}
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
|
Lets convert C example to working with Swift code
Pointers can be defined as OpaquePointer without type so far.
All functions are called same as in C.
Enum types if they are passed then can be accessed as NAME.rawValue .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import CSDL2
var windowPtr: OpaquePointer! = nil
var renderPtr: OpaquePointer! = nil
SDL_Init(0)
SDL_CreateWindowAndRenderer(200,200,0,&windowPtr,&renderPtr)
SDL_SetRenderDrawColor(renderPtr,0xff,0xff,0xff,0xff)
var quit = 0;
var e = SDL_Event();
while quit == 0 {
while SDL_PollEvent(&e) != 0
{
switch e.type {
case case SDL_QUIT.rawValue::
quit = 1
default:
print("Unknown event")
}
}
}
print("Hello, world!")
SDL_DestroyRenderer(renderPtr)
SDL_DestroyWindow(windowPtr)
SDL_Quit()
|
So far all Swift features looks like they are working out of the box, without deep Swift knowledge was able to bind library, and use it. Also this is largest code base of Swift code that I ever wrote in my life. So far it looks like easy language to learn.
Sources
Links
[01] https://www.programiz.com/swift-programming
[02] https://swift.org/download/
[03] https://aur.archlinux.org/packages/swift-bin/
[04] https://swift-arm.com/install-swift/
[05] https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md
[06] https://theswiftdev.com/how-to-call-c-code-from-swift/
[07] https://github.com/KevinVitale/SwiftSDL/blob/master/Package.swift
[08] https://rderik.com/blog/making-a-c-library-available-in-swift-using-the-swift-package/
[09] https://github.com/KevinVitale/SwiftSDL/tree/master/Sources
[10] https://www.uraimo.com/2016/04/07/swift-and-c-everything-you-need-to-know/#working-with-pointers
[11] https://www.objc.io/blog/2018/01/30/opaque-vs-unsafe-pointers/