retlat's blog

cmake-js を CLion で快適に扱う

Node Native Addon を作ってみようと思ったので、まずは node-addon-examples の first project を node-gyp から cmake-js に置き換えてやってみました
普段から使い慣れているので IDE は CLion を使って
ただ NODE_API_MODULE()C++ requires a type specifier for all declarations だったり、いくつかエラーが出てしまってどうにもおかしい

C++ をろくに書いたことがなくてどこが悪いのかさっぱり思いつかないので、まずは以下のコマンドを実行してコンパイルしてみた

$ ./node_modules/.bin/cmake-js compile

これは成功してちゃんと動くものができてたので実装は問題ないはず
じゃあ何が違うんだろうということで、綺麗なところからビルドしてディレクトリを比較してみる

まずは元の綺麗な状態がこう

.
|- lib
|  `- binding.js
|- node_modules
|- package.json
|- src
|  `- hello_world.cc
|- test
|  `- test_binding.js
|- CMakeLists.txt
`- yarn.lock

cmake-js compile 実行後にはこんな感じ

.
|- build
|  |- Release
|  |  `- out.node
|  `- (省略)
|- lib
|  `- binding.js
|- node_modules
|- package.json
|- src
|  `- hello_world.cc
|- test
|  `- test_binding.js
|- CMakeLists.txt
`- yarn.lock

次に CLion で開いた時にはこうなる

.
|- cmake-build-debug
|  |- Release
|  |  `- out.node
|  `- (省略)
|- lib
|  `- binding.js
|- node_modules
|- package.json
|- src
|  `- hello_world.cc
|- test
|  `- test_binding.js
|- CMakeLists.txt
`- yarn.lock

ということで生成されるフォルダが build か cmake-build-debug かというところが違った

これを調整したらエラーが消えるのかもということで、 CLion の設定で
Build, Execution, Deployment > CMake
の順にたどって ProfileBuild directory を build に設定してみる

設定を適用してロードを待ったらエラーが消えたのでめでたしめでたし
CLion は build directory に生成されたデータを元に解析対象を探してるっぽい

Post: 2021-10-07 Update: 2021-10-19
Tags: cmake-jsCLion

Visual Studio 2019 に付属の Git を使う

Windows での開発全くしてこなかったんですが、仕事のマシンで初めて Visual Studio 2019 をインストールしました
どんなのが入ってるのかとディレクトリを彷徨ってみたら、 git.exe がいたので単体で動くのか遊んでみました
Git for Windows はインストールしてない環境です

とりあえず簡単に PATH を通して

set PATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\CommonExtensions\Microsoft\TeamFoundation\TeamExplorer\Git\cmd";%PATH%

git version してみたら正常に出力されました

git version 2.33.0.windows.1

細かいことは見てないけど、 Electron のソース取得 は無事に動いたので普通に使えそう
Build Tools for Visual Studio とやらにも入ってたら良いなと思いますが、調べるのは面倒くさい

Post: 2021-10-06 Update: 2021-10-19
Tags: Windows

Golang で UTF-8 BOM 付きの CSV を読む

お仕事で UTF-8 の CSV を Go で読む機会があったんですが、中を見たら BOM があるファイルでした
これを何も考えずに encoding/csv で読んでみたらエラーになるんですね
実際には要件のおかげで雑に対処したけど、 BOM の有無にかかわらず読むってどうしたら良いのかなと遊んでみました

ドキュメント には RFC 4180 に書いてある CSV をサポートしていると書いてあって、 このフォーマットは BOM について書いていないので知る必要がないと issue に書いてありました
そりゃそうだってことで Seek の位置を調整してから Reader に渡すようにしてみたらちゃんと読めました

愚直にしか書けないからか頭の中がそのまま記述されるみたいで Go は書いてて気持ち良い

package main

import (
	"encoding/csv"
	"log"
	"os"
)

func main() {
	f, err := os.Open("sample.csv")
	if err != nil {
		log.Fatalln("Open failed", err)
	}

	b := make([]byte, 3)
	_, err = f.Read(b)
	if err != nil {
		log.Fatalln("Read failed", err)
	}

	if !(b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xEF) {
		f.Seek(0, os.SEEK_SET)
	}

	r := csv.NewReader(f)
	rs, err := r.ReadAll()
	if err != nil {
		log.Fatalln("CSV Reader read failed", err)
	}

	log.Printf("%v\n", rs)
}
Post: 2021-08-18 Update: 2021-10-19
Tags: Go

