俺々コミットメッセージ規約を作るエントリ Part 2

↑見てやる気を無くしたので終了します。

grepして一括で過去を書き換えたい需要を考えてみますと、submodule、makefile(いつの間にか別マシンでビルドできなくなってたetc)、readme(晒すのでメールアドレス消したいetc)……要するにメタ情報が多い気がします。
ソースコード本体を修正したいときは、具体的にどのコミットを直すべきなのか特定できているはずですし。
なのでこの辺を、Update submoduleImprove makefileEdit readmeみたいに固定してしまえば、需要は結構満たせる予感がします。Addの使い方なんてどうでも良かったですね。

というわけで本家gitのリポジトリのコミットメッセージの単語を数えてきました

#!/usr/bin/perl
# 適当に検索して取ってきたソース、誰かに感謝
while ( <> ) {
    @words = split(" ");
    $wc{@words[1]} += 1; # [0]=commit id [1]=first verb
}
foreach $key (keys %wc) {
    print "$wc{$key} $key\n";
}
git --oneline | perl ↑ | sort -n -r | head -n 100 

……ごめんなさい実際にはエディタの機能を使いました。

続きを読む

俺々コミットメッセージ規約を作るエントリ Part 1

コミットメッセージなんて、一般的にはgit自体のガイドラインGitのコミットメッセージに関する注意点を守ってればなんの問題もないと思います。先頭行は先頭大文字の現在形ではじめて、一行開けて詳細云々。

ただ最近gitに慣れてきて、rebase -iしまくるために過去のコミットをgit log --onelone | grepで一発特定できると嬉しい、という特殊事情が私の中だけで発生してまして、そのためにある程度コミットメッセージを統一しておきたくなりました。探しても深く主張しているものはあまり見当たりませんでしたので、俺ルールをどう作るかってエントリです。

全体の体裁

基本的に一行で全て済ませます。一行目に書かないと--onelineに出てこないのでgrepできません。72文字ルールは無視します。
3行目以降の詳細は余程のことがない限り書きません。主流とは逆になると思いますが、diff見たら一発でわかるものを書きなおすのは無駄です。基本的に私よりgitやコンパイラやdiff/grepのほうが賢いので、彼らの邪魔をしないほうがいいです。
ただ、本当に特記しておきたいことは書きます。普段書かないところを書いている、これは何かあったに違いない、ということで、重要なことが目立つ理論。

Initial commit(確定)

Initial commit

Initial commitのコミットメッセージはInitial commitです。
他から移行してきた場合はInitial commit, import from ...みたいにします。
なんといってもInitial commitはrebaseできません。grepして出てきたIDをろくに確認もせずにrebaseに渡して、なんてやってしまうと事故の元です。Initial commitはすぐに認識できる必要があります。

マージ(確定)

Merge branch 'マージ元' (commit 'ID7桁') into マージ先

並行ブランチ間で何度もマージしたりするので、デフォルトのメッセージだけでは特定ができなくなります。マージ元のコミットIDを付け加えます。
なおgit rebaseはマージコミットの再現に関しては非常に弱くて、自動マージは成功したけどビルドできなくなったからこのファイルも一緒に直さないと……なんてやっててもrebaseすると消失します。--preserve-mergesを付けても自動マージ分しか再現してくれません。
そのため、デフォルトで生成される本文のConflicts:の下に、Reverts:なりModifications:なりRemoves:なり勝手に書き加えて、一緒に変更したファイルをメモっておきます。

submoduleの更新(ほぼ確定)

Update サブモジュールのパス

Updateというのは便利な単語で、何にでも使えるのですが、submodule専用にしたいと今決めました。何しろsubmoduleとしてインポートしている外部ライブラリのリポジトリがpush -fされたり、最悪URL移転なんてされてしまうと、何もしなくても過去のコミットが壊れることになります。(この現象は、外部リポジトリも私作だったりすると特に頻繁に発生します) つまりsubmodule関連のコミットは潜在的にrebase -iしたくなるコミットなのです。なので、一般的なUpdateという単語をsubmodule専用にしてしまいます。grepabilityの向上のためです。

