Closed79

Swift on Windows

あらさんあらさん

swift replを起動するときにはとりあえずx64 Native Tools Command Prompt for VS 2022から起動するといろいろはかどる

あらさんあらさん

Windowsでswift replを起動しようとするとpython39.dllが見つからずにlldb.exeがエラーを吐いてそのまま終了することがある。 多分普通にPathがないだけなのでC:\Users\<name>\AppData\Local\Programs\Python\Python39\追加する

あらさんあらさん

なんとなく動いたっぽい

C:\Program Files\Microsoft Visual Studio\2022\Community>swift repl
Welcome to Swift version 5.10-dev (LLVM e8e5be8d5b39f46, Swift 99e9db868aefd99).
Type :help for assistance.
1> let a = 1
a: Int = 1
2> import Foundation
3> let b = Date()
b: Foundation.Date = 1993-02-08 12:49:03 東京 (標準時)
4> struct S { let a = 1; let b = Date() }
5> try JSONEncoder().encode(S())
Foundation.JSONEncoder:43:15: note: where 'T' = 'S'
    open func encode<T>(_ value: T) throws -> Data where T : Encodable
              ^

error: repl.swift:5:19: error: instance method 'encode' requires that 'S' conform to 'Encodable'
try JSONEncoder().encode(S())
                  ^

Foundation.JSONEncoder:43:15: note: where 'T' = 'S'
    open func encode<T>(_ value: T) throws -> Data where T : Encodable
              ^


5> let s = S()
s: S = {
  a = 1
  b = 1993-02-08 12:50:10 東京 (標準時)
}
6> try JSONEncoder().encode(s)
Foundation.JSONEncoder:43:15: note: where 'T' = 'S'
    open func encode<T>(_ value: T) throws -> Data where T : Encodable
              ^

error: repl.swift:6:19: error: instance method 'encode' requires that 'S' conform to 'Encodable'
try JSONEncoder().encode(s)
                  ^

Foundation.JSONEncoder:43:15: note: where 'T' = 'S'
    open func encode<T>(_ value: T) throws -> Data where T : Encodable
              ^


6> struct SS: Codable { let a = 1; let b = Date() }
7> try JSONEncoder().encode(SS())
$R0: Foundation.Data = 29 bytes
8> try String(data: JSONEncoder().encode(SS()), encoding: .utf8)!
$R1: String = "{\"b\":729175925.3969994,\"a\":1}"
9>
あらさんあらさん

swift.org の方の導入を見ると以下のmodule.modulemapをコピペする作業はもうしなくていいらしい。でもしないと動かないとかもあったり。あとsymlinkより普通にコピペした方が自分の手元だと変なトラブル少なかった

mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" S:\swift\stdlib\public\Platform\ucrt.modulemap
mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" S:\swift\stdlib\public\Platform\winsdk.modulemap
mklink "%VCToolsInstallDir%\include\module.modulemap" S:\swift\stdlib\public\Platform\vcruntime.modulemap
mklink "%VCToolsInstallDir%\include\vcruntime.apinotes" S:\swift\stdlib\public\Platform\vcruntime.apinotes
あらさんあらさん

VSCodeでSwift Packagesのプロジェクト開くとコード補完とかも効く、いいね

あらさんあらさん

VSCodeの内部ターミナルからx64 Native Tools Command Prompt for VS 2022を開くにはsetting.jsonに以下を追加してよしなに

/k 以降の文字列はx64のショートカットに書かれてた内容をそのままもってきた、やってることはショートカットと同じcmd.exeに特別なbatを食わせて環境変数とかデフォルトで追加することらしいよ

  "terminal.integrated.defaultProfile.windows": "Developer Command Prompt x64",
  "terminal.integrated.profiles.windows": {
    "Developer Command Prompt x64": {
      "args": [
        "/k",
        "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
      ],
      "overrideName": true,
      "path": [
        "${env:windir}\\Sysnative\\cmd.exe",
        "${env:windir}\\System32\\cmd.exe"
      ]
    },
  },
あらさんあらさん

個人的にコマンドプロンプトでファイルシステムの移動すらおぼつかないのでexplorer.exe開いて、作業したいところでVSCodeを開く。そんな感じでやるとswiftコマンドだけ内部ターミナルでたたいてpackage initしたりできる

あらさんあらさん

適当にConcurrencyなネットワーク処理をするexecutableを作ってみる。URLSessionを使うときにはもちろんFoundationだけでは足りないのでFoundationNetworkingも追加。普通に動いてるね

import Foundation
import FoundationNetworking

struct Todo: Decodable {
    let userId: Int
    let id: Int
    let title: String
    let completed: Bool
}