SwiftUI の NavigationView で複数回遷移する時がある

初めての iOS アプリを SwiftUI で書いてみたら、 NavigationLink の遷移が複数回発生したのでメモ
対処はしてないので、読み進めても何も解決策は書いてません

環境は

  • Xcode 12.4
  • Simulator (iOS 14.4)

挙動の再現するコードはこんな感じ
よくある NavigationBar のボタンで画面遷移するやつ
Timer を使用しているところは、実際に作ったものだとネットワーク経由のデータ取得をしてる

import SwiftUI

class ViewModel: ObservableObject {
  @Published var message = "aaa"

  init(_ number: Int) {
    Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { _ in
      self.message = "bbb"
      print("timer end \(number)")
    }
  }
}

struct MainView: View {
  @ObservedObject var vm: ViewModel

  var body: some View {
    NavigationView {
      Text(message)
        .toolbar {
          ToolbarItem(placement: .navigationBarTrailing) {
            NavigationLink(destination: SubView()) {
              Text("Show")
            }
          }
        }
    }
  }
}

struct SubView: View {
  @ObservedObject var vm: ViewModel

  var body: some View {
    Text("Sub view")
  }
}

これで Timer.scheduledTimer の block が実行される前に SubView に遷移すると、 block が実行されるタイミングで再度遷移する
ページのスタックが MainView -> SubView -> SubView になってしまうので、 NavigationBar の戻るをタップしても SubView が出る

なぜこの動きになるのかは調べたいけど、 Jetpack Compose の State Hoisting みたいに 状態管理を基点の View かその上にまで持ち上げる設計に直す方が優先な気がする

ちなみに SubView から ObservedObject を消すと発生しない
あとはこんな感じで toolbar から外すと遷移は 1 回だけになる

  struct MainView: View {
    @ObservedObject var vm: ViewModel
  
    var body: some View {
      NavigationView {
-       Text(message)
-         .toolbar {
-           ToolbarItem(placement: .navigationBarTrailing) {
-             NavigationLink(destination: SubView()) {
-               Text("Show")
-             }
-           }
-         }
+       VStack {
+         Text(message)
+         NavigationLink(destination: SubView()) {
+           Text("Show")
+         }
+       }
      }
    }
  }
Post: 2021-04-03 Update: 2021-10-19
Tags: SwiftUI

Robolectric 4.4でJava 9必須のAPI Levelを対象とした時、Android Studio 4.0の同梱JDKで動かないのを回避する

AndroidでJSONを操作するところのUnit Testを書こうと思ったら、Robolectricが動かなくて困ったのでメモ

環境

  • Android Studio 4.0.1
  • Robolectric 4.4
  • targetSdkVersion 30

Robolectric 4.3.1からはリリースノートにある通り、API Level 29が対象の時にはJava 9が必須になっている
そしてAndroid Studio 4.0に同梱されているのはJava 8系なので動かない

解決の方法としては以下の2通りが考えられる

  1. Java 9以降のJDKをインストールする
  2. Robolectricの対象API Levelを下げる

個人的にはAndroid Studio以外にインストールするものがあるのは面倒だし、環境を作り直すときに確実に忘れるので 対象API Levelを下げる 方向で対処する

Robolectricで対象のAPI Levelを設定するには以下のような方法がある

  1. @Config annotationで指定する
  2. robolectric.propertiesで指定する
  3. RobolectricTestRunnerをextendする

ただどれも面倒だと思ってしまったので、今回は build.gradle.kts を編集する

公式サイトにあるようにRobolectricは targetSdkVersion に指定されているバージョンを対象にしている
このため、例えばbuild.gradle.ktsが以下のようになっているとAPI Level 29が使用される

android {
    defaultConfig {
        targetSdkVersion(29)
    }
}

であるならば、testの実行時だけtargetSdkVersionを書き換えれば、他のAPI Levelを使用するよう設定できる
どうするかというと

android {
    defaultConfig {
        targetSdkVersion(29)
    }
    testOptions {
        defaultConfig {
            targetSdkVersion(28)
        }
    }
}

これでUnit TestとInstrumented Testの両方に作用する
Unit Testだけに絞るなら以下のようにネストすれば良い

android {
    defaultConfig {
        targetSdkVersion(29)
    }
    testOptions {
        unitTests {
            defaultConfig {
                targetSdkVersion(28)
            }
        }
    }
}
Post: 2020-09-20 Update: 2021-10-19
Tags: Android