バグ修正(ほぼ確定)

Fix ...
Dirty fix ...

この辺はまあいいでしょう。

で、後が問題です。私に限らずその辺のリポジトリを覗かせていただいても、皆さん適当にAdd ...なんて書いているわけですが、機能の追加なのかモジュールの追加なのかファイルの追加なのかソースコードレベルでの定義の追加なのかごちゃ混ぜで、grepabilityは低いと言わざるを得ません。
ここはひとつ、大きめのリポジトリを覗かせてもらって、wcでもかけてみましょうか……。

(続く)

Ovenモドキ妄想

gcc-4.7ではAda2012のAda.Iterator_Interfacesが実装されました。
コンテナをfor文で回すためのものですが、要するに所謂"range"そのものですので、Ovenの様に連鎖させることもできるはずです。(まあこんなもの無くてもシグネチャを全部自分で決めてしまえば当然どうにでもなりますが、枠組みが標準に入ったのは大きいです)

用語の違い補足。C++iterator=AdaのCursor、C++のrange=AdaのIterator、次の位置を求める操作はC++ではiteratorの++ですがAdaでは(Cursorではなくて)IteratorのNext関数。forward iteratorみたいな種類分けは(Cursorではなくて)Iterator側。

generic
   with package Source_Iterators is new Ada.Iterator_Interfaces (others => <>);
package Generic_Filters is
   -- FilterがTrueを返したCursorのみ抽出
   function Iterate (
      Source : Source_Iterators.Forward_Iterator'Class;
      Filter : not null access function (Position : Source_Iterators.Cursor) return Boolean)
      return Source_Iterators.Forward_Iterator'Class;
private
   -- 実装はこれを読んでいるあなたに任せた
end Generic_Filters;

↑みたいなのを用意しておけば、↓みたいに使えるはずです。

declare
   package Int_Lists is new Ada.Containers.Doubly_Linked_Lists (Integer);
   package Int_Filters is new Generic_Filters (Int_Lists.List_Iterator_Interfaces);
   function Even (X : Int_Lists.Cursor) return Boolean is (Element (X) rem 2 = 0);
   List : Int_Lists.List;
begin
   for I in Int_Filters.Iterate (Int_Lists.Iterate (List), Even'Access) loop
      Put (Element (I)); -- 偶数だけ出てくる
   end loop;
end;

(実はGNATはgenericのformal packageのothers関係が怪しいので、このままですと通らなかったりしますがCursorとHas_Elementを個別のパラメータにしてやれば通ります。タイプ量倍増orz さっさと直らないでしょうか→ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47748)

で、まあ、こういうのってライブラリとしては面白い反面、意外と使いどころがなかったりするのですが、妄想した範囲で実用的っぽいのは、Unicodeのややこしい操作を単純化するのに使えないかなー、とかなんとか。

   -- Iterate_Code_Pointsは文字列を取ってコードポイント単位のイテレータを返す関数
   for I in Iterate_Code_Points (S, Substitude => ' ') loop
      S (I.First .. I.Last) -- slicing
      To_Wide_Wide_Character (I) -- code point
      Has_Error (I) -- TrueならUTF-*としておかしい
   end loop;
   -- Iterate_Combiningはコードポイント単位のイテレータを取って
   -- Combining Character Sequence単位のイテレータを返す関数
   for I in Iterate_Combining (Iterate_Code_Points (S)) loop
      S (I.First .. I.Last) -- slicing
      Compose (I) -- NFCに
      Decompose (I) -- NFDに
   end loop;
   -- Iterate_Translatingはコードポイント単位のイテレータを取って
   -- Wide_Wide_Character_Mappingを適用する関数
   for I in Iterate_Translating (
      Iterate_Code_Points (S),
      Ada.Strings.Wide_Wide_Maps.Wide_Wide_Constant.Upper_Case_Map)
   loop
      Value (I) -- Ada.Strings.Wide_Wide_Maps.Value (Upper_Case_Map, ...)
   end loop;

これなら、先頭から順次NFCに変換した上でCase Foldingして比較、みたいな処理も簡単に書ける気がします。

どなたか実装しません?

Boost.Contextの微妙なところ

Boost.ContextのAda移植をしてたのですが、Boost.Contextオンリーイベントに間に合わずに完全に無駄に終わってしまいました。めでたしめでたし。

一応テストをパスするところまで→ https://gist.github.com/2342019 ……ただしこのエントリには全く関係ない!

で、その過程でいくつか不審な点を見つけて、オンリーイベントでも騒いでみたのですが、結局動作に問題はないということが判明しただけでした。問題はないのでバグ報告どやぁ、なんてこともできないのですが、微妙なことに変わりはないし放置も悔しいので残しときます。

ただの愚痴です!真に受けないでください!

続きを読む

__attribute__(&lpar;constructor))の引数