extension URLSession {
    func data(from url: URL) async throws -> (Data, URLResponse) {
        try await withCheckedThrowingContinuation { continuation in
            dataTask(with: url) { data, response, error in
                if let error = error {
                    continuation.resume(throwing: error)
                } else if let data = data, let response = response {
                    continuation.resume(returning: (data, response))
                } else {
                    fatalError("Neither data nor error was returned.")
                }
            }.resume()
        }
    }
}

@main
struct Main {
    static func main() async throws {
        let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
        print(url)
        try await Task.sleep(for: .seconds(1))
        let (data, response) = try await URLSession.shared.data(from: url)
        dump(response)
        let todo = try JSONDecoder().decode(Todo.self, from: data)
        print(todo)
    }
}
あらさんあらさん

Package.swiftも特に変わらず

// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "swift-my-app",
    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: "swift-my-app"),
    ]
)
あらさんあらさん

なにか特別な設定をしなくてもVSCodeの拡張を指示通りに入れていればコード補完も効いてて書きやすい

あらさんあらさん

Arc作ってるBrowserCompanyが毎日最新のSwiftのスナップショットをWindowsで使える形で配布してるのでSwift 5.11-devとか使いたかったらここから、compnerd/swift-build もあるけど今compnerdさんBrowserCompanyで開発に加わっているらしいからcompnerd/swift-buildの純粋な後継リポジトリがthebrowsercompany/swift-buildという扱いみたい
https://github.com/thebrowsercompany/swift-build

あらさんあらさん

"C:\src\windows-samples\WinUI3AnimationsPreview\WinUI3AnimationsPreview.code-workspace"をVSCodeで開いたらプロジェクトのセットアップは完了。CMake使ってるからConfigure, Buildでできるか試してみる。

あらさんあらさん

いざ!と思ってやってみたらしょっぱなからコケる。多分なんかVSCodeの設定うまくいってないんだろね

エラー: swift タスクの検出は、次の構成のタスクに貢献しませんでした。
{
    "type": "swift",
    "args": [
        "package",
        "resolve"
    ],
    "cwd": ".",
    "problemMatcher": [],
    "label": "swift: Resolve Package Dependencies",
    "detail": "swift package resolve"
}
このタスクは無視されます。
エラー: swift タスクの検出は、次の構成のタスクに貢献しませんでした。
{
    "type": "swift",
    "args": [
        "package",
        "resolve"
    ],
    "cwd": ".",
    "problemMatcher": [],
    "label": "swift: Resolve Package Dependencies",
    "detail": "swift package resolve"
}
このタスクは無視されます。
あらさんあらさん

でもまぁこれはCMakeのタスクをVSCodeにやらせようとしてるだけなのでいいでしょ、CLIから眺めていきます。cmake --preset debugでCMakeLists.txtからConfigureするとここまではおけーい

C:\src\windows-samples\WinUI3AnimationsPreview>cmake --preset debug
Preset CMake variables:

  CMAKE_BUILD_TYPE="Debug"
  CMAKE_CXX_COMPILER="cl"
  CMAKE_C_COMPILER="cl"
  CMAKE_INSTALL_PREFIX:PATH="C:/src/windows-samples/WinUI3AnimationsPreview/out"
  CMAKE_MODULE_PATH="C:/src/windows-samples/WinUI3AnimationsPreview/cmake"

利用できるインスタンスがありません。
-- Restoring NuGet package Microsoft.Windows.SDK.BuildTools.10.0.22000.194.
Adding package 'Microsoft.Windows.SDK.BuildTools.10.0.22000.194' to folder 'C:\src\windows-samples\build\NugetPackages'
Added package 'Microsoft.Windows.SDK.BuildTools.10.0.22000.194' to folder 'C:\src\windows-samples\build\NugetPackages'

使用されている NuGet Config ファイル:
    C:\src\windows-samples\nuget\NuGet.config

使用されているフィード:
    C:\Users\arasa\.nuget\packages\
    https://api.nuget.org/v3/index.json

インストール済み:
    packages.config projects に対する 1 個のパッケージ

-- Running swiftwinrt...
-- swifwinrt completed
-- Configuring done (5.4s)
-- Generating done (0.1s)
-- Build files have been written to: C:/src/windows-samples/WinUI3AnimationsPreview/build
あらさんあらさん

ドキドキビルドします。cmake --build --preset debugでビルドすると普通にコケる。
error: could not build C module 'CWinRT'とかなんとか

あらさんあらさん

