vak: (Default)
[personal profile] ufm задал важный вопрос: умеет ли Swift собирать бинарники в статике, чтобы без зависимостей. И он таки умеет! Фича описана в отдельной статье: Getting Started with the Static Linux SDK.

Для этого надо скачать и установить дополнительный пакет.
wget https://download.swift.org/swift-6.0-release/static-sdk/swift-6.0-RELEASE/swift-6.0-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz
swift sdk install swift-6.0-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz
Пересобираем тот же пример в статике:
$ cd ~/hello-swift

$ swift build --swift-sdk aarch64-swift-linux-musl -c release

Building for production...
[5/5] Linking hello-swift
Build complete! (0.59s)

$ .build/release/hello-swift
Hello, world!

$ ldd .build/release/hello-swift
not a dynamic executable

$ size .build/release/hello-swift
text data bss dec hex filename
5092581 1125304 180652 6398537 61a249 .build/release/hello-swift
Размер бинарника 5 мегабайт. Немало, но и не ужас-ужас.
vak: (Default)
Появилась новая версия компилятора Swift. На маке с ним всё понятно, а здесь покажу как установить под Линуксом. Для определённости Ubuntu 24.04 x86_64.
cd /opt
wget https://download.swift.org/swift-6.0-release/ubuntu2404/swift-6.0-RELEASE/swift-6.0-RELEASE-ubuntu24.04.tar.gz
tar xf swift-6.0-RELEASE-ubuntu24.04.tar.gz
После этого в файле ~/.bashrc устанавливаем алиас:
alias swift='/opt/swift-6.0-RELEASE-ubuntu24.04/usr/bin/swift'
Проверяем:
$ swift --version
Swift version 6.0 (swift-6.0-RELEASE)
Target: x86_64-unknown-linux-gnu
Скомпилируем простейший пример:
$ cd ~
$ mkdir hello-swift
$ cd hello-swift
$ swift package init --type executable

Creating executable package: hello-swift
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/main.swift
$ swift build
Building for debugging...
[8/8] Linking hello-swift
Build complete! (2.35s)
$ swift run
Building for debugging...
[1/1] Write swift-version-31DF0DBC1A6B63B8.txt
Build of product 'hello-swift' complete! (0.17s)
Hello, world!
Глянем зависимости в бинарнике:
$ ldd .build/debug/hello-swift
linux-vdso.so.1 (0x00007ffdf2398000)
libswiftSwiftOnoneSupport.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libswiftSwiftOnoneSupport.so (0x00007d08b5ee6000)
libswiftCore.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libswiftCore.so (0x00007d08b5800000)
libswift_Concurrency.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libswift_Concurrency.so (0x00007d08b5769000)
libswift_StringProcessing.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libswift_StringProcessing.so (0x00007d08b568e000)
libswift_RegexParser.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libswift_RegexParser.so (0x00007d08b557b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007d08b5200000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007d08b4e00000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007d08b546f000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007d08b5442000)
/lib64/ld-linux-x86-64.so.2 (0x00007d08b5f33000)
libdispatch.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libdispatch.so (0x00007d08b51a0000)
libswiftGlibc.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libswiftGlibc.so (0x00007d08b5ecf000)
libBlocksRuntime.so => /opt/swift-6.0-RELEASE-ubuntu24.04/usr/lib/swift/linux/libBlocksRuntime.so (0x00007d08b5eca000)
Предыдущие посты про Swift:
vak: (Default)
Решил на практике проверить идею насчёт лёгкости привинчивания оконного интерфейса Swift к коду Си++. Взял модель калькулятора TI-2500 и быстренько состряпал к ней простой гуй. Благо, SwiftUI позволяет обходиться даже без XCode IDE. Достаточно простого текстового редактора и компиляции из командной строки.

Собирается и запускается это дело так:
git clone https://github.com/sergev/ti-2500-datamath.git
cd ti-2500-datamath/swiftui
swift build
swift run
Вот такая загогулина получается:

Сравниваем с оригиналом:



Исходники главного окошка: ContentView.swift. Всё довольно просто. На самом деле, сильно помогла книжка. Как в том анекдоте: "купил англо-русский словарь и выучил англо-русский язык". 😀  

Собственно стык с Си++, файл ModelProxy.swift:
import Foundation
import ModelCxx

class ModelProxy: ObservableObject {
@Published var display: String = ""

static let shared = ModelProxy()

// Allocate C++ object.
private var calc = Calculator()

// Show initial display contents.
func setup() {
calc.run()
display = String(calc.get_display())
}

// Press key.
func send(_ ch: Character) {
calc.press_key(CChar(ch.asciiValue!))
display = String(calc.get_display())
}
}
Главное типы правильно преобразовать: свифтовый Character в сишный char, а потом сишный std::string в свифтовый String.
vak: (Default)
Здесь будет пост с большим количеством скучного исходного кода. Я покажу базовый механизм, как строить оконные приложения (GUI) для мака. Кто не интересуется - смело пролистывайте.