http://opensource.apple.com/source/Libsystem/Libsystem-159/init.c

関係ない調べ物をしていて見つけたのですが、OSXでは__attribute__((constructor))に引数が渡ってきてるみたいです。libSystemはその値を使って初期化されています。別にlibSystemの特例でもあるまいと思って↓を走らせてみたら、案の定どこで定義しても受け取れるっぽいです。

#include <stdio.h>

struct ProgramVars
{
    void*    mh;
    int*    NXArgcPtr;
    char***    NXArgvPtr;
    char***    environPtr;
    char**    __prognamePtr;
};

static __attribute__((constructor)) 
void ctor(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars)
{
    int i;
    char const **p;
    for(i = 0, p = argv; *p != 0; ++i, ++p){
        printf("argv[%d] = %s\n", i, *p);
    }
    for(i = 0, p = envp; *p != 0; ++i, ++p){
        printf("envp[%d] = %s\n", i, *p);
    }
    for(i = 0, p = apple; *p != 0; ++i, ++p){
        printf("apple[%d] = %s\n", i, *p);
    }
    printf("argc = %d\n", *vars->NXArgcPtr);
    printf("progname = %s\n", *vars->__prognamePtr);
}

int main(){}

役に立つとは思えませんが、唐突にmainの外でargvを参照したいなんて思い立った時には使えるかも???(←_NSGetArgcという関数があります)

みんなのホームディレクトリ下の構成を教えてください

http://d.hatena.ne.jp/ku-ma-me/20120330/p1

晒してみます。こないだ再インストールしたばかりですのでまだあまり汚れてません。

$ ls -a | cat
.
..
.CFUserTextEncoding
.DS_Store
.FontForge
.Trash
.Xauthority
.Xcode
.bash_history
.bash_profile
.config
.darcs
.emacs.d
.fontconfig
.gdb_history
.gitconfig
.gitignore
.inkscape-etc
.lftp
.local
.ocaml_history
.ssh
.subversion
.viminfo
.w3m
.wine
Applications
Desktop
Documents
Downloads
Library
Movies
Music
Pictures
Public
Sites
projects

なんてことはないSnow Leopardデフォルトのまま。~直下に自分で作ったディレクトリは~/Applicationsと~/projectsのふたつだけです。

あとパッケージマネージャはHomebrewを使っています。*1