これは新しいSwift(今回はswift 5.10)を利用していてNuGetで少し古いコード自動生成が利用されてるからっぽい。失敗原因を見ていくとoleauto.hが云々とか出てくる。これは自動生成されたコードの中で発生したエラーなのでswift-winrtのコミットから関係ありそうなやつを探す
C:\src\windows-samples\Shared\WinRT\Sources\CWinRT\include/CppInteropWorkaround.h:58:5: error: missing '#include <oleauto.h>'; 'SysFreeString' must be declared before it is used
https://github.com/thebrowsercompany/swift-winrt

あらさんあらさん

ほんだらswift-winrtを自前でmainからビルドしてswiftwinrt.exeをつくってくべよ

あらさんあらさん

ここで見ているバージョンは開発ターミナルでsetして出てくるバージョンにすればいいと思うよ、手元だとこれだった
WindowsSDKVersion=10.0.22621.0\

あらさんあらさん

これで動くはずなのでえいやえいやビルド
VSCodeだとCommand Pallete開いてcmakeのconfigureでpresetを指定するとうまく動くようになる
> cmake select configure Preset

あらさんあらさん

Run build taskで動くという話だけどswiftタスクがうまく動かないのでRun task...からConfigureとBuildを選んでビルドを回す

あらさんあらさん

Buildまで終わったらbin\WinUI3AnimationsPreview.exeにバイナリが生成されているはず

あらさんあらさん

ワークスペース設定を見るとlaunchにlldbを起動するようになってるので、VSCodeのRun and Debugからそのままデバッグ

WinUI3AnimationsPreview.code-workspace
  "launch": {
    "version": "0.2.0",
    "configurations": [
      {
        "type": "lldb",
        "request": "launch",
        "name": "WinUI3AnimationsPreview",
        "program": "${workspaceFolder:WinUI3AnimationsPreview}/../build/bin/WinUI3AnimationsPreview",
        "args": [],
        "cwd": "${workspaceFolder:WinUI3AnimationsPreview}",
        "preLaunchTask": "Build"
      }
    ],
    "compounds": []
  },

あらさんあらさん

import WinUIをしてもエディタ上だとうまくコード補完が効かないので何かしら設定が必要

あらさんあらさん

と思ったけどコード補完効き始めたな、なんだろ、sourcekit-lsp周りのあれこれなんかな

あらさんあらさん

このアプリケーションのビルド結果自体はボタンがあるだけで特に面白みはないかもしれない、これを構成するビルドシステムがおもしろい

あらさんあらさん

VS2022のインストーラーで入れてるものたち、C++によるデスクトップ開発をベースに以下の設定。いらないもの多分いっぱいある

あらさんあらさん

ここからさらにWinUI3をSwiftで書いたときにどんな見た目を作れるのかはこの人の動画を見るといい感じ。動画だとコード補完とか聞いてなさそうだしデバッグのやり方もわからないみたいなことを言ってるのでそこはこのスクラップで補ってる
https://www.youtube.com/watch?v=hbo98xNLzog

あらさんあらさん

Windows Sandboxを利用してクリーンな環境から始めると再現性高い
https://learn.microsoft.com/ja-jp/windows/package-manager/winget/#install-winget-on-windows-sandbox

setup.ps1
$progressPreference = 'silentlyContinue'
Write-Information "Downloading WinGet and its dependencies..."
Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle
Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile Microsoft.VCLibs.x64.14.00.Desktop.appx
Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.7.3/Microsoft.UI.Xaml.2.7.x64.appx -OutFile Microsoft.UI.Xaml.2.7.x64.appx
Add-AppxPackage Microsoft.VCLibs.x64.14.00.Desktop.appx
Add-AppxPackage Microsoft.UI.Xaml.2.7.x64.appx
Add-AppxPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle
あらさんあらさん

以下の情報はもう古い、Swift ToolchainとVisualStudioのビルドツール入れてそのままwindows-samplesのREADME.mdに進めばOK、swift-winrtとかそこらへんはbrowsercompanyがSPMで簡単に使えるようにまとめてた


いったんまとめ、これだけで一応最小の依存関係だとSwiftを利用するツールが入る。

$ winget install --id Microsoft.VisualStudio.2022.Community --exact --force --custom "--add Microsoft.VisualStudio.Component.Windows11SDK.22621 --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
$ winget install --id Swift.Toolchain -e
$ winget install --id Kitware.CMake -e
$ winget install --id Ninja-build.Ninja -e

for one-liner

winget install --id Microsoft.VisualStudio.2022.Community --exact --force --custom "--add Microsoft.VisualStudio.Component.Windows11SDK.22621 --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64";winget install --id Swift.Toolchain -e;winget install --id Kitware.CMake -e;winget install --id Ninja-build.Ninja -e

