Vim x C言語で書くプロジェクトの良さと(私的)つらみについて

この記事は Vim Advent Calendar 2016 の16日目の記事です。
15日目の前日は shinespark氏の「Vimでtreeっぽくディレクトリ構成を書きたい、そんなアナタの為のVim Plugin、できてます。」でした。


この前の新Mac騒動でCtrl+]を初めて知った@MNukazawaです。
しかし相変わらずESCを連打しています。


追記 > 投稿してから文字サイズがおかしいことに気づいたのですが、ざっくり対処しておきますので、多少おかしくても勘弁いただければと思います。

まえがき


家でも仕事でも、C言語を書くときは(C言語を書くときに限らず)Vimを使っています。
ライトVimmerにとって、Vimは最高ですが、完璧ではないです。
C言語での開発とVimとの相性は最高ですが、光のあるところには影もあり、以下に書いてあるアレコレのつらみに、私は今も苦しんでいます。

今回挙げた中には、受け入れていかなければならないものや、いかにも井の中の蛙っぽい悩みも含まれており、解決策やベストプラクティスが無いはずがないと思えるものもあります。
「プラグイン忌避を克服しろ」「Vimの基本コマンドくらい覚えろ」等々、厳しくツッコミを入れて頂けると、正直とても嬉しいです。


私は現在、Vecterion(べくてりおん)という名前のベクターグラフィック・エディタを書いています。
今回のVim記事も、VecterionをVimで書いているときの話をするので、Vecterionのスペックをざっくり挙げておきます。
* GUIアプリケーション(ベクターグラフィック・エディタ)
* C言語/GTK3で書いている。
* googletest使用
* GNUMake(生Makefile)でビルド
* 中規模アプリケーション(1人で開発。アプリケーション本体のヘッダとソースが50個ずつ有る)

リリースはまだしていませんが、昨日もリリースブロッカを一つ潰したので、来年のどこかでは...。
Vecterionについては、Vimに関わらない C言語 Advent Calendar 2016 の12日目の記事「Cで書く中規模GUIアプリケーションから得た知見(初稿) 」でざっくり書いたので、よければどうぞ。





つらみ達

# ディレクトリ構成

中規模アプリケーションは、ヘッダとソースが別パスになる。
=== Vecterionのヘッダ一覧 ==
:~/etaion_vge$ ls include/
et_canvas.h             et_error.h             et_snap.h          pv_bezier.h           pv_io.h
et_canvas_collection.h  et_etaion.h            et_snap_panel.h    pv_cairo.h            pv_render_context.h
et_color_panel.h        et_key_action.h        et_state.h         pv_color.h            pv_render_option.h
et_define.h             et_layer_view.h        et_stroke_panel.h  pv_element.h          pv_renderer.h
et_doc.h                et_mouse_action.h      et_thumbnail.h     pv_element_general.h  pv_rotate.h
et_doc_history.h        et_mouse_cursor.h      et_tool_id.h       pv_element_info.h     pv_stroke.h
et_doc_history_hive.h   et_mouse_util.h        et_tool_info.h     pv_error.h            pv_svg_info.h
et_doc_id.h             et_pointing_manager.h  et_tool_panel.h    pv_file_format.h      pv_type.h
et_doc_manager.h        et_position_panel.h    pv_anchor_point.h  pv_focus.h            pv_vg.h
et_doc_relation.h       et_renderer.h          pv_appearance.h    pv_general.h

===


ソースは source/ 以下にある。

* ヘッダとソースを別パスにすると、Vimでソースヘッダ間の移動が面倒になる

imでソースヘッダ間の移動で有名な 
`:sp %<.h`
 が効かなくなるため、移動が面倒。
仕方なしにヘッダからソースへの移動は、目に付いた適当な関数を叩いて飛んでいる。
というか、ソースへのアクセスもとりあえずヘッダを開いてctagsでソースへ飛び、いつでもctagsのスタックでヘッダへ飛べるようにしておく、という運用をしている。

 * Vimに必須なctagsが絡むと、ディレクトリが深くなりタイプ量が増える

 ctagsは、tagsファイル生成と利用のために、vimのカレントディレクトリを、常にプロジェクトのソース先頭にしておかないと都合が悪い。
そうでなくても、ソースとヘッダの間を行き来することを考えると、Tab補完はあるものの、ディレクトリの分だけタイプ数が増えることを受け入れなければならない。

# 今どきな長めの名前で、インテリセンス無しで書く

 今どきの関数・変数名は、省略を避けるなど、なんとなく長い名前になりがち。
 長い名前をインテリセンス無しで書くのはつらい。
 (名前が長いこと自体は、自分が悪い気がしないでもないが。)
 Vimは最高のテキストエディタだが、最高のIDEであるとは限らない。

 ex. `>-------pv_anchor_point_set_handle(&dst_aps[2], PvAnchorPointIndex_HandlePrev, p1_handle_prev_dst);`

 インテリセンスのプラグインに決定打がない。ついでにVimプラグインはインストールがつらい。


# リファクタリング的な置換ができる命名

「リファクタリング」というほど格好良くなくても、開発していて名前を変えたくなることは多い。

 * Vimのリネームは1ファイル内のみ

プロジェクト内でファイルを跨いだ命名変更がしたいとき、Vimは無力。
 ヘッダ・ソース合わせて100ファイルを超えているので手作業は嫌。
 なので、
`sed -i 's/AA/BB/g' */*.[hc]`
を使っている。

 * sedでリネームしやすい名前を付けている(付けなければならない)

 つまり`sed`しやすい名前を付けなければならない。
 EtElement構造体のオブジェクトの変数は"element", "layer_element"などと名付ける。
リネームしずらいので、"elem"とか"e"とは付けない。
 (というか初期に一部のコードでそういった変数名を付けてしまったので、後で直さないと...)
 関数の引数を変更したいとき、引数に折り返しがあるとsedできなくてつらい。(これはIDEでも難しいだろうけれど)

ファイルの"path"(filepath)と被るのが怖くて、アンカーポイントで構成される線分"path"(ahchorpath)と名づけるのを避けて、変な名前を付けたりしていた。

# ctagsでstatic関数定義へ飛ぶとき

中規模以上のC言語プロジェクトでは、ファイルローカルなstatic関数を定義する。
そしてコーディングルールによったり、その他の都合で、ソースの先頭近くでファイルローカルstatic関数の定義をすることになる。
そしてVimを使うならば、ctagsで関数間をジャンプする。

 * ctagsは最初の候補としてstatic関数の宣言へ飛ぶが、私は定義を見たい

いつもCtrl+]とCtrl+Tを使っている。Vimのタグジャンプ自体はとても便利で多用するので、いつも常に:tsと打って候補一覧を見てから数字キーで飛び先を指定する、なんてことはしたくない。

 * そもそも開発中に関数を追加しながら毎度`:!ctags -R`するのがつらい。

コミット前のコードは、書きかけの関数が消えたり、名前が変わったりが頻繁に起こる。
`ctags -R`を何度か打つことになる。が、ディレクトリに複数のソースファイルを抱えたプロジェクトで、再帰的にソースを探索させるとctagsも2~3秒かかることもある。(いま以上に大きくなったらどうなるか不安)

# インデント整形(gg=G)にコーディング規約を合わせる

 * Vimはデフォルトでインデント整形できる機能が用意されているのがすばらしい

 しかし完璧ではないので、以下のような考慮すべき点がある。

## switch case{}とLinuxコーディング規約の衝突

Linuxコーディング規約よりインデントが1段深い。
8tabを使っていると8文字分の横幅を使われる。
他の開発者に触ってもらうなどを考えると、Vimのデフォルトから.vimrcで変えたくなかった。
一人で開発しているだけなら、そのうち慣れたので大きな問題でもない。

## extern C{} (c++対応)

* extern C{}がVimの自動インデントで引っかかる。ヘッダ全体にインデントが一段かかってしまう。

 googletestでテストを書こうとするとこれに悩む。
 googletest側でextern C{}することで解決。
 CヘッダがC++に考慮するのが間違いと思うことにする。
 せいぜいC++テストコード内のinclude行だけの問題になる。




以上です。



あとがき

今更ですが、「つらみ」ってこういう使い方で良かったのでしょうかね?

もうそろそろ「ライトVimmerの世界 - 驚きのプラグイン無し生活 -」みたいな記事を書いたほうが良い気がしています。
それよりVimコマンドを覚えたほうが有意義そうですが。

水の中の魚は水のことを意識することがありません。Vimを使ってC言語でコードを書いていると、ctagsの有り難みをつい忘れてしまいがちです(Windows上のAtomでソースを開いたときに思い出す)。
それは必ずしも悪いことではないのだと思います。が、快適なVimコーディングも、使い続けていると細かいところでつらみが意識されるようになるのも、自然なことで、だからこそ先達たちは多用なVimプラグインを生み出してきたのかもしれません。


この記事は Vim Advent Calendar 2016 の16日目の記事でした。
17日目の明日はanekos氏の「Vim の下着の話」です。

Cで書く中規模GUIアプリケーションから得た知見(初稿)

この記事は C言語 Advent Calendar 2016 の12日目の記事です。
11日目の前日はegtra氏の「配列でないオブジェクトに対するポインタ演算」でした。


