環境: Clojure 1.2.0
お約束ですが、やっちゃいけません。
実際、Paul Graham がリードマクロの利点のひとつとしてあげている
read が再帰的に処理される、という例
(http://www.komaba.utmc.or.jp/~flatline/onlispjhtml/readMacros.html)
user=> ''a (quote a)
は、自分でマクロ展開すれば実現できますし、
user=> (defmacro q [form] (list 'quote (macroexpand form))) #'user/q user=> (q (q a)) (quote a)
ほんとうにリード時の処理が必要!ってなる場面は少ないような気がします。
ユーザー定義リードマクロが好ましくないとする理由はいろいろあるようですが、
ひとつに名前空間のサポートがない、ということがあります。
(http://clojure-log.n01se.net/date/2008-11-06.html)
ということで名前空間つきリードマクロ(namespaced reader macro)を
試みてみました。
これって cl-annot な方向なのかも…
;;; ;;; (ns rdm ;;; (:require clojure.contrib.def)) ;;; (defn dispatch-reader-macro "see: [http://briancarper.net/blog/449/clojure-reader-macros http://rd.clojure-users.org/entry/view/56001]" [ch fun] (let [dm (.get (doto (.getDeclaredField clojure.lang.LispReader "dispatchMacros") (.setAccessible true)) nil)] (aset dm (int ch) fun))) (defn true-sym "a helper function to treat namespace aliases." [ns sym] (symbol (str (if-let [t-ns ((ns-aliases *ns*) ns)] t-ns ns)) (str sym))) (defn find-true-var "a helper function to treat var renamings." [sym] (if-let [ns (.getNamespace sym)] (find-var (true-sym (symbol ns) (symbol (.getName sym)))) ((ns-map *ns*) sym))) (defn get-ns-reader-macro-fun "a helper function to get a var of the readermacro." [form] (cond (symbol? form) (find-true-var form) (list? form) (recur (first form)) :else (throw (Exception. (str "read error: " form))))) (defn get-ns-reader-macro-args "a helper function to get args of the readermacro." [form] (cond (symbol? form) nil (list? form) (rest form) :else (throw (Exception. (str "read error: " form))))) (defn read-form "a helper function to read a form following the readermacro." [rdr] (clojure.lang.LispReader/read rdr true nil true)) (defn dispatch-ns-reader-macro "a helper function to apply a var of the readermacro to args of it." [rdr letter-sharp] (let [rmsym (read-form rdr) rmfun (get-ns-reader-macro-fun rmsym) rmargs (get-ns-reader-macro-args rmsym)] (if rmfun (apply rmfun rdr rmsym rmargs) (throw (Exception. (str "reader macro not defined: " rmsym)))))) (dispatch-reader-macro \# dispatch-ns-reader-macro) (defmacro defreadermacro "defines the readermacro. usage: ##name form ##(name & args) form name can be ns/name." {:arglists '([name args & body])} [name & macro-args] (let [[mname# margs#] (clojure.contrib.def/name-with-attributes name macro-args)] `(defn ~mname# ~margs#))) ;;; ;;; client ;;; (defreadermacro uppercase-string "makes string uppercase." [rdr sym & args] (let [c (.read rdr)] (if (= c (int \")) (.toUpperCase (.invoke (clojure.lang.LispReader$StringReader.) rdr c)) (throw (Exception. (str "read error: " (char c) args)))))) ;;user> ##uppercase-string"a" ;;"A" (defreadermacro my-quote "simulates quote." [rdr sym & args] (let [form (read-form rdr)] (list 'quote form))) ;;user> ##my-quote ##my-quote a ;;(quote a) (defreadermacro my-wrap "something a little more complicated." [rdr sym & args] (let [form (read-form rdr) wrapper (first args)] (if (list? wrapper) `(~@wrapper ~form) `(~wrapper ~form)))) ;;user> ##(my-wrap (let [x 1])) (+ x 1) ;;2 ;;user=> (in-ns 'user2) ;;#<Namespace user2> ;;user2=> (clojure.core/refer-clojure) ;;nil ;;user2=> ##(user/my-wrap (let [x 1])) (+ x 1) ;;2 ;;user2=> (alias 'user1 'user) ;;nil ;;user2=> ##(user1/my-wrap (let [x 1])) (+ x 1) ;;2 ;;user2=> (refer 'user :rename {'my-wrap 'wrap}) ;;nil ;;user2=> ##(wrap (let [x 1])) (+ x 1) ;;2
read-form 以外にもいろんな read-* を提供するようにすれば
(で binding で rdr 隠すとかして呼びやすくすれば)
そんなにわかりにくくない形でユーザー定義リードマクロを導入できるのかも。
ちなみに最初はリードマクロの実体にマルチメソッドを使って
varを作らないようにしていたのですが
varをつくらないと refer, rename の解決ができない
(というかそもそも refer の対象にすらならない)
ということが判明しあえなく却下。
refer ってシンボル間の関係じゃなくて var へのマッピングを追加するんだ
ということをあらためて思い知らされました。
前回の続きです。
stateモナド変換子を使ってもうちょっとすっきりさせてみました。
(use '[clojure.contrib.monads]) (defn pass ([] (pass nil)) ([v] (fn [s] (with-monad cont-m (m-result [v s]))))) (defn fail [] (fn [s] (if s (s) ((pass) s)))) (defn choose-next [cc s] (if s (s) (cc [nil nil]))) (defn choose ([choices] (fn [s] (with-monad cont-m (call-cc (fn [cc] (choose cc choices s)))))) ([cc choices s] (if (empty? choices) (choose-next cc s) (cc [(first choices) #(choose cc (next choices) s)])))) (defn run-choose [m] (run-cont (m (fn [] (with-monad cont-m (m-result [nil nil])))))) (defn test-state-t [] (run-choose (domonad (state-t cont-m) [v1 (choose [1 2 3]) v2 (choose [7 8 9]) v (if (or (some nil? [v1 v2]) (odd? (* v1 v2))) (fail) (pass [v1 v2]))] v))) ;; example: ;; user=> (test-state-t) ;; [[1 8] #<user$choose$fn__1554 user$choose$fn__1554@55104da7>] ;; user=> (run-cont ((fnext *1))) ;; [[2 7] #<user$choose$fn__1554 user$choose$fn__1554@1c6745b9>] ;; user=> (run-cont ((fnext *1))) ;; [[2 8] #<user$choose$fn__1554 user$choose$fn__1554@430a14ad>] ;; user=> (run-cont ((fnext *1))) ;; [[2 9] #<user$choose$fn__1554 user$choose$fn__1554@67afe460>] ;; user=> (run-cont ((fnext *1))) ;; [[3 8] #<user$choose$fn__1554 user$choose$fn__1554@28fe53cf>] ;; user=> (run-cont ((fnext *1))) ;; [nil nil]
このへんになると型チェック機構がないとかなり厳しい感じがします…。
なんとかならないかなー。
ただ、いざ問題が起きたときにコンパイル時エラーと実行時エラーとどっちが問題を調べやすいか、というと、
場合によっては実行時エラーのほうが気が楽なこともあるかもしれないです。
コンパイル時の動作にトレース入れたりとかは実行時に比べると敷居が高い感じがするので…。
環境:
Clojure 1.2.0
非決定性を扱うにはいろいろな選択肢がありますが、そのひとつとして継続での実装があると思います。
clojureで継続を使うには On Lisp 風の継続渡しマクロを使った実装などが考えられますが、
今回は継続モナドの使い方のサンプルもかねて継続モナドで choose / fail を書いてみました。
連続して失敗する選択肢が多いとスタックが溢れるので実用にはなりません。
工夫したらなんとかなるんだろうか…。
陽に restart を返してたりとかあんまり継続を使うメリットいかせてないし…。
新しくモナド書いてうまく隠せばいけそうな気もするけど今の力ではとても大掛かりな感じがするし…。
そもそもモナドを使うのであれば素直にシーケンスとかを使ったほうが
m-zeroとか使ってもっとシンプルに書けるし対角化シーケンスとかの応用もできるしよさげです多分。
(use '[clojure.contrib.monads]) (defn choose "Chooses one from specified choices. Returns a function to try the next choice for each choices." ([choices] (with-monad cont-m (call-cc (fn [cc] (m-result (choose cc choices)))))) ([cc choices] (if (empty? choices) [nil nil] [(first choices) #(cc (choose cc (next choices)))]))) (defn pass "Falls through to pass the choice." ([] (pass nil)) ([v] (with-monad cont-m (m-result v)))) (defn merge-restarts "Calls restart to go back to the next choice." [restarts] #(if-let [restart (some identity (reverse restarts))] (restart) (pass true))) (defn test-choose "Gets choices from the function 'choose' and returns a passed result. The returned result contains a function to try the next choice." [] (run-cont (domonad cont-m [[v1 restart1 :as vr1] (choose [1 2 3]) [v2 restart2 :as vr2] (choose [5 6 7]) :let [v (if (not-any? nil? [v1 v2]) (* v1 v2)) fail (merge-restarts [restart1 restart2])] ;; sample test eos (if (or (nil? v) (odd? v)) (fail) (pass))] ;; passed result [[v1 v2 v] (if-not eos fail)]))) (defn do-test-choose [] "Collects results from the function 'test-choose' and returns lazy-seq of them." (letfn [(step [[v restart]] (lazy-seq (if restart (cons v (step (run-cont (restart)))) nil)))] (step (test-choose)))) ;;; get lazy-seq (do-test-choose) ;; user=> (do-test-choose) ;; ([1 6 6] [2 5 10] [2 6 12] [2 7 14] [3 6 18]) ;;; or step manually in repl (test-choose) ;; user=> (test-choose) ;; [[1 6 6] #<user$choose$fn__917 user$choose$fn__917@6155035a>] (run-cont ((fnext *1))) ;; user=> (run-cont ((fnext *1))) ;; [[2 5 10] #<user$choose$fn__917 user$choose$fn__917@72a60191>] ;; user=> (run-cont ((fnext *1))) ;; [[2 6 12] #<user$choose$fn__917 user$choose$fn__917@41697023>] ;; ...
環境:
GNU Emacs 23.2.1 (i386-mingw-nt6.1.7601)
emacs上での javascript 編集環境といえば js2-mode なわけですが、
整形コマンドが見当たりません。
というわけで以下で。
といっても
javaプロパティファイルを変換するconf-javaprop-mode拡張
とおなじことをやるだけです。
なので、タイトルに偽りがあって emacs から 単に整形コマンドを呼ぶだけです。
おなじことやるんならマクロかけという話もあるんですが手抜きです。情けない。
- jsbeautifierを入手。
https://github.com/einars/js-beautify - jsbeautifier.pyを呼ぶスクリプトをつくる。
自分のemacsは、
(setq shell-file-name “sh”) な環境なので、
な環境なので、#!/bin/sh python c:/js-beautify/jsbeautifier.py "$@"
とか。
- js-beautify を emacs から呼び出す本体をつくる。
;;; js-beautify.el (defcustom js-beautify-jsbeautifier-path "jsbeautifier" "js-beautify jsbeautifier command path." :type 'string :group 'js-beautify) (defcustom js-beautify-jsbeautifier-option "-i" "js-beautify jsbeautifier command option." :type 'string :group 'js-beautify) (defvar js-beautify-mode-map (let ((m (make-sparse-keymap))) (define-key m "\C-cc" 'js-beautify-convert-buffer) (define-key m "\C-cr" 'js-beautify-convert-region) m)) (defun js-beautify-convert-region-to-1 (rev start end buff) (let ((command (concat js-beautify-jsbeautifier-path " " js-beautify-jsbeautifier-option))) (save-excursion (shell-command-on-region start end command buff nil "*js-beautify-error*" t)))) (defun js-beautify-convert-region-to (rev start end buff) (let ((coding-system-for-read 'japanese-shift-jis-dos) (coding-system-for-write 'japanese-shift-jis-unix)) (js-beautify-convert-region-to-1 rev start end buff))) (defun js-beautify-convert-region-to-buffer (rev start end) (interactive "P\nr") (js-beautify-convert-region-to rev start end (get-buffer-create "*js-beautify*"))) (defun js-beautify-convert-buffer-to-buffer (rev) (interactive "P") (js-beautify-convert-region-to-buffer rev (point-min) (point-max))) (defun js-beautify-convert-region (rev start end) (interactive "P\nr") (js-beautify-convert-region-to rev start end t)) (defun js-beautify-convert-buffer (rev) (interactive "P") (js-beautify-convert-region rev (point-min) (point-max))) (add-hook 'js2-mode-hook (lambda () (let ((m (copy-keymap js-beautify-mode-map))) (set-keymap-parent m js2-mode-map) (use-local-map m)))) (provide 'js-beautify)
一応revが指定できるみたいに書いてありますが rev はなんの役割も果たしてません。
前回のjprop.elをそのまま使って手抜きしてるだけです。
自分の環境は (setq default-process-coding-system ‘(utf-8-dos . japanese-shift-jis-unix ))
というちょっと変則的な環境なのでエンコーディングを明示してたりします。
あと、js2-modeを前提としてたりします。
とここまで書いて、こんなおおげさなことをしなくても
M-| で ふつうに shell-command-on-region で jsbeautifier を呼べばいいんだよなー
と思ったのは内緒です。windows はパスとかいろいろ面倒なんです。
環境:GNU Emacs 23.2.1 (i386-mingw-nt6.1.7601)
たいしたもんじゃありませんが
eclipseを立ち上げるまでもないちょっとした確認をしたいときに…
(require ‘jprop) すれば、 conf-javaprop-mode に
\C-cc:バッファ全体を日本語へ変換した結果を*jprop*バッファに出力する
\C-cr:選択範囲を日本語への変換結果を*jprop*バッファに出力する
などが追加されます。
プレフィクスつければ逆になります。
あとは jprop-native2ascii-path を変更するなり
jprop-mode-map を変更するなり
適当にアドバイズするなり
全面的に書き換えるなり。
;;; jprop.el (defcustom jprop-native2ascii-path (let ((command "native2ascii") (jdk-path (getenv "JAVA_HOME"))) (if jdk-path (expand-file-name command (expand-file-name "bin" jdk-path)) command)) "jdk native2ascii command path." :type 'string :group 'jprop) (defvar jprop-mode-map (let ((m (make-sparse-keymap))) (define-key m "\C-cc" 'jprop-convert-buffer-to-buffer) (define-key m "\C-cr" 'jprop-convert-region-to-buffer) m)) (defun jprop-convert-region-to-1 (rev start end buff) (let ((command (concat jprop-native2ascii-path (if rev "" " -reverse")))) (save-excursion (shell-command-on-region start end command buff nil "*jprop-error*" t)))) (defun jprop-convert-region-to (rev start end buff) (let ((coding-system-for-read 'japanese-shift-jis-dos) (coding-system-for-write 'japanese-shift-jis-unix)) (jprop-convert-region-to-1 rev start end buff))) (defun jprop-convert-region-to-buffer (rev start end) (interactive "P\nr") (jprop-convert-region-to rev start end (get-buffer-create "*jprop*"))) (defun jprop-convert-buffer-to-buffer (rev) (interactive "P") (jprop-convert-region-to-buffer rev (point-min) (point-max))) (defun jprop-convert-region (rev start end) (interactive "P\nr") (jprop-convert-region-to rev start end t)) (defun jprop-convert-buffer (rev) (interactive "P") (jprop-convert-region rev (point-min) (point-max))) (add-hook 'conf-javaprop-mode-hook (lambda () (let ((m (copy-keymap jprop-mode-map))) (set-keymap-parent m conf-javaprop-mode-map) (use-local-map m)))) (provide 'jprop)
処理がどう進んでいくのかを分析するのに、コールグラフやコミュニケーション
図、シーケンス図、といったものをよく使うと思います。
しかし、これらの図は、基本的には呼び出しの関係を表すものであるため、特に
オブジェクトをつくってわたして、といったことを行うような処理では、オブジェ
クト間の関係を表すのにはがゆい思いをしたりします。
そこで、試験的にオブジェクトパッセージ図なるものを書いてみました。
以下のようなことがらを表すのを目標にしています:
- オブジェクトaからオブジェクトbのm1を呼び出す
- オブジェクトaからオブジェクトbのm1を呼び出してオブジェクトcを渡す
- オブジェクトaからオブジェクトbのm1を呼び出してオブジェクトcを生成する
試みとして、Android の Service の使用方法をオブジェクトパッセージ図にし
てみました。
(この辺の内容のつもりですが間違ってるかもしれません。→http://developer.android.com/guide/developing/tools/aidl.html)
あまりよくできてないところもあるので、とりあえずVer.0.1.0として公開してみ
ます。
図の要素の説明等は今は雰囲気だけでいずれまた整理したいと思います。
ソース(graphviz)
digraph sampleservice { graph [nodesep="1.2", ordering="in"]; node [fontname="tahoma", fontsize="8"]; edge [fontname="tahoma", fontsize="8", arrowsize="0.5"]; AIDL [label="<<aidl>>\nIXxxService"]; AIDL -> IXxxService_Stub [label="<<generate>>"]; AIDL -> IXxxService [label="<<generate>>"]; Binder; IXxxService_Stub [shape=Mrecord,label="<x>IXxxService.Stub|{<m1>doXxx|<m2>asInterface}"]; Binder -> IXxxService_Stub [dir=back, arrowtail=empty, arrowsize="1.5"]; XxxService_Stub [shape=Mrecord,label="<x>\<\<impl\>\>\nXxxService.Stub|{<m1>\<\<override\>\>\ndoXxx}"]; IXxxService_Stub -> XxxService_Stub [dir=back, arrowtail=empty, arrowsize="1.5"]; Service; XxxService [shape=Mrecord,label="<x>XxxService|{<m1>\<\<\override\>\>\nonBind}"]; Service -> XxxService [dir=back, arrowtail=empty, arrowsize="1.5"]; XxxServiceProxy [shape=Mrecord,label="<x>XxxServiceProxy(internal)|{<m1>\<\<\override\>\>\ndoXxx}"]; IXxxService_Stub:m2 -> XxxServiceProxy [label="(8)\n<<lead to>>", style="dashed"]; XxxService:m1 -> XxxService_Stub:x [label="(5)\n<<lead to>>", style="dashed"]; Activity; XxxActivity [shape=Mrecord, label="<x>XxxActivity|{<c0>ctor()|<m1>\<\<\override\>\>\nonCreate|<m2>\<\<\override\>\>\nonDestroy|<m5>onKey|<m3>\<\<inherit\>\>\nbindService|<m4>\<\<inherit\>\>\nunbindService}"]; Activity -> XxxActivity [dir=back, arrowtail=empty, arrowsize="1.5"]; ServiceConnection; XxxServiceConnection [shape=Mrecord, label="<x>XxxServiceConnection|{<m1>\<\<override\>\>\nonServiceConnected|<m2>\<\<override\>\>\nonServiceDisconnected}"]; ServiceConnection -> XxxServiceConnection [dir=back, arrowtail=empty, arrowsize="1.5"]; XxxActivity:c0 -> XxxServiceConnection [label="(1)\n<<create>>"]; Intent1 [label="Intent\n{ISampleService.class}"]; XxxActivity:m1 -> Intent1 [label="(2)\n<<create>>"]; XxxActivity:m1 -> XxxActivity:m3 [taillabel="(3)", labeldistance="6" labelangle="-20"]; XxxActivity:m3 -> Android [label="(4)"]; Intent1 -> XxxActivity:m3 [label="(3)\n<<passed to>>", style=dotted]; XxxServiceConnection -> XxxActivity:m3 [label="(3)\n<<passed to>>", style=dotted]; XxxActivity:m5 -> XxxServiceProxy:m1 [label="(10)"]; XxxServiceConnection -> XxxActivity:m4 [label="(11)\n<<passed to>>", style=dotted]; Android -> XxxService:m1 [label="(5)"]; Android -> XxxServiceConnection:m1 [label="(7)"]; XxxService_Stub -> XxxServiceConnection:m1 [label="(7)\n<<passed to>>", style=dotted]; XxxServiceConnection:m1 -> IXxxService_Stub:m2 [label="(8)"]; XxxActivity:m2 -> XxxActivity:m4 [taillabel="(11)", labeldistance="6", labelangle="-20"]; XxxActivity:m4 -> Android [label="(12)"]; Android -> XxxServiceConnection:m2 [label="(13)"]; }
svnでどういう変更が入ったのか追いかけるのに不便だったのでつくってみた。
どこかにありそうなものだけど…
準備:
- JDKをインストールする
- 環境変数 JAVA_HOME をJDKのインストールディレクトリに設定(オプショナル)
設定:
- 以下を jprop.sct という名前で保存する:
<scriptlet> <implements type="Automation" id="dispatcher"> <property name="PluginEvent"><get/></property> <property name="PluginDescription"><get/></property> <property name="PluginFileFilters"><get/></property> <property name="PluginIsAutomatic"><get/></property> <method name="UnpackFile"/> <method name="PackFile"/> </implements> <script language="VBS"> Option Explicit Const defaultJavaHome = "C:\jdk" '←ここは自分の環境にに応じて変更 Function get_PluginEvent() get_PluginEvent = "FILE_PACK_UNPACK" End Function Function get_PluginDescription() get_PluginDescription = "Convert Java Propertis Files" End Function Function get_PluginFileFilters() get_PluginFileFilters = "\.properties$" End Function Function get_PluginIsAutomatic() get_PluginIsAutomatic = True End Function Function UnpackFile(fileSrc, fileDst, pbChanged, pSubcode) Dim WshShell Dim javaHome Dim objExec Set WshShell = CreateObject("WScript.Shell") javaHome = WshShell.Environment("Process")("JAVA_HOME") If javaHome = "" Then javaHome = defaultJavaHome End If Set objExec = WshShell.Exec(javaHome + "\bin\native2ascii -reverse """ + fileSrc + """ """ + fileDst + """" ) Do While objExec.Status = 0 'WScript.Sleep(100) Loop pbChanged = True pSubcode = 0 UnpackFile = True End Function Function PackFile(fileSrc, fileDst, pbChanged, pSubcode) Dim WshShell Dim javaHome Dim objExec Set WshShell = CreateObject("WScript.Shell") javaHome = WshShell.Environment("Process")("JAVA_HOME") If javaHome = "" Then javaHome = defaultJavaHome End If Set objExec = WshShell.Exec(javaHome + "\bin\native2ascii """ + fileSrc + """ """ + fileDst + """" ) Do While objExec.Status = 0 'WScript.Sleep(100) Loop pbChanged = True pSubcode = 0 PackFile = True End Function </script> </scriptlet>
- jprop.sctを WinMerge をインストールしたディレクトリの下の
MergePlugins ディレクトリに置く
使い方:
- WinMergeで比較する
- [プラグイン]-[展開プラグインで開く]を選び、
[展開プラグインの選択]ダイアログの[ファイル展開プラグイン]で
jprop.sctを選ぶ
WScriptオブジェクトにアクセスできないので
ポーリング中Sleepしてないのが難点ですが、
最近のマルチコアなPCならさほど影響はないでしょうきっと…
もしくは、ExecじゃなくてRunとか使えばいいのかもしれませんが…
環境:
- Windows
- cygwin 1.7.8
- Android SDK r10
- Android NDK r5b
前提:
- 環境変数NDK_ROOTを設定済であること。
- Android SDKにパスを通してあること。
- ndk-buildでビルド済でプロジェクトフォルダのlibsにgdbserverがあること。
できれば-gでビルド済であること。 - apkがdebuggableであること。
- 端末/エミュレータが起動済であること。
- apkが端末/エミュレータにインストール済で、プロセスが起動済であること。
- 端末/エミュレータのシェルにrootで入れること。(Android 2.1以前の場合)
A. Android 2.2 以上の場合
- cygwinのシェルでプロジェクトのルートフォルダ上で$NDK_ROOT/ndk-gdbを実行。
- gdbが起動するのであとは煮るなり焼くなり。
B. Android 2.1 以前の場合
- 以下のスクリプトをプロジェクトのルートフォルダにmy-ndk-gdbの名前で保存。
#!/bin/sh ANDROID_NDK_ROOT=$NDK_ROOT . $NDK_ROOT/build/core/ndk-common.sh force_32bit_binaries find_program ADB_CMD adb ADB_FLAGS= AWK_CMD=awk DEBUG_PORT=5039 # Delay in seconds between launching the activity and attaching gdbserver on it. # This is needed because there is no way to know when the activity has really # started, and sometimes this takes a few seconds. DELAY=2 PARAMETERS= OPTION_HELP=no OPTION_PROJECT= OPTION_FORCE=no OPTION_ADB= OPTION_EXEC= OPTION_START=no OPTION_LAUNCH= OPTION_LAUNCH_LIST=no OPTION_DELAY= check_parameter () { if [ -z "$2" ]; then echo "ERROR: Missing parameter after option '$1'" exit 1 fi } check_adb_flags () { if [ -n "$ADB_FLAGS" ] ; then echo "ERROR: Only one of -e, -d or -s can be used at the same time!" exit 1 fi } get_build_var () { if [ -z "$GNUMAKE" ] ; then GNUMAKE=make fi $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 } get_build_var_for_abi () { if [ -z "$GNUMAKE" ] ; then GNUMAKE=make fi $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 APP_ABI=$2 } # Used to run an awk script on the manifest run_awk_manifest_script () { $AWK_CMD -f $AWK_SCRIPTS/$1 $PROJECT/$MANIFEST } if [ "$HOST_OS" = "cygwin" ] ; then # Return native path representation from cygwin one # $1: a cygwin-compatible path (e.g. /cygdrive/c/some/thing) # Return: path in host windows representation, e.g. C:/some/thing # # We use mixed mode (i.e. / as the directory separator) because # all the tools we use recognize it properly, and it avoids lots # of escaping nonsense associated with "\" # native_path () { cygpath -m $1 } else # HOST_OS != windows native_path () { echo "$1" } fi # HOST_OS != windows VERBOSE=no while [ -n "$1" ]; do opt="$1" optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` case "$opt" in --help|-h|-\?) OPTION_HELP=yes ;; --verbose) VERBOSE=yes ;; -s) check_parameter $1 $2 check_adb_flags ADB_FLAGS=" -s $2" shift ;; -s*) check_adb_flags optarg=`expr -- "$opt" : '-s\(.*\)'` ADB_FLAGS=" -s $optarg" ;; -p) check_parameter $1 $2 OPTION_PROJECT="$2" shift ;; -p*) optarg=`expr -- "$opt" : '-p\(.*\)'` OPTION_PROJECT="$optarg" ;; --exec=*) OPTION_EXEC="$optarg" ;; -x) check_parameter $1 $2 OPTION_EXEC="$2" shift ;; -x*) optarg=`expr -- "$opt" : '-x\(.*\)'` OPTION_EXEC="$optarg" ;; -e) check_adb_flags ADB_FLAGS=" -e" ;; -d) check_adb_flags ADB_FLAGS=" -d" ;; --adb=*) # specify ADB command OPTION_ADB="$optarg" ;; --awk=*) AWK_CMD="$optarg" ;; --project=*) OPTION_PROJECT="$optarg" ;; --port=*) DEBUG_PORT="$optarg" ;; --force) OPTION_FORCE="yes" ;; --launch-list) OPTION_LAUNCH_LIST="yes" ;; --launch=*) OPTION_LAUNCH="$optarg" ;; --start) OPTION_START=yes ;; --delay=*) OPTION_DELAY="$optarg" ;; -*) # unknown options echo "ERROR: Unknown option '$opt', use --help for list of valid ones." exit 1 ;; *) # Simply record parameter if [ -z "$PARAMETERS" ] ; then PARAMETERS="$opt" else PARAMETERS="$PARAMETERS $opt" fi ;; esac shift done if [ "$OPTION_HELP" = "yes" ] ; then echo "Usage: $PROGNAME [options]" echo "" echo "Setup a gdb debugging session for your Android NDK application." echo "Read $$NDK/docs/NDK-GDB.TXT for complete usage instructions." echo "" echo "Valid options:" echo "" echo " --help|-h|-? Print this help" echo " --verbose Enable verbose mode" echo " --force Kill existing debug session if it exists" echo " --start Launch application instead of attaching to existing one" echo " --launch= Same as --start, but specify activity name (see below)" echo " --launch-list List all launchable activity names from manifest" echo " --delay= Delay in seconds between activity start and gdbserver attach." echo " --project= Specify application project path" echo " -p Same as --project=" echo " --port= Use tcp:localhost: to communicate with gdbserver [$DEBUG_PORT]" echo " --exec= Execute gdb initialization commands in after connection" echo " -x Same as --exec=" echo " --adb= Use specific adb command [$ADB_CMD]" echo " --awk= Use specific awk command [$AWK_CMD]" echo " -e Connect to single emulator instance" echo " -d Connect to single target device" echo " -s Connect to specific emulator or device" echo "" exit 0 fi log "Android NDK installation path: $ANDROID_NDK_ROOT" if [ -n "$OPTION_EXEC" ] ; then if [ ! -f "$OPTION_EXEC" ]; then echo "ERROR: Invalid initialization file: $OPTION_EXEC" exit 1 fi fi if [ -n "$OPTION_DELAY" ] ; then DELAY="$OPTION_DELAY" fi # Check ADB tool version if [ -n "$OPTION_ADB" ] ; then ADB_CMD="$OPTION_ADB" log "Using specific adb command: $ADB_CMD" else if [ -z "$ADB_CMD" ] ; then echo "ERROR: The 'adb' tool is not in your path." echo " You can change your PATH variable, or use" echo " --adb= to point to a valid one." exit 1 fi log "Using default adb command: $ADB_CMD" fi ADB_VERSION=`$ADB_CMD version` if [ $? != 0 ] ; then echo "ERROR: Could not run ADB with: $ADB_CMD" exit 1 fi log "ADB version found: $ADB_VERSION" ADB_CMD="${ADB_CMD}${ADB_FLAGS}" log "Using final ADB command: '$ADB_CMD'" adb_shell () { # Run an adb shell command and return its output. # # We need to filter weird control characters like \r that are # included in the output. # $ADB_CMD shell $@ | sed -e 's![[:cntrl:]]!!g' } # Check the awk tool AWK_SCRIPTS=$ANDROID_NDK_ROOT/build/awk AWK_TEST=`$AWK_CMD -f $AWK_SCRIPTS/check-awk.awk` if [ $? != 0 ] ; then echo "ERROR: Could not run '$AWK_CMD' command. Do you have it installed properly?" exit 1 fi if [ "$AWK_TEST" != "Pass" ] ; then echo "ERROR: Your version of 'awk' is obsolete. Please use --awk= to point to Nawk or Gawk!" exit 1 fi # Name of the manifest file MANIFEST=AndroidManifest.xml # Find the root of the application project. if [ -n "$OPTION_PROJECT" ] ; then PROJECT=$OPTION_PROJECT log "Using specified project path: $PROJECT" if [ ! -d "$PROJECT" ] ; then echo "ERROR: Your --project option does not point to a directory!" exit 1 fi if [ ! -f "$PROJECT/$MANIFEST" ] ; then echo "ERROR: Your --project does not point to an Android project path!" echo " It is missing a $MANIFEST file." exit 1 fi else # Assume we are in the project directory if [ -f "$MANIFEST" ] ; then PROJECT=. else PROJECT= CURDIR=`pwd` while [ "$CURDIR" != "/" ] ; do if [ -f "$CURDIR/$MANIFEST" ] ; then PROJECT="$CURDIR" break fi CURDIR=`dirname $CURDIR` done if [ -z "$PROJECT" ] ; then echo "ERROR: Launch this script from an application project directory, or use --project=." exit 1 fi fi log "Using auto-detected project path: $PROJECT" fi # Extract the package name from the manifest PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk` log "Found package name: $PACKAGE_NAME" if [ $? != 0 -o "$PACKAGE_NAME" = "" ] ; then echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST." echo " Please check that the file is well-formed!" exit 1 fi # If --launch-list is used, list all launchable activities, and be done with it if [ "$OPTION_LAUNCH_LIST" = "yes" ] ; then log "Extracting list of launchable activities from manifest:" run_awk_manifest_script extract-launchable.awk exit 0 fi APP_ABIS=`get_build_var APP_ABI` log "ABIs targetted by application: $APP_ABIS" # Check the ADB command, and that we can connect to the device/emulator ADB_TEST=`$ADB_CMD shell ls` if [ $? != 0 ] ; then echo "ERROR: Could not connect to device or emulator!" echo " Please check that an emulator is running or a device is connected" echo " through USB to this machine. You can use -e, -d and -s " echo " in case of multiple ones." exit 1 fi # Get the target device's supported ABI(s) # And check that they are supported by the application # COMPAT_ABI=none CPU_ABI=`adb_shell getprop ro.product.cpu.abi` for ABI in $APP_ABIS; do if [ "$ABI" = "$CPU_ABI" ] ; then COMPAT_ABI=$CPU_ABI break fi done CPU_ABI2=`adb_shell getprop ro.product.cpu.abi2` if [ -z "$CPU_ABI2" ] ; then log "Device CPU ABI: $CPU_ABI" else log "Device CPU ABIs: $CPU_ABI $CPU_ABI2" if [ "$COMPAT_ABI" = "none" ] ; then for ABI in $APP_ABIS; do if [ "$ABI" = "$CPU_ABI2" ] ; then COMPAT_ABI=$CPU_ABI2 break fi done fi fi if [ "$COMPAT_ABI" = none ] ; then echo "ERROR: The device does not support the application's targetted CPU ABIs!" if [ "$CPU_ABI2" = "$CPU_ABI" ] ; then CPU_ABI2= fi echo " Device supports: $CPU_ABI $CPU_ABI2" echo " Package supports: $APP_ABIS" exit 1 fi log "Compatible device ABI: $COMPAT_ABI" # Check that the application is debuggable, or nothing will work DEBUGGABLE=`run_awk_manifest_script extract-debuggable.awk` log "Found debuggable flag: $DEBUGGABLE" if [ $? != 0 -o "$DEBUGGABLE" != "true" ] ; then # If gdbserver exists, then we built with 'ndk-build NDK_DEBUG=1' and it's # ok to not have android:debuggable set to true in the original manifest. # However, if this is not the case, then complain!! if [ -f $PROJECT/libs/$COMPAT_ABI/gdbserver ] ; then log "Found gdbserver under libs/$COMPAT_ABI, assuming app was built with NDK_DEBUG=1" else echo "ERROR: Package $PACKAGE_NAME is not debuggable ! You can fix that in two ways:" echo "" echo " - Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'." echo "" echo " - Modify your manifest to set android:debuggable attribute to \"true\"," echo " then rebuild normally." echo "" echo "After one of these, re-install to the device!" exit 1 fi else # DEBUGGABLE is true in the manifest. Let's check that the user didn't change the # debuggable flag in the manifest without calling ndk-build afterwards. if [ ! -f $PROJECT/libs/$COMPAT_ABI/gdbserver ] ; then echo "ERROR: Could not find gdbserver binary under $PROJECT/libs/$COMPAT_ABI" echo " This usually means you modified your AndroidManifest.xml to set" echo " the android:debuggable flag to 'true' but did not rebuild the" echo " native binaries. Please call 'ndk-build' to do so," echo " *then* re-install to the device!" exit 1 fi fi # push the gdbserver to of the package on the device APP_DST=`get_build_var NDK_APP_DST_DIR` log "Using app dst directory: $APP_DST" run $ADB_CMD push `native_path $APP_DST/gdbserver` /data/data/$PACKAGE_NAME/lib/gdbserver run $ADB_CMD shell chmod 777 /data/data/$PACKAGE_NAME/lib/gdbserver # Let's check that 'gdbserver' is properly installed on the device too. If this # is not the case, the user didn't install the proper package after rebuilding. # DEVICE_GDBSERVER=`adb_shell ls /data/data/$PACKAGE_NAME/lib/gdbserver` log "Found device gdbserver: $DEVICE_GDBSERVER" if pattern_match "No such file or directory" "$DEVICE_GDBSERVER" ] ; then echo "ERROR: Non-debuggable application installed on the target device." echo " Please re-install the debuggable version!" exit 1 fi # Get information from the build system GDBSETUP_INIT=`get_build_var_for_abi NDK_APP_GDBSETUP $COMPAT_ABI` log "Using gdb setup init: $GDBSETUP_INIT" TOOLCHAIN_PREFIX=`get_build_var_for_abi TOOLCHAIN_PREFIX $COMPAT_ABI` log "Using toolchain prefix: $TOOLCHAIN_PREFIX" APP_OUT=`get_build_var_for_abi TARGET_OUT $COMPAT_ABI` log "Using app out directory: $APP_OUT" # Find the of the package on the device DATA_DIR=`adb_shell /system/bin/sh -c "cd /data/data/$PACKAGE_NAME/lib && pwd"` log "Found data directory: '$DATA_DIR'" if [ $? != 0 -o -z "$DATA_DIR" ] ; then echo "ERROR: Could not extract package's data directory. Are you sure that" echo " your installed application is debuggable?" exit 1 fi # Launch the activity if needed if [ "$OPTION_START" = "yes" ] ; then # If --launch is used, ignore --start, otherwise extract the first # launchable activity name from the manifest and use it as if --launch= # was used instead. # if [ -z "$OPTION_LAUNCH" ] ; then OPTION_LAUNCH=`run_awk_manifest_script extract-launchable.awk | sed 2q` if [ $? != 0 ] ; then echo "ERROR: Could not extract name of launchable activity from manifest!" echo " Try to use --launch= directly instead as a work-around." exit 1 fi log "Found first launchable activity: $OPTION_LAUNCH" if [ -z "$OPTION_LAUNCH" ] ; then echo "ERROR: It seems that your Application does not have any launchable activity!" echo " Please fix your manifest file and rebuild/re-install your application." exit 1 fi fi fi if [ -n "$OPTION_LAUNCH" ] ; then log "Launching activity: $PACKAGE_NAME/$OPTION_LAUNCH" run $ADB_CMD shell am start -n $PACKAGE_NAME/$OPTION_LAUNCH if [ $? != 0 ] ; then echo "ERROR: Could not launch specified activity: $OPTION_LAUNCH" echo " Use --launch-list to dump a list of valid values." exit 1 fi # Sleep a bit, it sometimes take one second to start properly # Note that we use the 'sleep' command on the device here. run $ADB_CMD shell sleep $DELAY fi # Find the PID of the application being run PID=`$ADB_CMD shell ps | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=$PACKAGE_NAME` log "Found running PID: $PID" if [ $? != 0 -o "$PID" = "0" ] ; then echo "ERROR: Could not extract PID of application on device/emulator." if [ -n "$OPTION_LAUNCH" ] ; then echo " Weird, this probably means one of these:" echo "" echo " - The installed package does not match your current manifest." echo " - The application process was terminated." echo "" echo " Try using the --verbose option and look at its output for details." else echo " Are you sure the application is already started?" echo " Consider using --start or --launch= if not." fi exit 1 fi # Check that there is no other instance of gdbserver running GDBSERVER_PS=`$ADB_CMD shell ps | grep lib/gdbserver` if [ -n "$GDBSERVER_PS" ] ; then if [ "$OPTION_FORCE" = "no" ] ; then echo "ERROR: Another debug session running, Use --force to kill it." exit 1 fi log "Killing existing debugging session" GDBSERVER_PID=`echo $GDBSERVER_PS | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=lib/gdbserver` if [ $GDBSERVER_PID != 0 ] ; then run $ADB_CMD shell kill -9 $GDBSERVER_PID fi fi # Launch gdbserver now DEBUG_SOCKET=debug-socket run $ADB_CMD shell /data/data/$PACKAGE_NAME/lib/gdbserver 0:$DEBUG_PORT --attach $PID & if [ $? != 0 ] ; then echo "ERROR: Could not launch gdbserver on the device?" exit 1 fi log "Launched gdbserver succesfully." # Setup network redirection log "Setup network redirection" run $ADB_CMD forward tcp:$DEBUG_PORT tcp:$DEBUG_PORT if [ $? != 0 ] ; then echo "ERROR: Could not setup network redirection to gdbserver?" echo " Maybe using --port= to use a different TCP port might help?" exit 1 fi # Get the app_server binary from the device APP_PROCESS=$APP_OUT/app_process run $ADB_CMD pull /system/bin/app_process `native_path $APP_PROCESS` log "Pulled app_process from device/emulator." run $ADB_CMD pull /system/lib/libc.so `native_path $APP_OUT/libc.so` log "Pulled libc.so from device/emulator." # Now launch the appropriate gdb client with the right init commands # GDBCLIENT=${TOOLCHAIN_PREFIX}gdb GDBSETUP=$APP_OUT/gdb.setup cp -f $GDBSETUP_INIT $GDBSETUP #uncomment the following to debug the remote connection only #echo "set debug remote 1" >> $GDBSETUP echo "file `native_path $APP_PROCESS`" >> $GDBSETUP echo "target remote :$DEBUG_PORT" >> $GDBSETUP if [ -n "$OPTION_EXEC" ] ; then cat $OPTION_EXEC >> $GDBSETUP fi $GDBCLIENT -x `native_path $GDBSETUP`
上記はndk-gdbを編集したもの。オリジナルのファイルには以下のライセンス表
記が入っています。(っていう書き方でライセンス条件満たしているのでしたっ
けか。)# # Copyright (C) 2010 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #
- cygwinのシェルでプロジェクトのルートフォルダ上で./my-ndk-gdb を実行。
- gdbが起動するのであとは煮るなり焼くなり。
Android 2.1以前の場合のは、巷の情報をもとにndk-gdbを編集したもの。
ndk-gdbがもとになっているので上記以外の方法でもできるはず。
——–
2011/03/18 00:06 (JST): 「nkd」とかいてたところがあったので「ndk」に修正。
以下のいずれかを実施すればOKっぽい。
セキュリティレベルを弱めるので自己責任でお願いします。
- 方法1
[編集]-[環境設定]-[一般]の[起動時に保護モードを有効にする]をoffにする - 方法2
- HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Adobe\Acrobat Reader\10.0\FeatureLockDown
に
DWORD値 bUseWhitelistConfigFile をつくり 1を設定する - C:\Program Files\Adobe\Reader 10.0\Reader
(もしくは C:\Program Files (x86)\Adobe\Reader 10.0\Reader など、AcroRd32.exeのインストールされている場所)に
テキストファイル ProtectedModeWhitelistConfig.txt をつくり
中にFILES_ALLOW_ANY=\??\pipe\skkiserv010500_pipe_*
を書く。
(*はワイルドカード。skkimeは*のところをユーザ名にした名前のパイプをつくるので、ユーザ名にしておいたほうが安全かも。)
- HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Adobe\Acrobat Reader\10.0\FeatureLockDown
参考:http://kb2.adobe.com/jp/cps/880/cpsid_88069.html
Darwin mbp-090704.local 10.6.0 Darwin Kernel Version 10.6.0
uim-fep version 1.6.1
uim-fepを使うとターミナルでも ^J でSKKを使えるようになります。
AquaSKKでも(TSM版では)かなキーで日本語モードに入れますが、
つい^Jを押してしまうことがあるので便利です。
また、vncではだいたいかなキーが通らないので便利です。
- AquaSKKのskkservを有効にする
- ソースをダウンロードする:
http://code.google.com/p/uim/downloads/list - アーカイブを展開する
- ./configure
- make
- sudo make install
- uim-pref-gtk
X11が起動するまで少し時間がかかる
Xlib: extension “RANDR” missing on display とか出ても気にしない
SKK dictionaries グループ の SKK Server あたりを変更する - uim-fep
ですが、ある読みで一回変換すると別の読みで変換できなくなるという不具合が
ありました。
skkservを使わないという選択肢もありますが、やっぱり辞書をわけるのも嫌な感じなので
uim-fep側に以下のパッチを適用。
AquaSKK用なので他のskkservでは逆にだめになるかもしれません。
--- uim/skk.c.orig 2011-01-07 11:17:32.000000000 +0900 +++ uim/skk.c 2011-01-22 10:16:06.000000000 +0900 @@ -797,7 +797,8 @@ uim_asprintf(&idx, "%s%c", s, okuri_head); - fprintf(wserv, "1%s \n", idx); + // fprintf(wserv, "1%s \n", idx); + fprintf(wserv, "1%s ", idx); ret = fflush(wserv); if (ret != 0 && errno == EPIPE) { free(idx); @@ -847,6 +848,8 @@ while ((nr = read(skkservsock, &r, 1)) != -1 && nr != 0 && r != '\n') ; free(line); + // disconnect to re-sync + skkserv_disconnected(di); return NULL; } }