portmasterでPHP 5.5をモジュール含め一括でPHP 7.1に更新する
家鯖のPHPを5.5から7.1に更新した。
バージョン毎にportsが分かれているソフトを更新するときは、portmasterの-oオプションを使ってportmaster -o 置き換えるports(新バージョン) 置き換えられるports(旧バージョン)とする。そうしないと「php55-5.5.38 conflits with php71-7.1.1 (installs files into same place)」という具合に怒られる。
# sudo portmaster -o lang/php71 php56
PHPの場合、付随するモジュールも更新する必要があり、基本的には同じ方法で行う。しかし、1つずつportmaster -oしなきゃならなくて地味に面倒なんすよね(´・ω・`)。いい方法がないものかとネットを彷徨っていたら(この時間で手作業で更新できた説)、Stack Overflowに素敵な回答を発見。元のスクリプトだとdistfile削除プロンプトの入力待ちが正しく処理できなかったため、ちょっと改造させて貰った。
$ awk -vPATTERN="55" -vREPLACEMENT="71" \
'BEGIN { while (("pkg query -x %o \"/(mod_)?php" PATTERN "(-|$)\"" \
| getline name) > 0) { oldname = name; sub(PATTERN, REPLACEMENT, name); print "ALWAYS_SCRUB_DISTFILES=dopt portmaster --no-confirm -o " name " " oldname } }' \
| sudo sh
統廃合などで必ずしもモジュールが1対1で対応しなかったり、php-extensionパッケージは上手く扱えなかったりで、エラーなしで一発更新という訳にはいかないのが玉に瑕。でもこのスクリプトで更新作業はかなり楽になるハズ。PATTERNとREPLACEMENTはお使いの環境に応じて書き換えてね(ゝω・)v
参考サイト
portsnapにautoなるコマンドが実装されてた
portsnapのmanを見ていたら、autoなる見知らぬサブコマンドが目に止まった。普段からportsnapとお戯れになっている人ならコマンド名から挙動の予想がつくと思われるが、portsツリーの状況に応じてサブコマンドをイイ感じに発行してくれるモードのようだ。FreeBSD 11から実装されている模様。
今までは
- 初回はfetchしてextract
- 2回目以降はfetchしてupdate
- cronモードのときはupdateのみ
といった具合に人間様がコマンドを使い分ける必要があったが、これからはとりあえずportsnap autoしておけば良さそう。便利便利。
FName::GetPlainANSIString()で正しい文字列が取れない事がある
お急ぎのあなたのために、まずは結論。FNameが保持する文字列が必要な時はToString()で生成したFStringを使うべし。GetPlainANSIString()とGetPlainWIDEString()を使うとハマるから止めといた方がいい。
Unreal Engine 4の軽量文字列クラスFName(公式リファレンス)のGetPlainANSIString関数/GetPlainWIDEString関数で取得できる文字列ポインタには、そのFNameインスタンスが本来持っている文字列の一部しか入っていない事がある。恐らく仕様。以下が実証コード。
TArray<FName> Names; Names.Add(FName(TEXT("Hoge_"))); Names.Add(FName(TEXT("Hoge_0000"))); Names.Add(FName(TEXT("Hoge_0001"))); Names.Add(FName(TEXT("Hoge_10"))); Names.Add(FName(TEXT("Hoge_1200"))); Names.Add(FName(TEXT("Hoge_1300"))); Names.Add(FName(TEXT("Hoge_9999"))); Names.Add(FName(TEXT("Hoge9999"))); for (const auto& Name : Names) { UE_LOG(LogWindows, Log, TEXT("◆%s"), *Name.ToString()); UE_LOG(LogWindows, Log, TEXT(" PlainAnsiString=%s (%p)"), *FString(Name.GetPlainANSIString()), Name.GetPlainANSIString()); UE_LOG(LogWindows, Log, TEXT(" [FNameEntry]")); UE_LOG(LogWindows, Log, TEXT(" ComparisonIndex=%d"), Name.GetComparisonIndex()); UE_LOG(LogWindows, Log, TEXT(" [ComparisonEntry]")); UE_LOG(LogWindows, Log, TEXT(" Address=%p"), Name.GetComparisonNameEntry()); UE_LOG(LogWindows, Log, TEXT(" isWide=%d"), Name.GetComparisonNameEntry()->IsWide()); UE_LOG(LogWindows, Log, TEXT(" DisplayIndex=%d"), Name.GetDisplayIndex()); UE_LOG(LogWindows, Log, TEXT(" [DisplayEntry]")); UE_LOG(LogWindows, Log, TEXT(" Address=%p"), Name.GetDisplayNameEntry()); UE_LOG(LogWindows, Log, TEXT(" isWide=%d"), Name.GetDisplayNameEntry()->IsWide()); UE_LOG(LogWindows, Log, TEXT(" Number=%d"), Name.GetNumber()); }
実行結果を見ると、Hoge_10, Hoge_1200, Hoge_1300, Hoge_999で見事に同一のPlainAnsiStringが返ってきているのが分かる(★の部分)
LogWindows: ◆Hoge_ LogWindows: PlainAnsiString=Hoge_ (000000021299B988) LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029720 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B978 LogWindows: isWide=0 LogWindows: DisplayIndex=1029720 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B978 LogWindows: isWide=0 LogWindows: Number=0 LogWindows: ◆Hoge_0000 LogWindows: PlainAnsiString=Hoge_0000 (000000021299B9A0) LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029721 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B990 LogWindows: isWide=0 LogWindows: DisplayIndex=1029721 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B990 LogWindows: isWide=0 LogWindows: Number=0 LogWindows: ◆Hoge_0001 LogWindows: PlainAnsiString=Hoge_0001 (000000021299B9C0) LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029722 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B9B0 LogWindows: isWide=0 LogWindows: DisplayIndex=1029722 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B9B0 LogWindows: isWide=0 LogWindows: Number=0 LogWindows: ◆Hoge_10 LogWindows: PlainAnsiString=Hoge (000000021299B9E0)…★ LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029723 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: DisplayIndex=1029723 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: Number=11 LogWindows: ◆Hoge_1200 LogWindows: PlainAnsiString=Hoge (000000021299B9E0)…★ LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029723 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: DisplayIndex=1029723 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: Number=1201 LogWindows: ◆Hoge_1300 LogWindows: PlainAnsiString=Hoge (000000021299B9E0)…★ LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029723 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: DisplayIndex=1029723 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: Number=1301 LogWindows: ◆Hoge_9999 LogWindows: PlainAnsiString=Hoge (000000021299B9E0)…★ LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029723 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: DisplayIndex=1029723 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B9D0 LogWindows: isWide=0 LogWindows: Number=10000 LogWindows: ◆Hoge9999 LogWindows: PlainAnsiString=Hoge9999 (000000021299B9F8) LogWindows: [FNameEntry] LogWindows: ComparisonIndex=1029724 LogWindows: [ComparisonEntry] LogWindows: Address=000000021299B9E8 LogWindows: isWide=0 LogWindows: DisplayIndex=1029724 LogWindows: [DisplayEntry] LogWindows: Address=000000021299B9E8 LogWindows: isWide=0 LogWindows: Number=0
FNameはハッシュ付き文字列として実装されている。文字列はFNameの共用領域に格納され、各FNameインスタンスはその文字列格納領域へのインデックス=ハッシュを保持してる。FNameの同士の比較は互いのハッシュの比較、つまり整数の比較に還元されるため、通常の文字列比較より速いって仕掛けなんですな。
ただ、このハッシュ生成方法がちょっと曲者で、文字列がアンダースコア+ゼロ詰めされていない数値で終わっていたら、その部分を除いた文字列を共用領域に格納し、数値は各FNameインスタンスで保持するという方法なのだ。割と最近のバージョンアンプで変わったらしい(知人曰く4.12あたりで変わった気がすると)。なかなか破壊的な変更をしてくださりやがるな!言葉だと分かりにくいので図を作ってみた。
図を踏まえつつ改めて実行結果を見てみると、条件に合致するHoge_10, Hoge_1200, Hoge_1300, Hoge_999の中身が、その通りになっている事がお分かりいただけよう。Numberが実際の数値+1されているのは、これまた仕様で、数値分割条件を満たさないFNameインスタンス(Number == 0)との区別の為っぽい。
UE4では生成されたオブジェクトのインスタンスに対し、オブジェクト名+連番の名前を自動付与するため、大規模な開発になるとFName文字列ストアの肥大化が無視できなくなり、保持方法を変更したのだと思われる。
こんな格納の仕方で大丈夫なの!?と思うが、ふつーにFNameを使う分には何の問題もない。や、正確には大丈夫じゃなかったからこの記事書いてる訳だけど、文字列の生ポインタ取ってこねくり回すような事をしなければ大丈夫。FName文字列に対して低レベルな操作を行いたい時は、ToString()関数で生成したFStringに対して行う事をオススメする。エンジンのコードを見てもらうと分かるが、ComparisonIndexの文字列にアンダースコアと数値をくっつけて元の文字列を復元してやがるので(笑)
ちなみに、実行結果内のDisplayIndexというのは、名前の通り表示用の文字列へのインデックス。簡単に言えば、FName生成時に与えられた文字列のインデックスである。FNameは基本的にcase-preserving(内部的には大文字小文字を区別するが対外的には区別せずに扱う)なので、比較用と表示用で別々の文字列を持つ必要がある訳だ。知らずに使ってると、これも地味にハマりポイントかも。
FName abc(TEXT("abc")); FName ABC(TEXT("ABC")); UE_LOG(LogWindows, Log, TEXT("%s %s %s"), *abc.ToString(), (abc == ABC ? TEXT("==") : TEXT("!=")), *ABC.ToString());
念のため上記コードで確認したら、LogWindows: abc == ABCとなった。
掘ってみるとFNameには結構罠があるので注意が必要だ。
FreeBSD 11-STABLEでVirtualBox復活(`・ω・´)
3回に渡ってお送りしてきたFreeBSD 11.0-RELEASEでkern.proc.pathnameに失敗してVirtualBoxが動かない問題だが、無事解決。予想通り11-STABLEで問題なく動いた。
うちのデジタルデータ保管を一手に担っている結構重要な家鯖だし、STABLEを追いかける元気もないので、早いところ11.1-RELEASE出ないかしらー。これまでのマイナーリリース間隔から見るに、まだまだ先っぽいけど……。