@MNukazawaといいます。今、ベクターグラフィック・エディタをC言語で書いています。
Vecterion(べくてりおん)という名前です。
VecterionはGtk3/C言語で書いています。
現在進行形ですが、今日はVecterion開発の知見というか、Vecterionで使っているちょっと大きめのイディオムを、いくつか紹介したいと思います。

なおこの記事で言っている「中規模」ですが、
* ワンソース(ほぼmain.cだけ)に収まるアプリケーションを小規模
* 多人数で開発しなきゃ作れないアプリケーションを大規模
として、コマンドラインスクリプトよりは高度なことをしているけれど、コード量としては一人で収まる。
小規模と大規模の間くらい、程度の意味です。

小規模は、(語弊があるが)catやechoのような、ワンパスのコマンドラインスクリプト程度の機能。
大規模は、GIMP, LibreOfficeのようなGUIエディタや、Linuxカーネルなど。



なんというか、中規模コードの良い例を見つけることができなかったので書きました。
どなたかオススメのプロジェクトをご存知の方は教えてください。


開発中プロジェクトでまだ悩み中(本当は良くない)なので、まとまりきらない内容になっていますが、あなたが中規模プロジェクトを書く際に、一部でも参考になれば幸いです。

# ソース構成

## ソース構成

struct型の定義と一緒に、メンバ関数・構造体を操作するUtility関数を同じヘッダに置いている。
CヘッダとCソースは原則的に一対一対応させている。

これらのソース構成は、ビルド速度的には最善ではないが、関数の置き場所がわかりやすく、プロジェクト全体の読みやすさに繋がる。

"#define"でない方法で定数 を定義している。
===
 static const PvPoint PvPoint_Default = {0, 0};¬
===
static const は入れ子に宣言定義(?)できないので、その時は諦めて#defineを使う。


Gtk的な関数名の付け方を、そのままソースの名前にしている。
===
:~/etaion_vge$ ls include/
et_canvas.h             et_error.h             et_snap.h          pv_bezier.h           pv_io.h
et_canvas_collection.h  et_etaion.h            et_snap_panel.h    pv_cairo.h            pv_render_context.h
et_color_panel.h        et_key_action.h        et_state.h         pv_color.h            pv_render_option.h
et_define.h             et_layer_view.h        et_stroke_panel.h  pv_element.h          pv_renderer.h
et_doc.h                et_mouse_action.h      et_thumbnail.h     pv_element_general.h  pv_rotate.h
et_doc_history.h        et_mouse_cursor.h      et_tool_id.h       pv_element_info.h     pv_stroke.h
et_doc_history_hive.h   et_mouse_util.h        et_tool_info.h     pv_error.h            pv_svg_info.h
et_doc_id.h             et_pointing_manager.h  et_tool_panel.h    pv_file_format.h      pv_type.h
et_doc_manager.h        et_position_panel.h    pv_anchor_point.h  pv_focus.h            pv_vg.h
et_doc_relation.h       et_renderer.h          pv_appearance.h    pv_general.h

===



# コーディング

## ソース(関数・変数etc)のネーミング

sed, grep, vim, ctags, が効く名前を付けるべき。今はGtk3ライクな名前を付けている。
 vim上で一意性のある名前にするかは悩ましい(悩んでる)
 ctags対応には一意な名前を付ける。変に同名(static関数とか?)を付けなければ大丈夫。

特に開発中のプロジェクトは、実装により適切な名前が別にあることがわかったりするので、関数ローカルの一時変数名まで、置き換えを意識して付けるようにしている。
つまり、同名を避けることで、
 sed -i s/AA/BB/g */*.[hc]
 grep -r AA */*.[hc]

が効くように名前を付ける。
例えば、"Doc"を"Document"に直すときに、「ほぼ一括置換」くらいの手間で置き換えられる状態を目指している。
===
 11 struct EtDoc;¬
 12 typedef struct EtDoc EtDoc;¬
 13 ¬
 14 typedef int EtCallbackId;¬
 15 typedef void (*EtDocSlotChange)(EtDoc *doc, gpointer data);¬
 16 ¬
 17 ¬
 18 ¬
 19 EtDoc *et_doc_new();¬
 20 EtDoc *et_doc_new_from_vg(const PvVg *vg);¬
 21 void et_doc_delete(EtDoc *);¬
 22 EtDocId et_doc_get_id(EtDoc *self);¬
 23 char *et_doc_get_new_filename_from_id(EtDocId doc_id);¬

===



## C99(C11)を使う

変数の宣言、ただしgotoエラー処理と相性が悪い...
stdbool.hのbool型
const使う
安全な文字列操作 snprintf, strlcpy, g_strdup_printf
 strlcpy, g_strdup_printfは、それが無い環境では互換関数を書いたりしている。

# 未定義動作を避ける

最近は浸透してきた?
未定義動作の存在の認知度ってどのくらいなのだろう。
clang、本の虫、JPCERT CC、
 http://blog-ja.intransient.info/2011/05/c-13.html
 https://cpplover.blogspot.jp/2014/06/old-new-thing.html
 https://www.jpcert.or.jp/sc-rules/c-pre00-c.html

## ヘッダはシステム、自分ソースの順

ヘッダを書き間違えた時にとんでもないエラーメッセージが出ることがあるので、ヘッダはシステムヘッダを上に書く。
// と言いつつ、.h:.c一対一対応のコードはCソースの一番上に対応ヘッダファイルを書いているが。
===
  6 ¬
  7 #include <gtk/gtk.h>¬
  8 #include <gdk/gdk.h>¬
  9 #include <stdbool.h>¬
 10 #include "pv_element_general.h"¬
 11 #include "pv_color.h"¬
 12 #include "pv_stroke.h"¬
 13 #include "pv_appearance.h"¬
 14 ¬
===


## typedef enum定数

例えばアイコンをIDで管理して実体を引く関数を書くなら、get_icon_from_id(int icon_id)よりは(IconId icon_id)のほうが良いかと。
内部的にはint的なモノなのであくまで読みやすくするだけの糖衣。
コンパイラチェックが得られるとは期待しない。
===
 62 // ** ElementKind定数¬
 63 ¬
 64 typedef enum _PvElementKind{¬
 65 >-------PvElementKind_NotDefined,¬
 66 >-------/* special element document root */¬
 67 >-------PvElementKind_Root,¬
 68 >-------/* complex element kinds (group) */¬
 69 >-------PvElementKind_Layer,¬
 70 >-------PvElementKind_Group,¬
 71 >-------/* simple element kinds */¬
 72 >-------PvElementKind_Curve,¬
 73 >-------PvElementKind_Raster, /* Raster image */¬
 74 ¬
 75 >-------/* 番兵 */¬
 76 >-------PvElementKind_EndOfKind,¬
 77 }PvElementKind;¬
 78 ¬
===


## class的C記法 typedef struct & classメンバ的Utility関数

Gtkベースな、C++ classメンバなんて、C関数self引数の糖衣構文に過ぎない的思想。
最初はthisと名づけていたら、googletest側でC++予約語と衝突して、selfにリネームしなければならなくなった。
===
168 PvElement *pv_element_new(const PvElementKind kind)¬
169 {¬
170 >-------PvElement *self = (PvElement *)malloc(sizeof(PvElement));¬
171 >-------pv_assert(self);¬
172 ¬
173 >-------self->parent = NULL;¬
174 >-------self->childs = NULL;¬
175 ¬
176 >-------const PvElementInfo *info = pv_element_get_info_from_kind(kind);¬
177 >-------pv_assertf(info, "%d", kind);¬
178 >-------pv_assertf(info->func_new_data, "%d", kind);¬
179 ¬
180 >-------self->data = info->func_new_data();¬
181 >-------pv_assertf(self->data, "%d", kind);¬
182 ¬
183 >-------self->color_pair = PvColorPair_Default;¬
184 >-------self->stroke = PvStroke_Default;¬
185 ¬
186 >-------self->kind = kind;¬
187 ¬
188 >-------self->etaion_work_appearances = pv_appearance_parray_new_from_num(NUM_WORK_APPEARANCE + 1);¬
189 >-------pv_assert(self->etaion_work_appearances);¬
190 ¬
191 >-------return self;¬
192 }¬
193 ¬
===


## 分岐の方法

switch構文が読みやすくて好き > VimのデフォルトとLinuxでインデントルール衝突しているがまあそれはそれ。
switch文は縦横サイズを食うので、サンプルコードは略す。

## Info型とget_info_from_kind()関数

