これに続くいくつかの問題をクリアし、書き込みができるまでを記録する。
参考:
command > /dev/null 2>&1
の意味 - Qiitaこの問題は以下のように issue が挙がっていた。
Is it supposed to work on macOS High Sierra ? · Issue #13 · qmk/qmk_toolbox
XCode のプロジェクトが公開されているのでビルドし直したら起動した。 しかし今度はライブラリが無いというエラーが発生して書き込みができない。
*** Attempting to flash, please don't remove device
>>> avrdude -p atmega32u4 -c avr109 -U flash:w:/Users/leico_studio/Downloads/qmk_firmware-master/vitamins_included_rev1_default.hex:i -P /dev/cu.usbmodem1451 -C avrdude.conf
dyld: Library not loaded: /usr/local/lib/libftdi.1.dylib
Referenced from: /Users/leico_studio/Downloads/qmk_toolbox-master/osx/DerivedData/QMK Toolbox/Build/Products/Debug/QMK Toolbox.app/Contents/Resources/avrdude
Reason: image not found
Ran into this issue as well on High Sierra. As noted above
/usr/local/lib/libusb
was not found. Did a brew install (brew install libusb
) but to no avail. The issue was that I had homebrew installed at$HOME/homebrew
rather than the standard/usr/local
. Creating a new homebrew installation at/usr/local
and then runningbrew install libusb
did the trick. Now the toolbox starts up as expected.Perhaps the toolbox is assuming that
brew --prefix
will be/usr/local
?
Homebrew がデフォルトの /usr/local
にインストールされていないとリンクエラーが起こるらしい。
なんじゃそりゃなので動的リンクなライブラリをバイナリに含める方法を調べる。
ライブラリの本体パスと依存パスの設定
macのdylibはバイナリ内部に自身のパスと依存するライブラリ達のパスが書き込まれています。 この自身のパスは下記のようにして得られます。
$ otool -D libfoo.dylib
また、依存するライブラリのパスは下記のようにして得られます。
$ otool -L libfoo.dylib
こんな機能があるらしい。では確認してみる。
o$ otool -L avrdude
avrdude:
@executable_path/../Frameworks/libusb-1.0.0.dylib (compatibility version 2.0.0, current version 2.0.0)
@executable_path/../Frameworks/libusb-0.1.4.dylib (compatibility version 9.0.0, current version 9.4.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1253.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/usr/local/lib/libftdi.1.dylib (compatibility version 22.0.0, current version 22.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
/usr/lib/libedit.3.dylib (compatibility version 2.0.0, current version 3.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/local/lib/libftdi.1.dylib というものがリンク切れしているようだ。 他のライブラリやバイナリなどにも同様にリンク切れをおこしているものがあった。
これを修正するには install_name_tool
というものを使うらしい。
また、依存するライブラリのうち、一緒に配布するものは
@rpath
直下を探すように書き換えます。 この例ではbarの中にfooへの依存があるのでこれを修正します。 コマンドの引数として元々のパスを指定しますが、これは先述の方法で取得した結果を用います。$ install_name_tool -change "/Users/omochi/temp/libfoo.dylib" "@rpath/libfoo.dylib" libbar.dylib
ここで設定した
@rpath
は変数で、アプリが起動する時に変数展開されます。
リンクの変更先だが、 libusb-* の設定が既になされていて、それらは
@executable_path/../Frameworks/libusb-1.0.0.dylib (compatibility version 2.0.0, current version 2.0.0)
@executable_path/../Frameworks/libusb-0.1.4.dylib (compatibility version 9.0.0, current version 9.4.0)
となっている。XCodeを見ると、 @executable_path/../Frameworks/ 内にコピーされるようになっている。
@exeutable_path
とは
そこで登場するのが
@executable_path
となります。 Mac OS X の dyld は、ライブラリのパスのなかに@executable_path
があると それを実行時のバイナリの場所に置き換える機能があります。 だから、Nantoka.app/Contents/Frameworks
に特定のバージョンのライブラリを置いて それを使うってことができるんですね。 そこで、半恒久的に置き換える方法として次のような手段があります。$ install_name_tool -change \ /System/Library/Frameworks/WebKit.framework/Versions/A/WebKit \ @executable_path/../Frameworks/WebKit.framework/Versions/A/WebKit \ /Applications/Line.app/Contents/MacOS/Line
install_name_tool
を使うとバイナリが覚えてるライブラリのパスを書き換えることができます。 そこで、この例の場合は/Applications/Line.app/Contents/Frameworks/
にWebKit.framework
を置けばそれを使ってくれます。 見つからない場合は/System/Library/Frameworks
にフォールバックします。 また、実際にWebKit.framework
を置き換える場合は、 関連するWebCore.framework
とJavaScriptCore.framework
のそれぞれのパスも書き換えて、 セットで置き換える必要があります。
こういうことらしい。つまりAppのバイナリからの相対パスでライブラリを選択できると。 ひとまずこれで修正を行って実行をしてみた。
しょうがないので強制終了を行った。案の定キーボードが動かなくなったので別途 AVR の ICSP ポートを用いて復帰をかけた記事がこちら。
何が原因かよくわからないが、同様のエラーは avrdude
のバイナリを使った際に起こったことがある。
なので homebrew からビルドしてコピーするのがよいのではないかと考えた。
コマンドやライブラリをビルドして集めてリンクを貼り直すスクリプトを書いた。
#!/bin/bash
# homebrew command check
if !(type brew --prefix > /dev/null 2>&1); then
echo "homebrew not found" >&2
fi
homebrewdir=`brew --prefix`
# add fomulae ripository
brew tap leico/avr
brew tap PX4/homebrew-px4
# install requires
brew update
brew install avr-gcc@8_1
brew install dfu-programmer
brew install dfu-util
brew install gcc-arm-none-eabi
brew install avrdude
brew install teensy_loader_cli
# force copy requires on direcotry
# binaries
cp -f $homebrewdir/bin/avrdude ./avrdude
cp -f $homebrewdir/bin/dfu-util ./dfu-util
cp -f $homebrewdir/bin/teensy_loader_cli ./teensy_loader_cli
cp -f $homebrewdir/bin/dfu-programmer ./dfu-programmer
# libraries
cp -f $homebrewdir/lib/libusb-0.1.4.dylib ./libusb-0.1.4.dylib
cp -f $homebrewdir/lib/libusb-1.0.0.dylib ./libusb-1.0.0.dylib
cp -f $homebrewdir/lib/libftdi.1.dylib ./libftdi.1.dylib
chmod 755 libusb-0.1.4.dylib libusb-1.0.0.dylib libftdi.1.dylib
# change link directory
# get library directory
ftdidir=`otool -D libftdi.1.dylib | sed -n 2p`
usb_1dir=`otool -D libusb-1.0.0.dylib | sed -n 2p`
usb_0dir=`otool -D libusb-0.1.4.dylib | sed -n 2p`
# avrdude
install_name_tool -change $ftdidir @executable_path/../Frameworks/libftdi.1.dylib avrdude
install_name_tool -change $usb_1dir @executable_path/../Frameworks/libusb-1.0.0.dylib avrdude
install_name_tool -change $usb_0dir @executable_path/../Frameworks/libusb-0.1.4.dylib avrdude
# dfu-util
install_name_tool -change $usb_1dir @executable_path/../Frameworks/libusb-1.0.0.dylib dfu-util
# libusb0.1.4
install_name_tool -change $usb_1dir @executable_path/../Frameworks/libusb-1.0.0.dylib libusb-0.1.4.dylib
# libftdi.1
install_name_tool -change $usb_0dir @executable_path/../Frameworks/libusb-0.1.4.dylib libftdi.1.dylib
# dfu-programmer
install_name_tool -change $usb_0dir @executable_path/../Frameworks/libusb-0.1.4.dylib dfu-programmer
ここで公開している。 qmk_toolbox/replace.sh at master · leico/qmk_toolbox
のと、10.13で一応動くプロジェクトもここにあるが、 書き込みミスが起きた場合キーボードが文鎮化するので自己責任で利用すること。 まだ実績が少ない。 leico/qmk_toolbox: A Toolbox companion for QMK Firmware
コマンドが入っていなかったらインストール
対象のコマンドが入っていなかったらインストールするときは、こう。
if !(type "コマンド" > /dev/null 2>&1); then # 対象のコマンドをインストールするような処理 fi
以上
これでどうにかなるらしいがよくわからない。
タイトルにあったコマンドの意味を考えてみる
command > /dev/null 2&>1
分けて考えましょう。
2&>1
- 標準エラー出力の結果を標準出力にマージする
> /dev/null
- 標準出力を捨てる
つまり、このコマンドの意味は、
標準エラー出力の結果を標準出力にマージして、/dev/nullに捨てる
という事だったわけです。 (普通標準エラー出力は捨てない方が良いです(cronで動かす場合))
type Command
で Command
が存在していたら標準出力からパスが返り、存在しない場合エラー出力からメッセージが出るらしい。
やってみる。
$ type cat 1>result
$ cat result
cat is hashed (/bin/cat)
コマンドがある場合は標準出力
$ type cot 2>result
$ cat result
-bash: type: cot: not found
コマンドがない場合はエラーから出力されている。 つまり標準出力から何かが出力されない場合はコマンドが存在しないことになる。
インストール先を取得するには
brew --prefix
がインストール先を返してくれます。
これを利用する。さらに、バイナリやライブラリを集める際にも homebrew のディレクトリ位置が欲しいのでこの出力を変数に入れておく。
ここまでが
# homebrew command check
if !(type brew --prefix > /dev/null 2>&1); then
echo "homebrew not found" >&2
fi
homebrewdir=`brew --prefix`
# add fomulae ripository
brew tap leico/avr
brew tap PX4/homebrew-px4
# install requires
brew update
brew install avr-gcc@8_1
brew install dfu-programmer
brew install dfu-util
brew install gcc-arm-none-eabi
brew install avrdude
brew install teensy_loader_cli
プロジェクトファイル内に存在しているものを列挙しただけのような感じ。 libusb-* はこれらコマンドのインストール途中でインストールされる。
に書いてあるとおり、gcc8.2.0 だと問題が起こるので gcc8.1.0 用の formulae を用意してこちらをビルドしている。
cp
コマンドには「コピー先に同名のファイルがあった場合に強制上書きする」 というオプション-f
があります。# cp --help : : -f, --force if an existing destination file cannot be opened, remove it and try again. :
これで持ってくるが、 HOMEBREW/bin や HOMEBREW/lib の中身は基本シンボリックリンクである。 実体を持ってくることは可能なのだろうか。
cp
コマンドでシンボリックリンクそのものをコピーしようとした場合、 名前を維持して実体化されてしまい シンボリックリンクとしてコピーできない(これは普通だけどね…)。$ ls /path/to/symlink /path/to/symlink => substance $ cp /path/to/symlink . $ ls symlink symlink # 中身は substance になる
普通に実体がコピーされてくるらしいので問題なさそうだ。
ここまで
# force copy requires on direcotry
# binaries
cp -f $homebrewdir/bin/avrdude ./avrdude
cp -f $homebrewdir/bin/dfu-util ./dfu-util
cp -f $homebrewdir/bin/teensy_loader_cli ./teensy_loader_cli
cp -f $homebrewdir/bin/dfu-programmer ./dfu-programmer
# libraries
cp -f $homebrewdir/lib/libusb-0.1.4.dylib ./libusb-0.1.4.dylib
cp -f $homebrewdir/lib/libusb-1.0.0.dylib ./libusb-1.0.0.dylib
cp -f $homebrewdir/lib/libftdi.1.dylib ./libftdi.1.dylib
ここでライブラリが元々存在していたパスが必要になる。
install_name_tool
での書き換えに元のパスが必要になるからだ。
2.2.使用ライブラリのディレクトリの変更
otool -L
で表示されるライブラリが使用しているライブラリのディレクトリは、install_name_tool -change
で変更ができます。install_name_tool -change [変更前のライブラリのフルパス名] [変更後のライブラリのフルパス名] [ライブラリ名]
変更方法
install_name_tool -change /opt/local/lib/libbz2.1.0.dylib @executable_path/../Frameworks/libbz2.1.0.dylib libboost_iostreams-mt.dylib
フルパスは以下のように取得できる。
1.1.ディレクトリの確認
ライブラリのディレクトリは、otool -D で確認できます。
otool -D [ライブラリ名]
(例)
$ otool -D libboost_iostreams-mt.dylib libboost_iostreams-mt.dylib: /opt/local/lib/libboost_iostreams-mt.dylib
出力の2行目がフルパスになるのでここだけを取得する。
10行目を表示する
sed -n 10p file
ここまで
chmod 755 libusb-0.1.4.dylib libusb-1.0.0.dylib libftdi.1.dylib
# change link directory
# get library directory
ftdidir=`otool -D libftdi.1.dylib | sed -n 2p`
usb_1dir=`otool -D libusb-1.0.0.dylib | sed -n 2p`
usb_0dir=`otool -D libusb-0.1.4.dylib | sed -n 2p`
パーミッションが違って書き換えられない時があったので chmod
を挟んでいる。
あとは install_name_tool
で参照先を変更するだけ
# avrdude
install_name_tool -change $ftdidir @executable_path/../Frameworks/libftdi.1.dylib avrdude
install_name_tool -change $usb_1dir @executable_path/../Frameworks/libusb-1.0.0.dylib avrdude
install_name_tool -change $usb_0dir @executable_path/../Frameworks/libusb-0.1.4.dylib avrdude
# dfu-util
install_name_tool -change $usb_1dir @executable_path/../Frameworks/libusb-1.0.0.dylib dfu-util
# libusb0.1.4
install_name_tool -change $usb_1dir @executable_path/../Frameworks/libusb-1.0.0.dylib libusb-0.1.4.dylib
# libftdi.1
install_name_tool -change $usb_0dir @executable_path/../Frameworks/libusb-0.1.4.dylib libftdi.1.dylib
# dfu-programmer
install_name_tool -change $usb_0dir @executable_path/../Frameworks/libusb-0.1.4.dylib dfu-programmer
libftdi.1.dylib が現状埋め込まれない状態になっている。ライセンスの確認をした上で埋め込むことにした。
実際に動作させて確認すると、
ということが判明した。ひとまずここまでで諦める。