nightlyを利用する場合にはwingetで入れたSwiftを消してこっちから入れる。wingetだとGitとVCRedistとPython39の依存が一緒に入るのでいったん実行することがおすすめ
https://github.com/thebrowsercompany/swift-build/releases

swiftのwindows-examplesはいろいろなバージョンに気を付ける。
windows-examplesが依存しているswift-winrtは0.2.0だと古いのでmain branchを利用する。
とりあえず上記の内容をそのまま入れてnightlyを使えば動くものがこのフォークしたリポジトリにある

https://github.com/arasan01/windows-samples

Windows App Runtimeは手動で入れる、wingetは古いかもしれない
https://learn.microsoft.com/ja-jp/windows/apps/windows-app-sdk/downloads

Sandbox環境でやるときはterminalがadministratorになるのでspm.cmakeを適切に編集して動くようにする

あらさんあらさん

まぁその前にwindows-exampleをベースにもうちょっと変数などをわかりやすく切り出して躓かずにビルド&ランできるようにする

browsercompanyが全部SDK周りまとめてやる必要なくなった

あらさんあらさん

GRDB.swift使いたいからビルドしてみる、SQLite3のsystemLibraryに依存してるのでこれを解決したい

あらさんあらさん

とりあえずlibsqlite3-dev相当のものをWindowsで用意しないといけないのでvcpkgからsqlite3を引っ張ってみる

https://github.com/Microsoft/vcpkg?tab=readme-ov-file#quick-start-windows

vcpkgのGetting Startedを進めたらvcpkg.exeが叩けるようにcloneしてきたところまでPATHを通す。

$ vcpkg integrate install
$ vcpkg install sqlite3
$ vcpkg install pkgconf
$ winget install -e --id bloodrock.pkg-config-lite

環境変数の追加

PKG_CONFIG_PATH = C:\Users\<path_to>\vcpkg\installed\x64-windows\lib\pkgconfig

いちおうsqlite3がpkg-configで探せるようになるっぽい。

そしたらspmでpkgConfigの設定を追加

          .systemLibrary(
              name: "CSQLite",
+             pkgConfig: "sqlite3",
              providers: [.apt(["libsqlite3-dev"])]),
あらさんあらさん

実行ファイルにsqlite3.dllが含まれていないので普通にビルドしただけだとこけちゃうな…
pkgConfigの作り方とかミスってるのか?

あらさんあらさん

spmのpkg-configだけだと出力先にsqlite3.dllを自動でコピーむりっぽ?とりあえず適当なスクリプトでmyapp.exeの隣にsqlite3.dllをコピーするような感じにしよう

あらさんあらさん

GRDB.swiftのテスト結果

Test Suite 'All tests' failed at 2024-02-25 22:40:09.815
         Executed 2474 tests, with 7 tests skipped and 139 failures (129 unexpected) in 126.571 (126.571) seconds
あらさんあらさん

Windowsでほとんど調整してない割には動いているものが多くて悪くない感じ、sqlite3.dllを直接openしている関係上sqlite3.dllに足りてない機能がある可能性もあるからvcpkg側の調整が必要だったりするかも、embedされてるSQLiteCustomを利用してないから意図しているテストケースにはまだなってない

あらさんあらさん

次はwindows-samplesにTCAとGRDB.swiftを突っ込んで適当なアプリを作成する

あらさんあらさん

最高すぎる、普通にobserveでFeatureの状態を観測してWinUIに反映できてる

あらさんあらさん

WindowsでVSCodeを開いている状態ですでにビルド済みのマクロがある場合に、依存をアップデートなどしてマクロがリビルドされるときはVSCodeがマクロの.exeを所有しているので削除ができずにビルド失敗する

あらさんあらさん

systemLibraryの仕様勘違いしてた、pkg-configでリンクされたらあとは普通に環境変数のPATHにC:<path_to>\vcpkg\installed\x64-windows\binをセットしたら動くわ

あらさんあらさん

sqlite3.dllをコピー、もしくはPATHが認識できるところでいいのね

あらさんあらさん

配布するときには生成したsqlite3.dllをコピーしてexeの隣においてあげないと環境によって動かないし、そもそもfts5やsnapshotがない実行ファイルを利用するとクラッシュする

あらさんあらさん

そろそろこのスクラップもいったんまとめの時期かもしれんな、第二弾のスクラップを作らないと見通しが悪い

あらさんあらさん

parallelsとかで動かしたいなら最悪x86_64互換モードみたいなやつある気がするからそれで開発できる気がする

このスクラップは1ヶ月前にクローズされました