不満はあるが、Info型をKind,Id,Indexのいずれかで引く方式を使っている。
記事参照 「Cに欲しい機能 インデックス番号付き構造体配列」
===
1739 ¬
1740 const PvElementInfo _pv_element_infos[] = {¬
1741 >-------{PvElementKind_Root, "Root",¬
1742 >------->-------.func_new_data>->------->------->-------= _func_group_new_data,¬
1743 >------->-------.func_free_data>>------->------->-------= _func_group_free_data,¬
1744 >------->-------.func_copy_new_data>---->------->-------= _func_group_copy_new_data,¬
1745 >------->-------.func_write_svg>>------->------->-------= _func_group_write_svg,¬

1757 >------->-------.func_get_rect_by_anchor_points>>-------= _func_notimpl_get_rect_by_anchor_points,¬
1758 >------->-------.func_set_rect_by_anchor_points>>-------= _func_notimpl_set_rect_by_anchor_points,¬
1759 >------->-------.func_get_rect_by_draw>->------->-------= _func_notimpl_get_rect_by_draw,¬
1760 >------->-------.func_apply_appearances>>------->-------= _func_nop_apply_appearances,¬
1761 >-------},¬
1762 >-------{PvElementKind_Layer, "Layer",¬

===

## データ構造の生成というか確保というか

malloc,freeで生成・削除し、ポインタで保持する。
構造体の中身を見せたくなければ、Cのimplイディオムのように、ヘッダに宣言のみ書く方法で定義を隠す。

可変長配列は、ポインタ配列をポインタポインタで確保して使うことで実現している。
Cを使っているので、可変長配列が使いたければmalloc,freeと仲良くするしかない(mallocの速度については必要になるまでは忘れる)。

 size_t *_get_parray_num()トリックで個数を取るとイテレートがやりやすい。
 for(int i = 0; i < (int)num; i++) が定型文になりつつある。
===
 170 >-------int num = pv_general_get_parray_num((void **)elements);¬
 171 >-------for(int i = 0; i < num; i++){¬
 172 >------->-------const PvElement *element = elements[i];¬
 173 >------->-------const PvElementInfo *info = pv_element_get_info_from_kind(element->kind);¬
 174 >------->-------et_assertf(info, "%d", element->kind);¬
 175 ¬
 176 >------->-------PvRect rect = info->func_get_rect_by_anchor_points(element);¬
 177 ¬
 178 >------->-------if(0 == i){¬
 179 >------->------->-------rect_extent = rect;¬
 180 >------->-------}else{¬
 181 >------->------->-------rect_extent = pv_rect_expand(rect_extent, rect);¬
 182 >------->-------}¬
 183 >-------}¬
 184 ¬
 185 ¬
 

===

## エラー返り値

NULLまたはbool falseに統一。
それ以外が必要なら最終引数に "~bool *is_error)"
引数が不正な場合、Vecterionはabort()するが、PhotonVectorはエラーを返す方針。
===

  8 #define et_assert(hr) \¬
  9 >-------do{ \¬
 10 >------->-------if(!(hr)){ \¬
 11 >------->------->-------fprintf(stderr, "et_assert: %s()[%d]:'%s'\n", __func__, __LINE__, #hr); \¬
 12 >------->------->-------assert(hr); \¬
 13 >------->-------} \¬
 14 >-------}while(0);¬
 15 ¬
 16 #define et_assertf(hr, fmt, ...) \¬
 17 >-------do{ \¬
 18 >------->-------if(!(hr)){ \¬
 19 >------->------->-------fprintf(stderr, "et_assertf: %s()[%d]: "fmt"\n", \¬
 20 >------->------->------->------->-------__func__, __LINE__, ## __VA_ARGS__); \¬
 21 >------->------->-------assert(hr); \¬
 22 >------->-------} \¬
 23 >-------}while(0);¬
 24 ¬
 25 // Caution: depend gcc¬ 33 #define et_error(fmt, ...)  \¬
 34 >-------fprintf(stderr, "error: %s()[%d]: "fmt"\n", __func__, __LINE__, ## __VA_ARGS__)¬ 37 #define et_debug(fmt, ...)  \¬
 38 >-------fprintf(stdout, "debug: %s()[%d]: "fmt"\n", __func__, __LINE__, ## __VA_ARGS__)¬
 

===


...という感じです。
本当はもっといろいろありますし、どうしてこれが良さそうか、という理由も、いずれ書こうと思っています。
また、ここに書いたのとは別に Vim (その2) Advent Calendar 2016 にVimでCアプリケーションを開発する話を書く予定です。そちらもよろしくお願いします。

この記事は C言語 Advent Calendar 2016 の12日目の記事でした。
13日目の明日はyashi氏の「The Meson Build System」です。楽しみですね。

GdkPixbufを高速にリサイズ(cairo)


GdkPixbufのサイズ変更を、元画像500x500px から 出力画像100x100~10x10pxで行うと、どうやらとても遅くなるようです。



GDK_POINTER_MOTION_HINT_MASK を使わずに motion-notify-eventをそのまま再描画に投げている私も悪いのですが。
とはいえ GDK_POINTER_MOTION_HINT_MASK はGtk3で思った通りにならず、アプリケーションがフリーズするようになっていました。

将来的には描画前のレンダリングをマルチスレッド化するつもりではいます。しかしシングルスレッドだと競合などを考えずに済み楽であるため、どうにもならなくなるまではシングルスレッドで行くつもりです。

というわけで、単純にリサイズ機能を高速化して対応。このあたりも、cairo任せにするといろいろオブジェクトを確保するコストを考えてもずっと早いのではないかというカンが当たった感じです。


なお、 gdk_pixbuf_scale_simple()をそのまま、画質を GDK_INTERP_HYPER からバイリニア指定に変えるのは試してみましたが、ほとんど効果なしでした。

なお画質は不明。見た目にわからないですし悪くないですが、アプリケーションが将来的に画質を指定するようになったら、リサイズ機能を見直そうと思います。

呼び出し例は以下。
====
/*

 
GdkPixbuf *pb = gdk_pixbuf_scale_simple(

 

 

 
pixbuf,

 
 
 
(int)w, (int)h,
 
 
 
GDK_INTERP_HYPER);
*/

 
GdkPixbuf *pb = _pv_copy_new_pixbuf_scale(
 
 
 
pixbuf,
 
 
 
(int)w, (int)h);
====

実装は以下。
====
static GdkPixbuf *_pv_copy_new_pixbuf_scale(GdkPixbuf *pb_src, double w_dst, double h_dst)
{
 
cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w_dst, h_dst);
 
pv_assert(surface);
 
cairo_t *cr = cairo_create (surface);
 
pv_assert(cr);

 
double w_src = gdk_pixbuf_get_width(pb_src);
 
double h_src = gdk_pixbuf_get_height(pb_src);

 
cairo_matrix_t m = {
 
 
w_dst/w_src, 0,
 
 
0, h_dst/h_src,
 
 
0, 0,
 
};
 
cairo_set_matrix(cr, &m);

 
gdk_cairo_set_source_pixbuf (cr, pb_src, 0, 0);
 
cairo_paint (cr);

 
GdkPixbuf *pb = gdk_pixbuf_get_from_surface(surface, 0, 0, w_dst, h_dst);
 
pv_assert(pb);

 
cairo_surface_destroy (surface);
 
cairo_destroy (cr);

 
return pb;
}
====

作ったPixbufは呼び出し元で開放しましょう。
以上です。

BLADE v7 liteについて(スマホ買いました)

BLADE v7 liteを買って、半年使ったので、本日レビューを書き散らします。
半分くらいAndroid6.0レビューみたいになるかも。
 
NTT-Xで2万円弱で買った。

BLADE v7 Liteと付属物

BLADE v7 Liteを選んだ理由は、Android6.0が使える端末で他に手頃なものが無かったから。
同価格のASUSのZenPhoneのほうがCPUが少し良いので、そちらでも良いかと思った。ノートPCはASUSなので揃えてみたかったし。
だが最終的に、ZenPhoneにはGPSに問題があるという話と、やはりAndroid5.xではなく6.0にしたいということでBLADE v7 liteにした。


開いてすぐBLADE v7 lite端末


多言語でオモテウラ有りのペライチ、マニュアルはこれっきり。


この端末、届いてから3週間近くSIMを待っていた。SIM契約のアテが外れたから。
初期設定でSIM設定をスキップすると、後が面倒そうだったので、WiFi運用などもせずバッグに放り込んでいた。この間ほぼ無充電だったが、2週間くらい放っておいてもBLADE v7 liteは普通に起動した。電波を掴んでいなかったからかもしれないが。

鎮座するBLADE v7 lite


途中で一度、Y!MobileのSIMが差し込まれた。
(店頭での動作確認。きちんとブラウザでWebサイトが見れることを確認した。通話は未確認)
Y!Mobileはパケット繰越しないと言われたためSIMを買わなかった。
UQMobileのSIMは試していない。店頭で契約しようとしたら、店員に「未対応端末では壊れるかもしれないからやめたほうがいいです」と静止されたから。これについては感謝している。次があればまたUQMobileを検討しよう、と思ったくらい。

閑話休題。

その後、MineoのSIMを契約して家で刺して、動いた。
Mineoの設定はあらかじめ登録されている。ユーザ名とパスワードも共通のものだったようで、デフォルトのままで動いた。SIMのMNP切替中に、何度か"モバイルデータの有効化"だったかを無駄にON/OFFしていたが、ともかく1時間も置いておいたらインターネットも通話もできるようになった。

なお、microSDは、秋葉原で1000円程度で買った32GBのTOSHIBA製を新規購入して入れた。

前の端末は3.5年前のAndroid4.2端末だった。電池がヘタっていたのと、ブラウザ(chromeその他問わず)が3タブほど開くと、タブ切り替えの度にページ再読み込みするのが不満だった。

BLADE v7 liteにして、ブラウザ再読み込みはかなり減った。chromeは前の端末でも最新版だったと思うし、CPU/memoryも前の端末とあまり違わないので、Android6.0の性能なのだろうと思う。

chromeの表示が豆腐になる問題も、システムアップデートを実行したら消えた。システムアップデートのサイズは忘れてしまったが、500MBも無かったと思う。

買ったばかりの端末だからか、電池の減りも少ない。前の端末は、行きにブラウザを開きつつ音楽を聞いていただけでも、会社で充電しないと帰宅中に電池切れでシャットダウンしていた。BLADE v7 liteにしてからはそれがない。試していないが、1日充電しなくても持つと思う。

以下、気にしていなかったのだけれど、新端末になってよかった点がふたつ。

前の端末ではほとんど読んでいなかった『通知』をよく見るようになった。前の端末で通知は不要なものが入り混じって読みづらかったし、整理する方法もわからなかった。今はSMSもGMailもSkypeメッセージも通知から知るし、ダブルクリックで飛んで読む。
前の端末は通知と思わしきLEDが常に点滅していて、未読のためか消えずに不愉快ですらあった。今はそういうことはない。新端末のLEDが光るとき、その色が何を示すのかは未だに知らないままだが。特に困ってはいない。

標準キーボードがよくなっていた。Simejiなどは入れたことがない。誤入力を起こさない工夫か
フリックなどの入力で指を動かす距離が少し増えたと思う。私が買ったBLADE v7 liteを触った友達も同じことを言っていたので、感覚としては間違いないと思う。
唯一残念なのは、Qwertyキーボードで日本語が打てないこと。これは設定変更でできるようになるのか、後で調べるつもり。今はチャットなどはQwertyキーボードでアルファベットだけ入力し、エセ英語で返事している。元から携帯であまり文字入力しないし。

テザリングの手間は変わっていない。あまり使われない機能なのだろう。あるいは、UIの位置を浅くすると、誤認識でテザリングが起動しそうとかあるのだろうか。
テザリングON/OFFボタンのアプリを探そうかとも思った。が、一部にAndroid6.0対応できているアプリがあるようなので、もう少し経って、必要になってからその時に探そうかと思う。

Android6.0からアプリケーションの権限が詳細に設定できるようになったが、こちらの恩恵はまだ受けていない。一部のアプリで細かく聞かれるようになっただけとも言える。現在はとりあえず、LINEに連絡先を読ませる許可を与えていない。はず。

BLADE v7 liteに付属するメールアプリケーションに登録アカウントを解除できない問題があるとのことだが、使っていない。GmailなのでGmailのアプリケーションを使っている。Mineoのメールアドレスは使っていないし、スマホに紐付けしているメインのメールアドレス以外は、すべてPCで扱うので。

この前実家に帰った際に歩きまわったが、ZenPhoneは知らないが、BLADE v7 liteはGPSは問題なかったと思う。
ポケモンGOはダウンロードして少しだけ動かしたが、まだ最初のポケモンをゲットしていないので動作確認としてはよくわからない。少し歩くとピカチュウ出るって本当ですか。


この1週間くらい、運用してきたレビューとしては以上の通り。

気になった現象は以下。

このレビューを書いている今日、帰りに音楽プレイヤーの音楽が止まった。
(再生表示のまま。再度再生すると続きが鳴った)
また、今日はじめて、Skypeクライアントがタップに反応しなくなったことが気になる。

音楽プレイヤーはアプリケーション側の問題かもしれないので、後でプレイヤーを変えてみる。Skypeクライアントの問題は、剥がれかけの保護フィルムのせいかもしれない。

保護フィルムは、買った時のものをまだ剥がしていないだけ。操作はできるが、剥がす前提なので一週間と持たず剥げてくる。
昨日、ヨドバシ.comでBLADE v7 lite専用の保護フィルムを注文したので、明日あたり届くと思う。

マイナAndroid端末は、保護フィルムもすぐに在庫されなくなるはず。前の端末もそうだった。といっても、前の端末と同じように、後半はフィルム無しで運用するつもりでいる。

安いAndroid端末の気楽さは、iPhoneなどと違って、壊れたらすぐに新しい端末に買い換えてしまえることだと思う。画面の割れたiPhoneを使い続けるよりは良いと思っている。
ケースには入れないつもり。保護する必要はないし、厚くしたくない。
ギリギリポケットに入るサイズなので、ポケットに入れて運用するつもり。
Android、特にマイナなAndroid端末は、ケースも汎用品などで限られてくる。iPhoneのようにたくさん有るケースを選びたい向きには向かない。



というわけで、今のところ BLADE v7 lite を気に入っている。
思えば、これまで携帯は仕方なく嫌々で買っていた。携帯電話を積極的に自分で選んだのは初めてである気がする。

Windows環境でMakeする際に起こるエラーと対処


Make オルタネイティヴは数あれど、小さな実験プロジェクトを始めるときに、簡単に使えるMakeは未だにビルドツールとして有力な選択肢だと思います。

MakefileはUNIX環境、というかCLIツール群があることを前提にしているところがあります。Windows環境ではUNIXコマンドがそもそも無かったりするため、Makefileが可搬ではないです。



GitHubに公開しているmkwin_mnでは、mkdir -p と rm -f を解決しました。
以下には ls find が無いことにより発生する問題もあり、wildcard関数による解決は満点とは言いがたいので、誰か対処してくれないかなと思っています。
Cygwin入れろと言われると返す言葉がないです。(MinGWといろいろ共存面倒そう、という言い訳がある。きちんと調べて対処すべきなのだろうけれど。)


 # ls, findが無いことにより起こるエラー


====
 process_begin: CreateProcess(NULL, sh C:\Users\nuka\Documents\etaion_20160920_22
h17m\etaion\library\gtk+-bundle_3.6.4-20130921_win32\bin\xml2-config --cflags --
libs, ...) failed.
cc1.exe: error: ./: No such file or directory [-Werror=missing-include-dirs]
source/pv_color.c:130:1: fatal error: opening dependency file object/pv_color.d:
 No such file or directory
 }
 ^
cc1.exe: all warnings being treated as errors
compilation terminated.
Makefile:40: recipe for target 'object/pv_color.o' failed
mingw32-make: *** [object/pv_color.o] Error 1
 process_begin: CreateProcess(NULL, ls source/*.c, ...) failed.

====

ルールによるビルドのために、ソースディレクトリ以下の *.c ファイルを探させます。Makefileを書くなら、大抵はこれをやらせます。
このとき、Make組み込みの wildcard 関数でなく、 shell lsshell find のように外部コマンドを呼び出していると、コマンドの無い環境でエラーが起こります。

wildcard()は子ディレクトリを再帰的に探索してはくれません。なので、以下のようなサブディレクトリのあるソースディレクトリ構成に対応できない問題が有ります。
src/
         + engine/
         + enemy/
サブディレクトリがそうそう増えないもので、階層も深く掘らないならば、サブディレクトリごとにwildcardを書いて、結果を加算していく



$(wildcard **.c) などとやると、子ディレクトリ内以下へ再帰的にマッチできるよ』
というStackOverflowに書いてあった小技。これの投稿の下に付いているコメント欄でも出来ない報告が書き込まれていましたが、わたしの環境ではUbuntu LinuxのGNU Make4.1でも使えなかったです。いったい何処由来のネタなのだろう。

閑話休題。



 # 出力先ディレクトリの生成に失敗したことにより起こるエラー


====
process_begin: CreateProcess(NULL, object, ...) failed.
make (e=2): wウスt@CェゥツゥワケB
Makefile:41: recipe for target 'object/pv_color.o' failed
mingw32-make: [object/pv_color.o] Error 2 (ignored)
 source/pv_color.c:130:1: fatal error: opening dependency file object/pv_color.d:
 No such file or directory
 }
 ^
compilation terminated.
Makefile:41: recipe for target 'object/pv_color.o' failed
mingw32-make: *** [object/pv_color.o] Error 1
====


出力先として想定ているobjectディレクトリが作れず、書き出し先が存在しないため、ファイルを作れなくて起こるエラーです。
mkdir -pが無いことが原因です。
GitHubに公開しているmkwin_mnでは、mkdir -p相当の機能を用意することで対処しています。
他に、objectなど、必要なディレクトリがあらかじめわかっているなら、object/.gitkeepなどを置いておいて、書き出し先ディレクトリを予めプロジェクトリポジトリに含めておくという手もあります。
その場合は誤って削除してしまわないよう注意。

以上です。

Gtk3でコンボボックス(ドロップダウン) Widgetを作る

Gtk3で、ComboBox(DropDown)を作るサンプルコードです。

これまでGtk3では公式ドキュメントを反面教師として、単体動作するコードをサンプルとして提示してきたつもりなのですが、今回は諦めました。すいません。なんというか、実用を目指すプロジェクトからコードを一部だけ、動く状態に抜き出すって無理ですね。



====
        // self->combo_linejoin = gtk_combo_box_new();
    //    GtkListStore *liststore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
        GtkListStore *liststore = gtk_list_store_new(1, G_TYPE_STRING);
        int num = get_num_stroke_linejoin_infos();
        for(int i = 0; i < num; i++){
            const PvStrokeLinejoinInfo *info = get_stroke_linejoin_info_from_id(i);
            gtk_list_store_insert_with_values(liststore, NULL, -1,
                            0, info->name,
    //                        1, info->linejoin,
                            -1);
        }
        self->combo_linejoin = gtk_combo_box_new_with_model(GTK_TREE_MODEL(liststore));
        g_object_unref(liststore);
        GtkCellRenderer *column = gtk_cell_renderer_text_new();
        gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(self->combo_linejoin), column, TRUE);
        gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(self->combo_linejoin), column,
                    "text", 0, NULL);
        gtk_combo_box_set_active(GTK_COMBO_BOX(self->combo_linejoin), 0);
        gtk_box_pack_start(GTK_BOX(self->box_linejoin), self->combo_linejoin, false, true, 3);
    }


====

ついでに使っている独自型などは以下。

====
 /*! @brief StrokeLinejoin */
typedef enum{
    PvStrokeLinejoin_Miter,
    PvStrokeLinejoin_Round,
    PvStrokeLinejoin_Bevel,
}PvStrokeLinejoin;

typedef struct{
    PvStrokeLinejoin linejoin;
    const char *name;
    int cairo_value;
}PvStrokeLinejoinInfo;

int get_num_stroke_linejoin_infos();
const PvStrokeLinejoinInfo *get_stroke_linejoin_info_from_id(PvStrokeLinejoin linejoin);


====

ドロップダウンリストの内容をGtkListStoreで作ります。それをGTK_TREE_MODEL形式でcombo_box_new_*()関数に流しこめば完了です。
一番重要なのはGtkListStoreがGtkTreeModelに変換できること、そしてそれがComboBoxに流し込めることです。

Python版コードなどを見てみると、GTK_TREE_MODELのところはタプルとか作ってそのまま流し込んでいて、確かに動的言語なら辞書配列が簡単に作れるし、それがコンボボックスの材料になるのも納得ですが、C言語だとこのなんだかしっくり来ない謎型変換をするコードになっています。

参考にしたのはこちら。
http://stackoverflow.com/questions/16630528/trying-to-populate-a-gtkcombobox-with-model-in-c
参考コードのほうが綺麗で完結していて読みやすいですね。
参考コードはコンボボックスの選択肢に色を付けるという愉快なサンプルコードになっていますが、これもしかしてここからオブジェクト取り出して選択した要素を一意に特定してコールバック処理の際に処理を分岐するとかの用途を想定しているんじゃないかな、私はコールバックの中でアクティブな選択肢の昇順を取ってきて、キャストなどもせず乱暴に元データのIndexに置き換えています。いずれはgpointer *data に選択肢のポインタそのまま投げ込んで判定するつもりだけれど、といった感じです。

====
g_signal_connect(self->combo_linecap, "changed",
            G_CALLBACK(_cb_changed_linecap_with_combo), NULL);


static void _cb_changed_linecap_with_combo(GtkComboBox *widget, gpointer user_data)
{
    EtStrokePanel *self = stroke_panel;
    assert(self);

    self->stroke.linecap = gtk_combo_box_get_active(GTK_COMBO_BOX(self->combo_linecap));

    _update_focus_elements_from_local();
}

====

以上です。

Gtk3アプリケーションをWindows環境でビルドした際に出たエラーと対処

ここでは、私の環境とビルド内容で出たGtk3固有のエラーとその対処について。Windows7 x64 と MinGWにて。

最初はビルド環境の構築その他を扱おうかと思ったのですが、他サイトでも少しづつ情報があるのでやめました。なんとなくできると思います。
Gtk2 Windows開発環境キットをGitHubにて公開しているので参考にどうぞ。
https://github.com/MichinariNukazawa?tab=repositories



今回は、gtk+-bundle_3.6.4-20130921_win32を使いましたが、見返してみると、
gtk+-bundle_3.10.4-20131202_win32.zip
とか、それなりに新しいのがありました。が、それでも2013年...。
その他バージョンなどダウンロードはこちらから。
http://win32builder.gnome.org/
この公式Gtk3パッケージ、バージョンを見ればわかるとおり、すでに非常に古いものとなっています。


 #  Gtk3バージョンが古いからか、未実装関数的なものがある?

====
 source/et_layer_view.c: In function 'et_layer_view_init':
source/et_layer_view.c:146:2: error: implicit declaration of function 'gtk_text_
view_set_monospace' [-Werror=implicit-function-declaration]
  gtk_text_view_set_monospace (GTK_TEXT_VIEW(self->text), TRUE);
  ^
cc1.exe: all warnings being treated as errors
Makefile:42: recipe for target 'object/et_layer_view.o' failed
mingw32-make: *** [object/et_layer_view.o] Error 1
====

対処:この関数だけWindows環境を判定してCソースにて #IFDEF 切って無効化しました。


 # SVG画像ファイルを読もうとすると失敗する。

実行時、それもSVG画像を読もうとしてはじめて発生するので、ちょっと厄介。
文字化けはGtk3の出力で別問題、今回は無視しています。
====
C:\Users\nuka\Documents\etaion_20160920_22h17m\etaion>make run
./build/etaion_vector.exe
debug: et_tool_init()[59]: 4
error: et_tool_init()[71]: 'サュシサ「ク・シォェュソセシセ: Z:/srv/win32builder/fixed_364/build
/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll: `Z:/srv/win32b
uilder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-s
vg.dll': ョ「ク・シォヲ、セ€'
BUG: main()[81]:
Makefile:50: recipe for target 'run' failed
mingw32-make: *** [run] Error -1

C:\Users\nuka\Documents\etaion_20160920_22h17m\etaion>
 、
 'Unable to load image-loading module:
 ./build/etaion_vector.exe
debug: et_tool_init()[59]: 4
error: et_tool_init()[71]: 'Unable to load image-loading module: Z:/srv/win32bui
lder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg
.dll: `Z:/srv/win32builder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loade
rs/libpixbufloader-svg.dll': 指定されたモジãƒÂ
¥Ã£Æ’¼ãƒ«ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。'
BUG: main()[81]:
Makefile:50: recipe for target 'run' failed
mingw32-make: *** [run] Error -1
====

libpixbufloader-svg.dll が見つからないことが原因、とのことです。

gtk+-bundle_3.6.4-20130921_win32\lib\gdk-pixbuf-2.0\2.10.0\loaders.cacheを見ると、
====
 "Z:/srv/win32builder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll"
====
などと書いてあるので、相対パスなどで *svg.dll のロードをするよう書き換え。

====
C:\Users\nuka\Documents\etaion_20160920_22h17m\etaion>
 "library/gtk+-bundle_3.6.4-20130921_win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll"
====

以上です。

mkwin_mnを作りました (Makefile in Windows toolkit)

mkwin_mnとは

小規模なMakefileを、Windows環境で動作させるための互換用ファイル群です。
主にMakefileの $(MKDIR_P) と $(RM) のWindows版を提供しています。
というかそれだけです。

用途に対して実用的であれば良しとしています。Unix CLIとの互換性は、高くはありません。完全コンパチを目指すつもりもないです。



ダウンロードする

GitHubで公開しています。
あなたのプロジェクトの中から git clone などしてください。
$ git clone https://github.com/MichinariNukazawa/mkwin_mn

## 使い方

ダウンロードしてきて、あなたのプロジェクトの Makefile にインクルードするだけです。簡単ですね。
include mkwin_mn.Makefile
非Windows環境との切り分けは、呼び出し側のMakefileでやってもらうようにしました。
そのほうが、環境のフラグに好きな方法を使えるから良いだろうと考えています。

Windows環境のmakeによる Makefile実行で、変数からの機能呼び出しにより mkdir -prm -f  (rm -rf) のようなことができるようになります。

具体的な使い方の例は、添付してある example_project を参考にしてください。

## mkwin_mnのターゲット

小規模プロジェクトの手書きのMakefileがターゲットです。

循環定義的ですが、小規模プロジェクトとは、今どきMakefileを手書きしているようなプロジェクトを指します。

## 動機

開発の動機は、現在Ubuntu環境で作成中のGtk3アプリケーションをWindowsでビルドしようとしたら make clean が機能しなかったから、です。

## mkwin_mnのアプローチ

2016年現在、GNU Makeを置き換えうる、次世代ビルドツールは乱立しています。
新言語には新ビルドツールが付いていて当たり前の雰囲気です。
次世代ビルドツールは当然のようにUnix/Windowsコンパチなので、どれかに移行できればWindows非互換問題は片付きます。

私はMakeはそれなりに黒魔術だと思うので、より良くサポートされた汎用ビルドツールがあるなら、積極的に切り替えたいです。

しかし、次世代ビルドツールはどれも普及度がイマイチで、Web上にエラー解決の情報が不足しています。文法エラーなどで詰まると、そのまま死ぬ。
その点、Makeは優秀です。ほかの追随を許しません。実環境で30年近く叩かれまくっているので、ワークアラウンドとナレッジが溜まっています。大抵の問題は、検索してStackOveflowなどを読めば解決します。


Windows環境に足りないもの(MKDIR_P, RM)を足してやる、というのがmkwin_mnのアプローチです。
既存のMakefileに(ほとんど)手を加えません。
GNU Makeのエコシステムに乗ったままでいられる、という他にはないメリットがあります。

## 実装の詳細

ざっくり実装について。ここから下は適当に流し書きしています。

win:MKDIRはUnixセパレータ'/'を指定しても大丈夫。
win:RMとwin:RMDIRは'\'で渡してやらないと駄目。
というわけで、文字列置換。
bat変数でも、bash変数やMakefile変数と似たような置換の文法が使えた。
ただし、batで変数を扱うには魔法が必要だった。
この魔法が何という文法なのかわからない。検索しようが無い。

今回batファイルを書きましたが、Windowsバッチはよくわかりません。
C++における江添氏のような文法解説がほしい。MSのエバンジェリストの方々はきっとAzureでPowerShellでMSSQLをどうこうするのが忙しいでしょうから期待できないですが。

!cons.batは私のバイブル「30日でできる OS自作入門」由来。
MinGWのmakeにエイリアスをかける拡張をしています。



『Makefileのコマンド変数をWindows用に置き換えて、バッチを呼び出す』
という大枠にしました。
『Unix CLIと同じexeファイルを用意する』
というアプローチは、いろいろ面倒であることが予想されたので、早々に諦めました。

バッチはbatとvbsで少し悩みました。多少は早くて軽くていろいろな環境で動くだろうと思って、batにしました。
vbsのほうが複雑な機能を書きやすいはずなので、もっと複雑な処理が必要になったら、その時はvbsで書き直すかもしれません。
どちらにしろ速度は大して出ないでしょうし、速度を求めるようなモノでもないと思います。

今後について

開発動機は現状で満たしてしまったので、今後の機能追加は無いかもしれません。

リポジトリのTODOにTestと書いてある通り、とりあえず作って、開発中のGtk3アプリケーションのプロジェクトには十分使えることを確認しただけです。
そういう物だとわかった上でお使いください。プルリクエスト、利用報告があれば嬉しいです。

組み込みJavaScriptエンジン Duktape 非公式日本語訳(途中)









Cアプリケーションに組み込める、フリーのJavaScriptエンジンを探していて、Duktapeというプロジェクトが見つかり、興味があったのでドキュメントを読んでいました。
以下は、途中までですが、読みながら翻訳していたものを公開します。



 == 以下、翻訳版注意書き ==





この文章は、アプリケーション組み込み向けEcmascript(Javascrip)エンジンであるDuktapeのドキュメントの非公式日本語訳です。
この翻訳は、自分がDuktapeについて把握することを目的として行ったものです。全体を通して翻訳が雑です。というか誤訳を含みます。翻訳ミスしても実際のサンプルコードと自作コードで確認すれば気づくだろう、だからいいや、というレベルです。訳しづらかったところを飛ばしていたりもします。翻訳ミス前提で読んでください。
日本語としてメチャクチャなので、頭の中で再度訳しながら読むことになるかと思います。
ライセンスは原文に準じます。原文が許す限り、新しい翻訳のベースなど自由に使っていただいて構いません。とはいえ、新規に訳したほうが早いでしょう。
指摘・修正はメール(michinari.nukazawa@gmail.com)またはtwitterに頂けると嬉しく思います。

原文
http://duktape.org/guide.html


== 以下、翻訳文 ==

version 1.4.0 (2016-01-10)

## このドキュメントの範囲

このガイドの内容は、あなたのプログラムでDuktapeを使いはじめるためのイントロダクションです。あなたが基礎知識をすでに持っているならば、わかりやすい<a href>API reference</a>にてAPI構成を一覧できます。<a href>Duktape Wiki</a>の内容は、さらなる概要と使用例、ベストプラクティスです。

このドキュメントはDuktapeの内部構造について扱いません。(もしあなたが我々と共にDuktapeを弄ることに興味があるようならば、Duktape repoをご覧ください。)

## Duktapeとは何か?

Duktapeは、移植性とコンパクトなフットプリントに注力した、組み込み可能なEcmascript E5/E5.1エンジンです。Duktapeにより、あなたのC/C++プログラムへ、スクリプティング機能を簡単に拡張することができます。あなたはEcmascriptの中へ、プログラムの中心的な制御フローを作ることができ、同時に高速動作するC関数を使い、重い処理を取り扱うことができます。

(訳注:適当訳)EcmascriptとJavascriptは、大まかに等価ですが、ブラウザ向け拡張などが付いている場合もあって等しくはありません。Duktapeも例外ではなく、よく使われるprint(),alart()を持っています。なので、我々はEcmascriptの全体を実装したと言い表しています。

## Conformance
 (略)
## Features
 (略)
## Goals
 (略)

## ドキュメント構成

<a href>Getting started</a>は、あなたがダウンロード、コンパイル、そしてDuktapeをあなたのプログラムに導入するまでを取り扱っています。これの内容は確固とした例によって、あなたがどのようにあなたのプログラムにスクリプティング機能を導入するかを含んでいます。
<Programming Model><Stack types><C types>で語られているのは、Duktapeヒープのコアコンセプト, context, value stacks, Duktape API, Duktape C関数 です。Dukatapeのスタック構造とC型システムのラッパーについて概要を語っています。

Duktapeが実装しているEcmascript機能についていくつかのセクションで語っています: Type algorithms (for custom types), Duktape built-ins (additional built-ins), Ecmascript E6 features (features borrowed from ES6), Custom behavior (behavior differing from standard), Custom JSON formats, Custom directives, Buffer objects, Error objects (properties and traceback support), Function objects (properties), Debugger, Modules, Logging, Finalization, Coroutines, Virtual properties, Internal properties, Bytecode dump/load, Threading, Sandboxing.

<Performance>では(略)

<Comparison to Lua>では(略)

## Getting started
(略)
## Downloading
(略)
## Command line tool
(略)

## Integrating Duktape into your program

コマンドラインツールは、簡単なサンプルプログラムでDuktapeの組み込み方法を示しています。
組み込んでDuktapeをあなたのプログラムで使う方法はシンプルです:duktape.c, duktape.h, and duk_config.hを追加してビルドし、Duktape APIをあなたのプログラムのどこかから呼ぶ。

Duktapeの配布物に含まれるとても単純なサンプルプログラムの、hello.cがそれを示します。コンパイルとテストをこのプログラムで試すには():

The distributable contains a very simple example program, hello.c, which illustrates this process. Compile the test program e.g. as (<Compiling>の compiler optionも参照してください):
(サンプル略)
テストプログラムは、Duktape contextを作り、任意のEcmascriptコードを実行します:
(サンプル略)

Duktapeは組み込み可能なエンジンであるため、あなたのプログラムの基本のコントロールフローを変更する必要はありません。一般的な使い方は:
・プログラムを起動した際に、Duktape contextを作る(または、スクリプティングが必要となったタイミングで作る)。(訳注:適当訳)通常は、あなたのスクリプトがロードされる間に初期化が終わり、必要なタイミングでそれを実行することができます。
・一意な箇所があなたのプログラムの中にあり、そこでscriptを実行する、またscript内の関数をそこに挿入して呼ぶ。
・script内関数を呼ぶ。最初にpushして呼び出した引数はDuktapeコンテキストのvalue stacksに入れられてDuktape APIから使われる。これは、Duktape APIが実際に呼び出された際に使われる。
・ひとつのscriptを最後まで実行する。戻り値を渡す方法を制御でき、あなたのプログラムへ(Duktape API呼び出しの)戻り値として渡すか、Duktapeコンテキストのvalue stackに置くことができる。Cコードはこれらの戻り値をDuktape APIを通して使うことができる。

単純なサンプルプログラムを見てみましょう。プログラムはstdinを使った行の読み込みをCのmainloop内で行い、呼び出したEcmascriptの助けにより行文字列を変換して、結果を出力します。

スクリプトコードは"process.js"です。サンプルの文字列変換関数は、プレーンテキストの行文字列をHTMLに変換し、アスタリスク'*'で囲まれた文字を太字(bold)にします。
(サンプルコード略)
Cコードは"processlines.c"で、Duktapeコンテキストを初期化し、scriptを実行し、スクリプトが変換する処理対象の行文字列をstdinから読み込み、processLine()関数を行文字列ごとに実行します。
(サンプルコード略)

サンプルコード内のDuktape機能呼び出し箇所を、部分ごとに見て行きましょう。(適当訳)さらなる情報が欲しいならば、<Programming model>に書いてあります。
(サンプルコード略)
まずDuktapeコンテキストを生成します。コンテキストはEcmascriptの変数を変換し、value staskに入出力(pushing,popping)します。以降のDuktapeAPI動作の呼び出しではvalue stackが付き、入出力(pushing,popping)と実行がスタック上で行われます。製品コードには、望むならば、duk_create_heap()によってfatal error handlerをセットすることができます。<Error handling>を参照することで、エラーハンドリングのベストプラクティスについての議論を見ることができます。
(サンプルコード略)
評価(eval)呼び出しで"process.js"ファイルを読み込み、それがスクリプトをコンパイルし実行します。scriptに登録されているprocessLine()関数はEcmascriptグローバルオブジェクトへ、後で使うために格納されます。実行の際、スクリプトエラー・シンタックスエラーはハンドリングされ、fatal errorはハンドリングされません。もしエラーが起これば、エラーメッセージは強制的に安全に使うduk_safe_to_string()を通して提供され、それ以上に深刻なエラーにはなりません。実行結果の文字列は強制的にconst char * (ポインタ)に変換されるためリードオンリで、NULL終端のUTF-8エンコーディング文字列であるため、printfで直接使うことができます。
(サンプルコード略)
1行目は、Ecmascriptグローバルオブジェクトをvalue staskにpushしています。2行目はグローバルオブジェクトからprocessLineプロパティを探索しています("process.js"スクリプトファイル内に定義されている)。引数の-1はvalue stackのインデックスで、マイナス値はスタックを先頭から始め、よって-1はグローバルオブジェクトを一番先頭にスタックします。
(サンプルコード略)
Pushして行文字列が入っている文字列ポインタをvalue stackに入れます。文字列長は自動で、NULL終端で測られます(strlen()のように)。Duktapeは文字列のコピーを作成してstackにpushしますので、この関数の呼び出し後に文字列バッファを開放・書き込みしても構いません。
(サンプルコード略)
ここでvalue stackに入っているものは:グローバルオブジェクト,processLine関数,行文字列。
duk_pcall()メソッドは関数を実行し、指定された数の引数をvalue stack上から与え、それらを関数,引数,戻り値に置き換えます。ここで実行後のvalue stackに入っているものは:グローバルオブジェクト,実行結果 です。実行時のエラーは保護されており、取得してprint出力できます。duk_safe_to_string() APIを呼び出して安全エラーを表示することができます。最終的に、結果(またはエラー)がvalue stackからpopされます。
(サンプルコード略)
Duktapeコンテキストを破棄し、コンテキストのすべてのリソースを開放します。この呼び出しはvalue stackとその上の参照をすべて開放します。このサンプルではvalue stackの左端にglobal objectが入っているというくわだてです。これは問題ありません:メモリリークは起こりません、もしheapを破棄する際にvalue stackが空でも。

コンパイルは次のように。
(サンプルコード略)
テスト実行(process.jsがカレントディレクトリに必要)
(サンプルコード略)

## EcmascriptからCコードを呼び出す(Duktape/C bindings)

この導入サンプルでは、どのようにEcmascriptからCコードを呼び出して実行するか、難しくないCコードをEcmascriptから簡単に呼び出す方法を示します。

Ecmascriptは例にもれずしばしば、逆のシチュエーションとして中からCコードを呼び出したくなります。事例としては、スクリプト内で何らかの処理をとても多く実行する場合や、最適化されない低レベルbyte操作や文字列操作です。最適化されたCヘルパが呼び出し可能であり、あなたはすばらしいEcmascript言語の中で、あなたのスクリプトのロジックを記述して、その中で性能要求がクリティカルな部分をCコード呼び出しにすることができます。別の推測では、ネイティブなライブラリへのアクセスに使われるでしょう。

あなたが書くネイティブC関数の実装は標準的なC関数を、特殊な呼び出し規約の Duktape/C バインディングに適合させたものです。Duktape/C 関数は1つの引数を持ち、Duktapeコンテキストと1つの戻り値としてエラーまたは数値戻り値を持ちます。関数が呼び出し時の引数を得たり戻り値をセットする際にはDuktapeコンテキストのvalue stackに対して、Duktape APIを用いてそれを操作します。我々がさらに深くDuktape/CバインディングとDuktapeAPIを見るのはもう少し後にしましょう。サンプルは:
(サンプルコード略)
行ごとに見ていきます:
(サンプルコード略)
value stackのインデックス0番(スタックの先頭から、関数呼び出し時に指定された最初の引数)の内容が数値(number)であるか確認しています、この場合はこれは数値(number)です;もしそうでなかったなら、errorを投げて、値を返しません。もし内容が数値(number)であるならば、その数値をdouble型で返します。
(サンプルコード略)
引数を2乗して、value stackにプッシュします。
(サンプルコード略)
関数呼び出しからreturnして、これ(訳注:return 1)が指し示すものは(1つの)戻り値がvalue stackの先頭に存在することです。あなたはreturn 0を返すこともでき、これは戻り値が無いことを示します(DuktapeのデフォルトではEcmascriptのundefinedを返す)。負の値の戻り値は、errorが発生し、自動的に投げられたことを示す:これはエラーの投げ方としては単純なものです。なお、あなたがstackから値をポップして返すことを必要としないならば、Duktapeはあなたに関数からのreturnを自動で提供することができます。Programming modelにて詳細を参照ください。

我々の素数判定コードはネイティブコードによりEcmascriptで記述してあるアルゴリズムを高速化することができます。さらにテストプログラム側では1000000以下から素数を探すことに加えて値の下位が'9999'で終わる数値を探します。Ecmascript版プログラムは以下の通り:
(サンプルコード略)
雑記として、このプログラムはネイティブ関数を呼びますが、もしそれが使えない場合にはEcmascript版にフォールバックします。これにより、Ecmascriptコードは(訳注:今回のサンプルプログラム以外の)他のプログラムからも使用可能になります。一方で、もしこの素数判定プログラムを他のプラットフォームへ移植する場合でもネイティブコードは再コンパイル以外の変更は必要なく、ヘルパ関数を移植するまで遅いだけです。このプログラムの場合、ネイティブなヘルパ関数を検出するのはスクリプトを読み込む際です。あなたは一方でヘルパ関数の検出を任意のタイミングにフレキシブルに変更することができます。

ネイティブヘルパ関数には機能としてprimeCheckEcmascriptと等価なものを素直に実装しました。そこにmainを追加した"primecheck.c"は以下の通りです:
(サンプルコード略)
新しい関数呼び出しがあるので、行毎に見ていきます:
(サンプルコード略)
この2行の呼び出しは2引数をネイティブヘルパ関数が要求するためです。もし値がEcmascriptの数値型ではなかった場合、エラーを投げます。もしこれらが数値であった場合、数値を整数に変換し、それぞれローカル変数のval,limに格納します。index 0が第一引数でindex 1が第二引数です。

技術的には、duk_require_int()が返すのはduk_int_t型です;これは間接的にintへ変換されますがそれはintの長さが16bitまでしかない環境では問題が発生します。一般的なアプリケーションであれば、あなたはこれを心配する必要はありません。さらなる詳細はC typesを参照ください。
(サンプルコード略)
value stackにEcmascript falseをプッシュします。Cコードからreturn 1した場合、呼び出し元のEcmascript側ではこれを戻り値falseが返されたと解釈します。
(サンプルコード略)
1行目は、この後に、Ecmascriptグローバルオブジェクトをvalue stackにpushしています。2行目は、Ecmascript関数オブジェクトを作成し、それをvalue stackにpushしています。関数オブジェクトは、Duktype/C関数であるnative_prime_checkをバインドしてます:この時にEcmascript関数を、Ecmascript側から呼ぶために作成することで、C関数を呼び出せるようになります。第二引数の(2)は、このC関数が呼び出される際にvalue stackに必要な引数の数を示しています。もし呼び出し元がこれより少ない引数で関数を呼び出した場合、足りない引数はundefinedで埋められます; もし呼び出し元がこれより多い引数で関数を呼び出した場合、余った引数は自動で無視されます。最後の3行目で実行されていることは、関数オブジェクトをグローバルオブジェクトに格納しつつ、"primeCheckNative"という名前を付けながら関数オブジェクトをスタックからpopして取り出しています。
(サンプルコード略)
このときvalue stackにはすでにglobal objectがスタックの先頭に格納されています。1行目はprimeTest関数をglobal objectから探索しています(ロード済みのスクリプト内で定義されています)。2-4行目は、primeTest関数を引数無しで呼び出し、保護されたエラーが起こっていないかチェックしています。5行目は、実行結果をpopしてスタックから取り除いています; ここで戻り値は必要でないため。

コンパイル方法は以下:
(サンプルコード略)
実行します("prime.js"がカレントディレクトリに必要です):
(サンプルコード略)
さらに素数チェックに必要な時間を測って、スピードアップがEcmascriptと比べてどの程度有効か見てみましょう。あなたがこれを試すには"prime.js"を編集してネイティブヘルパ関数を無効にします。
(サンプルコード略)
再コンパイルして再実行すると:
(サンプルコード略)

## プログラミング・モデル

## 概観

Duktapeによるプログラミングは直裁的には以下の通りです。
・Duktapeソースコード(duktape.c)とヘッダ(duktape.hとduk_config.h)をあなたのビルドに追加
・Duktapeのheap(ガベージコレクション領域)を作成し、context(スレッドハンドラ的なもの)を初期化することを、あなたのプログラム内で行う
・必要なEcmascriptスクリプトファイルを読み込み、Duktape/C関数を準備しておく。Duktape/C関数はC関数をEcmascriptコード側から呼び出すことで、妥当な性能を出すことやネイティブライブラリへのアクセスを提供する。
・Duktape APIを用いて、Ecmasccript関数を妥当なタイミングで呼び出す。Duktape APIは関数と変数へのアクセスを提供する。変数はC変数型とDuktape内部形式(Ecmascript互換)を相互変換することができる。
・Duktape APIは一方で、Duktape/C関数(をEcmascriptから呼び出した)から、関数呼び出し時の引数および、戻り値へアクセスする方法を提供する。

これらすべてのステップを見ることが、我々の提供するさらなる情報にアクセスするのに必要です。

Heapとコンテキスト
Duktape heapは、ガベージコレクションを提供する単一の領域です。heapは文字列のために確保される領域、Ecmascriptオブジェクトやその他の変数に必要な領域、ガベージコレクション情報などに使われます。heap上のオブジェクトは、heapの先頭に持っている管理に必要な情報により、リファレンスカウント、mark-and-sweepガベージコレクション、オブジェクトのfinalizationなどを行います。Heapオブジェクトはリファレンスなどにより、ガーベージコレクションから見た到達可能性グラフ(reachability graph from a garbace collection perspective)を作ります。インスタンスに対しては、Ecmascriptオブジェクトへの参照を、キーと値によるオブジェクトとプロパティのセットにより提供します。あなたは複数のheapを持つことができ、オブジェクトは異なるheap上に直接存在することができません; (適当訳)あなたは異なるheap間では値をserializationしなければなりません。

Duktapeコンテキストは、Ecmascriptの"実行スレッド"としてDuktape heap内に生成されます。コンテキストはduk_context *によりDuktape APIから参照可能で、Duktape内部のコルーチン(a form of a co-operation thread)により組織化されます。いくつかのコンテキストを、共通のglobal objectを(訳注:ベースの?)環境として組織化することができます; コンテキストは任意のglobal objectを共有することができる一方で、異なる環境を持つことができます。コンテキストハンドルはほとんどのDuktape API呼び出しが要求し、そこからDuktapeコルーチンでvalue stackへアクセスします: 値の挿入と探索、関数の呼び出し、など。

コルーチンたちはcall stackにより実行を制御され、関数呼び出しの追跡、ネイティブまたはEcmascriptの分類、などをEcmascriptエンジン内で行います。(適当訳)コルーチンたちは一方でvalue stackを持ち、すべてのコルーチンのアクティブなコールスタックをstoreします。


== 以下、翻訳未終了 ==


テザリング状態のUbuntuでaptできない原因と対処

結論としては、『テザリングで使うIPS+Proxy接続(また仮想マシンのNATなど)』の組み合わせにより、Ubuntu環境でaptが使えなくなる場合があるようです。








対処方法としては、Proxy接続を通さないで直接インターネット接続させることで、aptを使えるようになりました。
Ubuntu,aptの設定変更で済ませたかったところですが、仕方ありません。

現象


apt-get update が、以下のエラーメッセージを表示して失敗します。
===
InRelease: クリアサインされたファイルが有効ではなく、'NODATA' を得ました (認証にネットワークが必要?)
===

英語だと以下になります。
===
Get:1 http://extras.ubuntu.com trusty InRelease                               
100% [1 InRelease gpgv 3650 B]Splitting up /var/lib/apt/lists/partial/extras.ubuIgn http://extras.ubuntu.com trusty InReleaseta and signature failed
E: GPG error: http://extras.ubuntu.com trusty InRelease: Clearsigned file isn't valid, got 'NODATA' (does the network require authentication?)

===

fix-missingも何も効かないので焦りました。

対処


ppaリポジトリを追加しようとしてこの現象が起こったので、最初はaptの設定に原因があるかとも考えました。

http://askubuntu.com/questions/474549/got-nodata-issue-nodata-does-the-network-require-authentication

最初は、このページに書かれている、『/var/lib/apt/lists以下を再作成』などの設定作業による回避を模索していました。
過去の経験からaptが壊れた場合、aptの設定(/var/apt/source.list.d/ 以下など)をクリアすれば解決することを知っていたからですが、余計な寄り道になってしまいました。

=====
Your mobile broadband provider is running a faulty transparent proxy. The URL http://extras.ubuntu.com/ubuntu/dists/trusty/InRelease should return "404 Not Found". Instead it returns "200 OK" but sends 404 content. Since the URL is "OK",
(以下略)
=====

とのことでしたので、仮想マシン(VirtualBox)のNAT経由ではなく、ゲストへのUSBブリッジ接続により、直接ゲストからUSBテザリングしました。
これで、aptが使えるようになったことを確認しました。

また、これも経験的にそうだろうと思ってアタリだったのですが、Ubuntuゲスト側でNAT接続を切らないとネットに繋がらないようです。
また、繋がらない場合にpingを打つと(ex. ping www.google.com など)繋がりはじめる場合がある気がします。こちらは検証していませんが。

Gtk3でダイアログにWidgetを追加する



Gtk3で書いているアプリケーションで、カスタムダイアログが必要となった際の記録です。

最初にGtkDialogを使ったときに、 ダイアログ内に追加したWidgetが表示されなかったため、少し時間を取られたので記事にしています。

SpinButtonを追加したGtkDialog

Gtk3でダイアログボックスを使用するGtkDialogにWidgetを追加する場合、まずダイアログから、ダイアログ内のコンテナウィジェットへのポインタを取り出します。
GtkWidget *    gtk_dialog_get_content_area ()
によって取得したコンテナにGtkBoxなどのWidgetを追加していくことで、ダイアログをカスタマイズします。

また、
GtkWidget *    gtk_dialog_get_action_area ()
もありますが、こちらはdeprecated (廃止予定に非推奨)となっています。

そして、ダイアログ内へ追加したWidgetを、gtk_dialog_run() したときに表示するには、
gtk_widget_show_all(dialog);
を実行しておきます。
GtkWindowに対して使うことが多いと思いますが、dialogにも有効でした。

ソースコード

作成中のプログラムからそのままコピーしてきたコードなので、オリジナル関数(et_error())などが入っていますが上手くやってください。



 
GtkWidget *dialog;
 
GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
 
dialog = gtk_dialog_new_with_buttons ("New Document",
 
 
 
NULL,
 
 
 
flags,
 
 
 
"_OK",
 
 
 
GTK_RESPONSE_ACCEPT,
 
 
 
"_Cancel",
 
 
 
GTK_RESPONSE_REJECT,
 
 
 
NULL);

 
GtkWidget *hbox_w = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
 
GtkWidget *hbox_h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
 
GtkWidget *spin_w = gtk_spin_button_new_with_range(0, 20000, 100);
 
GtkWidget *spin_h = gtk_spin_button_new_with_range(0, 20000, 100);
 
GtkWidget *label_w = gtk_label_new_with_mnemonic("width ");
 
GtkWidget *label_h = gtk_label_new_with_mnemonic("height");
 
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_w), 1200);
 
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_h), 600);
 
GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 
gtk_box_pack_start(GTK_BOX(hbox_w), label_w, true, true, 0);
 
gtk_box_pack_start(GTK_BOX(hbox_h), label_h, true, true, 1);
 
gtk_box_pack_end(GTK_BOX(hbox_w), spin_w, true, true, 0);
 
gtk_box_pack_end(GTK_BOX(hbox_h), spin_h, true, true, 1);
 
gtk_box_pack_start(GTK_BOX(content), hbox_w, true, true, 0);
 
gtk_box_pack_start(GTK_BOX(content), hbox_h, true, true, 1);
 
gtk_widget_show_all(dialog);

 
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
 
switch (result)
 
{
 
 
case GTK_RESPONSE_ACCEPT:
 
 
 
{
 
 
 
 
vg->rect.w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_w));
 
 
 
 
vg->rect.h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_h));
 
 
 
 
et_debug("size:%f,%f,%f,%f\n",
 
 
 
 
 
vg->rect.x, vg->rect.y, vg->rect.w, vg->rect.h);
 
 
 
}
 
 
 