Раньше "родные" маковские приложения положено было писать на языке Objective-C. Это такой гибрид ужа и ежа Си и Смолтока. У языка своя интересная история, как и у мака, но он был компромиссом со своими проблемами, и потихоньку сошёл на нет. Эппл придумал другой язык в замену, называется Swift. Он быстро развивается, и в последней версии достиг достаточной совместимости с Си++. Я ждал этого момента, чтобы попробовать поиграться со Swift.

Я ведь в GUI совсем не знаток. За всю жизнь не наваял ни одной приличной GUI-программы. Мои потребности в оконных интерфейсах ограничены примитивными случаями. Но механизмы знать полезно.

Обычно оконные интерфейсы принято "рисовать" графически. В смысле, строить из блоков в навороченных средах разработки типа Visual Studio или XCode. Долго и нудно возюкать мышкой и к каждому элементу настраивать сотни каких-то маловразумительных параметров. К счастью, для Swift появилась возможность всё делать прямо в исходном коде. Так называемый тулкит SwiftUI. Его я и покажу. Впрочем, он успешно уживается с визуальной средой XCode. То есть можно GUI и мышкой набрасывать при желании.

Swift хорошо уживается с Си++. Можно из Swift создавать объекты классов Си++ и обращаться к его полям и методам. И в обратную сторону: обращаться к классам и функциям Swift из кода Си++. Это крайне важная фича, и без неё Swift для меня малоинтересен. По простой причине: переносимость. Софт, которым я обычно занимаюсь, должен работать на всех платформах Linux (в том числе Андроид), мак (часто также iOS), Windows. По этой причине суть приложения (часто называют "бизнес-логика") пишется безотносительно к среде выполнения. Си++ обычный выбор. И потом эта бизнес-логика через хорошо определённые стыки привязывается к юзерскому интерфейсу конкретной платформы. Чтобы приложение смотрелось как "родное", лучше его делать на языке, родном для платформы. Для Windows это нынче C#, для Андроида Kotlin или Dart, для мака Swift. Скриптовые языки я тут обхожу стороной: от них больше проблем чем пользы.

Рассмотрим простейший пример приложения: целочисленный счётчик и пара кнопок "+" и "-", его изменяющих.


vak: (Default)
На днях компилятор Свифт обновился до версии 5.9, и теперь мы имеем совместимость между Свифтом и Си++. Покажу пример вызова Си++ из кода Свифт.

Создадим тривиальный проект на Swift типа "Hello World". Сразу скомпилируем и запустим.
$ mkdir Demo
$ cd Demo
$ swift package init --type executable
$ swift run
Building for debugging...
[3/3] Linking Demo
Build complete! (0.56s)
Hello, world!
Теперь перекурочим проект, чтобы из Swift вызывался код на Си++. Проект состоит из двух файлов:
$ tree
.
├── Package.swift
└── Sources
└── main.swift
В файле Package.swift находится описание проекта. Это такой как бы Makefile, но попроще. Проект логически состоит из отдельных компонентов, называемых targets. Эти targets могут быть на разных языках. Отредактируем файл: добавим target "HelloCxx", укажем зависимости и разрешим совместимость с Си++. Было:
import PackageDescription

let package = Package(
name: "Demo",
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "Demo"),

]
)
Стало:
import PackageDescription

let package = Package(
name: "Demo",
targets: [
.target(name: "HelloCxx"),
.executableTarget(
name: "Demo"
,
dependencies: ["HelloCxx"],
swiftSettings: [.interoperabilityMode(.Cxx)]
),

]
)
В файле main.swift находится основной код приложения. Преобразуем его в отдельный target, для этого переместим в новую папку Demo.
$ mkdir Sources/Demo
$ mv Sources/main.swift Sources/Demo
Отредактируем файл: вместо выдачи строки непосредственно, вызовем код на Си++ и получим строку из него. Было:
print("Hello, world!")
Стало:
import HelloCxx

let hello = HelloCxx()
let message = String(hello.message())
print(message)
Теперь добавим target HelloCxx на языке Си++. Создадим два файла, Sources/HelloCxx/include/HelloCxx.h:
#include <string>

class HelloCxx {
public:
std::string message() const;
};
и Sources/HelloCxx/HelloCxx.cpp:
#include "HelloCxx.h"

std::string HelloCxx::message() const
{
return "Hello from C++ to Swift!";
}
Получилась такая иерархия файлов:
$ tree
.
├── Package.swift
└── Sources
├── Demo
│   └── main.swift
└── HelloCxx
├── HelloCxx.cpp
└── include
└── HelloCxx.h
Компилируем, запускаем:
$ swift run
Building for debugging...
[4/4] Linking Demo
Build complete! (2.87s)
Hello from C++ to Swift!
Это я раздумываю, какую пользу может нанести Свифт в моих делах. Как минимум ГЮИ на нём удобно делать. А бизнес-логику приложения оставить на Си++.