~/Applications/ # ここにアプリを放り込む。/Applicationsはなるべく汚さない。インストーラ付きのものは諦めてます……
~/Applications/bin/ # 各アプリのbin以下のファイルへシンボリックリンク。スクリプトとかも放り込んでます
~/Applications/Homebrew/ # Homebrewのインストール先
~/Documents/ # プログラムに関係ないファイルはここ
~/Pictures/ # なぜか人狼とFontForgeの作業空間に
~/Site/ # ローカルCGIとか自サイトのミラーとか
~/Site/SandTrip/ # 神プラグインだったSandTrip用CGI置き場。64bitでは使えないのでゴミと化しました……
~/projects/ # リポジトリのcheckout/cloneはこの下で
~/projects/build/ # 野良ビルド空間。ビルドしたときの手順はテキストファイルに残してgitで保存
~/projects/experimental/ # 実験場

PATHは↓のように通してます。*2

PATH=~/Applications/bin:~/Applications/Homebrew/bin:$PATH

~/binや~/localではない理由は、これらの名前はFinder*3で一番上にくるためです。~/projectsを一番上に持ってきたかったですからね。(~/Applicationsはローカライズされて「アプリケーション」と表示されてます)

で、ホームディレクトリとは別に次のディレクトリにもファイルを置いています。

/usr/local/ # gccをインストールする場所
/usr/local/bin/ # gcc以外にもTextWranglerのツールも(「Install Command Line Tools...」を実行したら選択の余地なく)入ってます
/usr/local/i686-pc-freebsd7/ # クロスコンパイル用
/usr/local/i686-w64-mingw32/ # クロスコンパイル用
/usr/local/lib/ # GMP, MPFRなんかもUniversal Binary化してここに入れました
/usr/local/quartz/ # /usr/X11/やHomebrewと干渉するライブラリ置き場
/書庫/ # ~/Downloadsから昇格したファイルを置いています。wgetするのもここ。
/書庫/Backup/ # 手動ローカルバックアップ。一応ホームディレクトリの外に作りました。

野良ビルドしたライブラリは/usr/localに置いてます。ここなら-L指定しなくていいですので、サボりです。
あらかじめDESTDIR指定で別の場所にインストールしてUniversal Binary化の作業*4をして、それをdmg*5にして取っておいて、それからsudo cpって手順を踏んでますので変なファイルは入りません……はずです。

/usr/local/quartzには、X11と衝突するライブラリを置いています。Quartzバックエンド*6を有効化したcairoやgtk+みたいな。
使うときは↓のようにします。*7

export PKG_CONFIG_LIBDIR=/usr/local/quartz/lib/pkgconfig/

「書庫」は、どうせターミナルからアクセスすることはないやーと思ってFinder上で一番下にくるように日本語名にしたのですが、アーカイブファイルを展開するなどで案外アクセスすることがあって悩んでます。

ベストプラクティスは私も教えて欲しいです。

*1:OS X特有の事情について。
OSXでは、MacPortsFink、Homebrewというパッケージマネージャが鎬を削っています。私の環境は、Homebrewの都合が色濃く出ているため、OS Xユーザーのうち更にHomebrewを選択した人のうち更にHomebrewのデフォルトの振る舞いが気に入らない人、という超狭い対象向けのディレクトリの切り方になっています。晒しといてなんですが真似するのはおすすめできません。サーセン
最初この事情を書いていなかったため混乱させたようです。そして遠藤さんの感想→https://twitter.com/#!/mametter/status/185725646431993857
Linuxディストリビューションごとにパッケージマネージャは固定でしたっけ……そっちを意識してませんでした……。

*2:Homebrewのデフォルトのインストール先は/usr/localですのでPATHを通す必要はないのです。ところがこのHomebrew、/usr/localのアクセス権を勝手に変えたりします。ので私はホームディレクトリ以下に配置しました。

*3:標準のファイルマネージャ。

*4:32ビットと64ビットを混在させるための魔術←手抜き説明。

*5:OS X標準のアーカイブ形式。

*6:QuartzOS XのネイティブGUIです。それとは別というかその上のレイヤーとしてXサーバーもあるわけです。

*7:HomebrewはなるべくX11を使うようになっています。MacPortsですとvariantで最初からQuartz向けにビルドできたりしますのでこんなことは不要です。