break;
 
 
default:
 
 
 
et_debug("Cancel\n");
 
 
 
break;
 
}
 
gtk_widget_destroy (dialog);

 
return false;


以上です。

JavaScript enginたちのコンパイル(2015)

2015年版の情報ですが、SpiderMonkeyとv8のビルド手順を確認した際のメモです。
勿体無いので一応公開しておきます。


 SpiderMonkeyの取得とビルド



公式の手順にあるビルドシステムのブートストラップを実行します。
wget -O bootstrap.py https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py && python bootstrap.py

下記の返答を 要求されたので「1」を答えました。
その後で依存パッケージ導入のために管理者権限を要求されます。
Please choose the version of Firefox you want to build:
1. Firefox for Desktop
2. Firefox for Android
Your choice:

SpiderMonkeyの環境構築はV8と違って、ブートストラップの最後にgitリポジトリの案内まで表示してくれる親切さです。(公式ダウンロードリポジトリ案内を読むまでもなかった。)


Gitリポジトリ取得は、履歴の長さ故かとても時間がかかります。
git clone https://git.mozilla.org/integration/gecko-dev.git

わたしは一晩放置しました。
最新ソースだけ欲しい場合は、単に最新ソースツリーを指定して取得したほうが良さそうです。
git clone --depth 1 https://github.com/mozilla/gecko-dev.git


ビルドは、思った以上に早く終わりました。8コア指定したからかもしれません。
autoconfは念の為にバージョンを確認しましょう。 ブートストラップが最適バージョンを配置してくれているようですが。
autoconf --version
autoconf
mkdir build_OPT.OBJ
cd build_OPT.OBJ
../configure make -j8

ビルド成功を確認。
dist/bin/js --help

今回は、インストールはしませんでした。




JavaScript v8の取得



V8 JavaScriptエンジンの取得

公式のGit利用案内ページにはいろいろ書いてありますが、結局Githubの公式ミラーからいつものcloneで取得できます。
git clone https://github.com/v8/v8-git-mirror.git


glient <- V8ビルドシステム(makefile生成)


リンクを辿ってChromiumビルドツールのページ http://dev.chromium.org/developers/how-tos/install-depot-tools  にて
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git


export PATH=`pwd`/depot_tools:"$PATH"

要はパスを通しておけということのようです。

gclient --help
が通ることを確認。

公式手順には記載されていませんがgclientはconfigが必要。
gclient config https://chromium.googlesource.com/v8/v8
gclient sync

しばらく反応がなかったり、反応があったと思えば時間をカウントするだけだったり、進行しているのか疑ってしまう動作のあとでProgressがちゃんと進行します。


SVNは、checkoutの途中でエラーで止まりました。

TIGORA(ティゴラ)のトレッキング シューズ

 TIGORA(ティゴラ)の トレッキング シューズを買いました。 メインの靴がアシックスウォーキングで街歩き用なのですが、これまではこれで高尾山などの軽い山も登っていました。 今回、靴底があまりに摩耗したこともあってアシックスウォーキングを買い替えたのですが、ついでに消耗が激...