<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5094259393086141803</id><updated>2012-03-04T12:34:06.674+08:00</updated><category term='objc'/><category term='upnp'/><category term='android'/><category term='java'/><category term='ios'/><title type='text'>keep reading</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-3005124407201990654</id><published>2012-03-04T12:29:00.001+08:00</published><updated>2012-03-04T12:34:06.684+08:00</updated><title type='text'>[autotools] 加入額外的 m4 路徑</title><content type='html'>&lt;div&gt;&lt;span &gt;要試著在 osx 上編譯 irssi，由於我並不是用 macports 安裝 library 的，有些 .m4 檔並不是裝預設的目錄。&lt;/span&gt;&lt;span style="font-family: Georgia, serif; "&gt;以 homebrew 來說，它會放在 homebrew 安裝目錄的 share/aclocal。為了要讓 irssi 提供的 autogen.sh 吃到我指定的 .m4 路徑，需要更動 ACLOCAL 變數：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;blockquote&gt;ACLOCAL="aclocal -I. -I$HOME/app/homebrew/share/aclocal" ./autogen.sh &lt;/blockquote&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;除此之外，再解決一些相依性的問題，我就能編譯自己的 irssi 了 :D&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-3005124407201990654?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/3005124407201990654/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/03/autotools-m4.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/3005124407201990654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/3005124407201990654'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/03/autotools-m4.html' title='[autotools] 加入額外的 m4 路徑'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-3949584675378247601</id><published>2012-03-02T15:03:00.003+08:00</published><updated>2012-03-02T15:06:15.917+08:00</updated><title type='text'>[autotools] configure 指定額外的 library</title><content type='html'>&lt;div&gt;&lt;span&gt;想試著編 ffmpeg 並且引用 libmp3lame 的功能。但我並沒有將此 library 裝在系統內，所以得另外指定 library 的位置：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;span&gt;&lt;blockquote&gt;CFLAGS="-I/opt/libmp3lame/include" LDFLAGS="-L/opt/libmp3lame/lib" ./configure --enable-libmp3lame --prefix=/opt/ffmpeg_full&lt;/blockquote&gt;&lt;/span&gt;&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;主要就是透過 CFLAGS 指定 include path，透過 LDFLAGS 通知 linker 該去哪個目錄找 library &lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-3949584675378247601?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/3949584675378247601/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/03/autotools-configure-library.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/3949584675378247601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/3949584675378247601'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/03/autotools-configure-library.html' title='[autotools] configure 指定額外的 library'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-3207691265226681068</id><published>2012-03-01T13:45:00.002+08:00</published><updated>2012-03-01T13:46:22.828+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>[Android] 偵測 USB 拔除</title><content type='html'>&lt;div&gt;&lt;span style="font-family: Georgia, serif; "&gt;最近 Player 開始測試播放 USB 上的檔案，播放當然是正常的，只是當使用者未正常地結束播放就拔掉 USB 這件事就不知道怎麼處理了。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;好在同事分享了一個方法，能直接透過 Broadcast Receiver 來獲得通知，重點在於要怎麼寫 IntentFilter：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;span&gt;IntentFilter intentFilter = new IntentFilter();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_NOFS);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        intentFilter.addDataScheme("file");&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;對我們的情況來說，最重要的是 Intent.ACTION_MEDIA_EJECT，因為不管怎麼樣，只要 USB 被拔掉了，而 Player 又在播放 local file 的情況我們就需要停下它。&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-3207691265226681068?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/3207691265226681068/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/03/android-usb.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/3207691265226681068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/3207691265226681068'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/03/android-usb.html' title='[Android] 偵測 USB 拔除'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-7459061804011858824</id><published>2012-02-24T09:16:00.000+08:00</published><updated>2012-02-24T09:17:39.089+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='objc'/><title type='text'>[objc] 被沒收的 UTF8String</title><content type='html'>&lt;div&gt;&lt;span style="font-family: Georgia, serif; "&gt;最近協助同事開發 webkit plugin，我提供的 Player 能使用 playWithUrl 啟動播放：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;blockquote&gt;-(void)playWithUrl: (const char*) aUrl;&lt;/blockquote&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;但在 plugin 由外層 javascript 傳入的參數是 NSString* 型式的，直覺上我們會利用 UTF8String 方法，獲得 c String：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;blockquote&gt;[theUrl UTF8String]&lt;/blockquote&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;但試了幾次後，發現轉進來的 c String 偶爾會變成奇怪的內容，俗稱『亂碼』。這種偶發性的不確定因素，有股熟悉感。先翻閱手冊，在 UTF8String 中提到：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;blockquote&gt;The returned C string is automatically freed just as a returned object would be released; you should copy the C string if it needs to store it outside of the autorelease context in which the C string is created.&lt;/blockquote&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;簡單地說，這個 c String 是生命週期會跟著原來的 NSString 跑，所以，要確保它被使用的時候，是正確的內容，我們應該先 retain 它，最後，我們討論了一下，決定提供一個新的 method：&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;/span&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;span &gt;-(void)playWithNSStringUrl: (NSString*) theUrl&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;{&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;    [theUrl retain];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;    memset(inputUrl, 0, sizeof(char) * 1024);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;    strncpy(inputUrl, [theUrl UTF8String], 1024);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;    [theUrl release];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;    [self playWithUrl: inputUrl];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;}&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;span &gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;確實宣示我們持有 theUrl，並保存其內容。亂碼就不再來亂了 :D&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-7459061804011858824?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/7459061804011858824/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/02/objc-utf8string.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/7459061804011858824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/7459061804011858824'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/02/objc-utf8string.html' title='[objc] 被沒收的 UTF8String'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-2624932965104572993</id><published>2012-02-17T11:49:00.001+08:00</published><updated>2012-02-24T09:07:39.807+08:00</updated><title type='text'>XCode 編譯 i386 架構時的 "illegal text reloc to XXX" 問題</title><content type='html'>&lt;div&gt;最近在協助同事編寫 Webkit Plugin。經驗上如同 iOS 實作時相仿：為了同時支援不同的架構(arm6, arm7)得將用到的 library 分別編譯出來，將利用 lipo 將它們 merge 成一個同時支援多種架構的 library，即為俗稱的 universal library。&lt;/div&gt;&lt;div&gt;在一開始我忘了需要不同架構的 library，因為某些 osx 需要 32bit 的 library，在我電腦上，預設編出來的是 x86_64 架構，本來以為單純編譯出 i386 的版本再合併起來就好。沒想到發生了新的問題：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;ld: illegal text reloc to cstring from ../FFMPEG.i386/lib/libavformat.a(aea.o) in _aea_read_header for architecture i386&lt;/div&gt;&lt;div&gt;collect2: ld returned 1 exit status&lt;/div&gt;&lt;div&gt;Command /Developer/usr/bin/llvm-g++-4.2 failed with exit code 1&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;雖然不太清楚細節是什麼，總之這錯誤看起來是 linker 在進行 relocation 時發生了問題。為何這個問題只在 i386 編譯時會發生呢？剛好找到在 apple maillist 上有說明：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;http://lists.apple.com/archives/unix-porting/2008/Jan/msg00027.html&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;*** EXPLANATION ***&lt;/div&gt;&lt;div&gt;The two assembly commands load the absolutes address of _trail into R15. Doing so is fine if _trail is ultimately in the same linkage unit. _trail is in libmodule.dylib.&lt;/div&gt;&lt;div&gt;For this to work, at runtime the dynamic loader (dyld) would have to rewrite the two instructions. Normally dyld only updates data pointers.&lt;/div&gt;&lt;div&gt;One work around is to make libdyalog an archive (e.g. libdyalog.a) and link that with pere.s. Then all the code would be in the same linkage unit, so there would be no need for runtime text relocs.&lt;/div&gt;&lt;div&gt;The runtime (dyld) does support text relocs (updating instructions) for i386, but you need to link with -read_only_relocs suppress.&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;相信大部分的人跟我一樣對於底層能掌握的知識不多，至少我們知道：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;The runtime (dyld) does support text relocs (updating instructions) for i386, but you need to link with -read_only_relocs suppress.&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;索性在 other link flags 加上：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;-read_only_relocs suppress&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;程式似乎就能正常編譯成功了。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS. 這個 flag 不能加在 x86_64 的架構，請設定在條件式編譯選項。&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-2624932965104572993?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/2624932965104572993/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/02/xcode-i386-illegal-text-reloc-to-xxx.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/2624932965104572993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/2624932965104572993'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/02/xcode-i386-illegal-text-reloc-to-xxx.html' title='XCode 編譯 i386 架構時的 &quot;illegal text reloc to XXX&quot; 問題'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-8915245554071845008</id><published>2012-02-07T10:40:00.004+08:00</published><updated>2012-02-07T10:46:13.533+08:00</updated><title type='text'>MySQL 資料檔移轉 (on osx)</title><content type='html'>&lt;div&gt;由於某台測試 Server 的一些設定檔毁損讓 LVM 無法正常掛載，經 MIS 緊急修復後我也參與了復原工作。從掛回去的檔案系統上，MIS 備份出了 MySQL data 路徑的資料庫檔案。由於 MySQL Server 是 5.0 版本的，要直接將檔案丟到新版的 MySQL Server 是不可行的。最好的情況是能轉變成 mysqldump 匯出的 .sql 檔。在 MySQL 官網看了半天，找不到合用的 5.0 binary 檔，要弄個合適的 linux 環境可能又要花不少時間，乾脆就抓 source code 自己來編了。這樣就可以不需要 linux 環境，能編出來放在 osx 上執行。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;source code 的編譯上沒有遇到什麼困難，一如往常地 ./configure --prefix=/opt/mysql &amp;amp;&amp;amp; make install 就能搞定。而接下來遇到的問題才是值得我筆記下來的部分！&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;將備份出來的 data 放至 MySQL 指定到的 data 路徑(自編的情況是放在 $MYSQL/var)，啟動 MySQL 看起來一切正常。show tables 也都有看到資料表出現，但有些是採用 innodb engine，因為忘了放 ibdata1 檔而只有看到名字，沒有實際的內容。在關閉 MySQL Server 放回檔案就能正確讀到那些使用 innodb engine 的資料表。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我開始了第一次 mysqldump 的嘗試，但它跟我說找不到資料表：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;mysqldump: Got error: 1146: Table .agentlog doesn't exist when using LOCK TABLES&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這個資料表在 show tables 有出現，但名稱為 AgentLog。我也查了檔案是有 AgentLog.frm 的。於是我推測問題應該是出在 osx 的檔案系統，osx 安裝時選用的檔案系統預設為不分大小寫的。要在 osx 上建立出區分大小寫的檔案系統很容易，這步驟在 Android Soruce 編譯的指引中有包含：請參考『&lt;a href="http://source.android.com/source/initializing.html"&gt;Setting up a Mac OS X build environment&lt;/a&gt;』。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;於是我重新將資料放在新建立的、有區分大小寫的檔案系統中，再度執行 mysqldump 就不會找不到檔案。但我遇到新的問題：mysqldump 要匯出某些表格時說它無法支援 big5_chinese_ci。於是我查了 ./configure --help&lt;/div&gt;&lt;pre&gt;&lt;span  &gt;&lt;span style="white-space: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;  --with-charset=CHARSET&lt;br /&gt;   Default character set, use one of:&lt;br /&gt;   binary&lt;br /&gt;   armscii8 ascii big5 cp1250 cp1251 cp1256 cp1257&lt;br /&gt;   cp850 cp852 cp866 cp932 dec8 eucjpms euckr gb2312 gbk geostd8&lt;br /&gt;   greek hebrew hp8 keybcs2 koi8r koi8u&lt;br /&gt;   latin1 latin2lab latin5 latin7 macce macroman&lt;br /&gt;   sjis swe7 tis620 ucs2 ujis utf8&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;                          &lt;/div&gt;&lt;div&gt;在預設的情況下沒有包含 big5_chinese_ci！不過我們有另一個參數可以使用：&lt;/div&gt;&lt;pre&gt;&lt;span  &gt;&lt;span style="white-space: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;  --with-extra-charsets=CHARSET,CHARSET,...&lt;br /&gt;   Use charsets in addition to default (none, complex,&lt;br /&gt;   all, or a list selected from the above sets)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;於是我指定了 --with-extra-charsets=all，最後終於將資料匯成 .sql 的型式&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-8915245554071845008?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/8915245554071845008/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/02/mysql-on-osx.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/8915245554071845008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/8915245554071845008'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/02/mysql-on-osx.html' title='MySQL 資料檔移轉 (on osx)'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-5513146752030382068</id><published>2012-02-02T10:24:00.002+08:00</published><updated>2012-02-02T11:02:31.717+08:00</updated><title type='text'>以 OpenGL ES 1.x 在 iOS 上貼圖(texture) 的限制</title><content type='html'>&lt;div&gt;最近為了要製作 Media Player 開始研究怎麼在 iOS 上畫圖，其中一種畫圖的方式是使用 OpenGL ES。OpenGL ES 對於我這初學者來說難度不小，而在先前『試作』Media Player 的經驗，我們能運用 Texture 來貼圖。參考了 iPhone 遊戲自作入門，我已經知道如何畫出一個方形的圖片。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;a href="http://www.anobii.com/books/iPhone%E9%81%8A%E6%88%B2%E8%87%AA%E4%BD%9C%E5%85%A5%E9%96%80/9789866348709/01db614d442d20d52e/" title="More about iPhone遊戲自作入門"&gt;&lt;img src="http://image.anobii.com/anobi/image_book.php?type=1&amp;amp;item_id=01db614d442d20d52e&amp;amp;time=1315364323" title="More about iPhone遊戲自作入門" alt="More about iPhone遊戲自作入門" style="padding: 5px;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;值得注意的是在書上的 93 頁提到：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;讀入材質中的圖像尺寸的長與寬必須分別都是 2 的倍數。若沒有遵守這個條件，圖像可能無法正常描繪，必須多加注意。&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;同樣的限制在著名的 OpenGL ES From the Ground Up, Part 6: Textures and Texture Mapping 也有提到：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;Images used for textures must be sized so that their width and height are powers of 2, so both the width and height should be 2, 4, 8, 16, 32, 64, 128, 256, 512, or 1024. An image could be, for example, 64x128, or 512x512. &lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;所以，我們能知道書上的翻譯不太正確，是 2 的次方，而非 2 的倍數。在這限制大約在半年前就有遇過，那時只是先對於在 iOS 上貼圖的方法進行調查與撰寫雛型，隨著時間過去與忙碌於其他專案而將程式碼弄丟了(也忘了筆記下來)。在重新開始這個工作前，我試著在 StackOverflow 提問：&lt;a href="http://stackoverflow.com/questions/8563012/how-do-i-render-frames-from-ffmpeg-to-the-screen-in-an-ios-app"&gt;How do I render frames from FFmpeg to the screen in an iOS app?&lt;/a&gt; 在網友的回覆中，得到一些重要的 keyword！其中最有用的資訊就是知道 NPOT。並且由另一篇提問得到畫出非 2 次方邊長是可行的 &lt;a href="http://stackoverflow.com/questions/4760174/rendering-to-non-power-of-two-texture-on-iphone/4761453#4761453"&gt;Rendering to non-power-of-two texture on iPhone&lt;/a&gt;。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;因此，對於畫出非 2 次方邊長的 texture 的條件在於：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;一、你的裝置必需支援 GL_APPLE_texture_2D_limited_npot Extension，可以利用官方文件提到的 &lt;a href="http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/DeterminingOpenGLESCapabilities/DeterminingOpenGLESCapabilities.html"&gt;CheckForExtension&lt;/a&gt; 測試&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;二、必需滿足 &lt;a href="http://www.khronos.org/registry/gles/extensions/APPLE/APPLE_texture_2D_limited_npot.txt"&gt;APPLE_texture_2D_limited_npot&lt;/a&gt; 的使用條件&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;In the absence of OES_texture_npot, which lifts these restrictions, neither mipmapping nor wrap modes other than CLAMP_TO_EDGE are supported in conjunction with NPOT 2D textures.  A NPOT 2D texture with a wrap mode that is not CLAMP_TO_EDGE or a minfilter that is not NEAREST or LINEAR is  considered incomplete.  If such a texture is bound to a texture unit, it is as if texture mapping were disabled for that texture unit&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;這句話在實作上的意義就是：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span &gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;完整的範例如下：&lt;/div&gt;&lt;div&gt;https://s3.amazonaws.com/qrtt1.articles/xcode.sample/texture.npot.tgz&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-5513146752030382068?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/5513146752030382068/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/02/opengl-es-1x-ios-texture.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/5513146752030382068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/5513146752030382068'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/02/opengl-es-1x-ios-texture.html' title='以 OpenGL ES 1.x 在 iOS 上貼圖(texture) 的限制'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-7119380926831193607</id><published>2012-02-01T11:34:00.002+08:00</published><updated>2012-02-01T11:46:38.275+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ios'/><title type='text'>讀取 iOS 專案內的資源檔</title><content type='html'>&lt;div&gt;去年也開始接觸了 iOS 的開發，有些小技巧還是不熟悉啊！像簡單地讀取專案內的資源檔就查了半天。決定開始記錄這些小事情。為了練習使用 opengles 畫出單一的圖形，我放了一個 RGB565 的資料檔(rgb320x240.raw) 在專案內，打算採用 fopen 將它開啟讀入記憶體上操作。但不知道『路徑』該填些什麼。一開始只填檔名，想碰一下運氣，但 FILE* 始終是 NULL。後來在 stackoverflow 找到了解答：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;NSString *path = [[NSBundle mainBundle] pathForResource:@"rgb320x240" ofType:@"raw"];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這麼一來我就取得了 rgb320x240.raw 在 iPhone 上的實際路徑：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/var/mobile/Applications/DAC73AB8-79F8-4B6F-9AC0-099C564C8ADA/GLES.app/rgb320x240.raw&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-7119380926831193607?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/7119380926831193607/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2012/02/ios.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/7119380926831193607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/7119380926831193607'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2012/02/ios.html' title='讀取 iOS 專案內的資源檔'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-6678223539160604084</id><published>2011-02-05T19:38:00.001+08:00</published><updated>2011-07-23T16:54:56.445+08:00</updated><title type='text'>版本控制隨筆 (2) 關於開發支線</title><content type='html'>&lt;div&gt;對於沒有版本控制系統使用經驗的人，可能還沒意識到二種需求的衝突面：開發成果必需被持續地追蹤且版本控制系統內的狀態同時保持最穩定的狀態。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;因為，我們尚未提到開發支線的概念。個人認為版本控制系統最大的特色與差異並非集中式或分散式之別，是如何處理開發支線及使用開發支線會糟遇的各種衍生問題。若您試著使用版本控制系統，而沒有利用過任何開發支線的功能。您就是在預設的開發支線上工作，以 CVS 來說就是尚未設定開發支線標籤的情況。以 Subversion 來說，通常就是 trunk 目錄。trunk 目錄可以是想像的，或實際上建立的目錄。它的中譯即為主幹、樹幹的意思。隱喻著樹枝會伴隨著開發而萌發。以 GIT 來說，通常就是 master 開發支線。對於 GIT 或 Mercurial 來說，它們預設就建好了開發支線。開發支線的概念，對他們來說是明確且必需的功能。所以，他們在設計上，對於開發支線的操作都思考著如何以較低的成本(例如：儲存空間、建立的效率)來產生開發支線。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在您漸漸意識到開發支線的存在後，這時解釋開發需求的衝突就變得有意義。假設，在我們的開發活動中，僅使用一個開發支線(預設的開發支線)。在沒有建立程式碼提交規範的情況下，任何能操作版本控制系統的開發人員，都能隨時將手邊的程式或任意檔案提交給版本控制系統。團隊人數越多，修訂版本也就累積地越快。而這使得該開發支線上的程式碼，永遠處於不穩定的狀態。不穩定的狀態，最顯性的表徵就是當您由版本控制系統提取最新的專案內容時，因為存在著許多不完程式程式或組態設定造成編譯上的錯誤。這樣的錯誤傷害開發者的生產力。要替尚未完成的程式碼排除錯誤是徒勞無工的，何況對於同時使用版本控制系統的開發者，他們並不需要關注到同樣的軟體元件。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;由於使用單一開發支線的情境下，毫無限制地提交新的開發結果很可能使專案最新的狀態毀損(Broken)。有種消極的對策：規範開發者在提交任何內容前，確定它是正確的、可進行編譯的。這是許多使用單一開發支線的團隊會使用的策略。不過總是有部分開發者無法確定自己的內容是否符合要求，這使得軟體品質時常因此而降低。在極端的例子，這類的版本控制系統使用者，會被取消提交權限，建議他提供修補檔案(Patch File)，在經過驗證後由具有權限的使用者進行提交的動作。這種策略，勉強滿足使軟體/專案最新的狀態維持在較穩定狀態的需求。但在運用版本控制系統的開發流程中，並不能輔助開發者在開發活動尚未穩定之前的版本控制需求。在程式還未穩定前，只能依賴開發者逕自手工般地管理嗎？這樣消極的工作流程，讓需要加強訓練的生手與重新進行觀念調整的開發者缺少了成長的機會。即便，開發團隊已經採用此種保守的策略應付單一開發支線的使用情境，仍無法保證專案的穩定度。只是滿足在任何時間提取出最新的狀態時，編譯工作能順利地執行。為了達到較高的穩定度，更進一步要求，開發者在新功能(Feature)或是功能改善(Enhancement)被完整地實作與驗證後才能進行提交的工作。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這樣保守的工作流程規範，使得版本控制系統的採用，就像是學生被要求在合理的時間點，向 FTP 上傳已經完成的作業一樣。但是這種情況會在善用多個開發支線時轉變，您可以既保守，卻極積地使用版本控制系統的便利。將特定的開發支線定為穩定版本專屬的，僅在將已穩定狀態的變更合併(Merge)至此開發支線。而一般的開發活動，則是由此穩定的開發支線產生的分枝來進行開發。因此，維護產品品質的開發支線只有在合併經過多次檢驗的開發成果時才會用到，除了這個理由只剩下急迫性較高的錯誤修補會動到它。建立開發支線是為了滿足日常的開發活動，原因大致為：修正程式的錯誤、替程式進行完整的複察(Code Review)與重構(Refactoring)、嘗試不同的實作方案或是實作新的功能。當然，我們也能隨意地建立開發支線來測試任何的新想法，程式開發的過程中線出的活動，通常伴隨著有創意的點子產生。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;對於使用開發支線的最大困擾是在開發活動告一段落，當開發者試著要將變更合併至另一個開發支線的時候，這時可能會產生程式碼衝突(Conflict)。衝突的產生，其實就是同一個地方，出現了些微的差異，導致版本控制系統無法透過演算法來決定該合併成什麼樣子。因此，標示檔案狀態為「衝突」是版本控制系統通知您，需要您的協助、請決定新的版本要是什麼樣子的途徑。只有全新的檔案、與舊有程式無關的全新的實作不會產生衝突。只要明白衝突的意義，就會知道，這是個決策的時候，而非新問題產生的時刻。不同的開發類型，有不同的方式降低衝突的機率。但不良的寫作習慣，卻會導致各種開發類型、分工都提高了衝突發生的機會。若您曾閱讀過重構的相關書籍，您可能已經聯想到有哪些壞味道(Bad smell)肯定會提高衝突發生的機率。個人認為最嚴重的，有立即重構必要的是：霰彈式修改(Divergent Change)與發散式變化(Shortgun Surgery)。一個會讓特定的程式常常產生變化，簡單地說，您設計的類別不符合單一責任原則。負責的越多，修改的理由就越多。另一種是，要增加一個新功能卻要改變許多的程式，即使他們的關係沒有那麼深厚。無論是同一個區間時常被改變，或著改變會動到的圍範太大，皆使得衝突發生的機會大增。經過這番的「提醒」相信您已經知道如何，輕易地製造產生許多衝突的開發支線，只要您掌握住通則，也能順利地避免。&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-6678223539160604084?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/6678223539160604084/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2011/02/2.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/6678223539160604084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/6678223539160604084'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2011/02/2.html' title='版本控制隨筆 (2) 關於開發支線'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-5394027702894882630</id><published>2011-02-05T19:17:00.001+08:00</published><updated>2011-02-05T19:38:40.193+08:00</updated><title type='text'>版本控制隨筆 (1)</title><content type='html'>&lt;div&gt;由於工作上的需要，我在從事開發工作後便開始接觸了版本控制系統。Subversion 是我第一個接觸的版本控制系統。那時，對於版本控制系統的體會大概就是：哇！這實在非常方便。我可把程式寫一個段落，然後將它上傳到一個伺服器裡。即使，我不小心殺掉了電腦裡的資料，只要重建好開發環境，再將專案由伺服器上抓回來就能繼續工作。這是我對版本控制系統最先感受到的好處。事實上，也是對版本控制系統的錯誤理解。要做到同樣的功能，用 email 或 ftp 上傳檔案，其實是一樣的事情。有太多技術能做到把檔案便利地保存至遠端，所以我向自己提出這樣的疑問：是哪些特質使得版本控制系統，不同於哪些將檔案方便上傳至伺服器呢？這個問題的答案，同時也是拒絕使用分散式版本控制工具的常見迷思之所在。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;將檔案保存至中央的伺服器，只是版本控制系統實作的一種型式。通常我們稱這一類版本控制系統為集中式的版本控制系統，如 Subversion 或 CVS 都是屬於這一類。它們都需要依賴一個中央的伺服器來處理版本控制的行為。版本控制系統的存在，並不只是為了讓使用者方便地將檔案上傳至伺服器，上傳至伺服器只遷就於版本控制系統的實作模型的需要產生的副作用。這個行為，我們稱為提交(Commit)。提交是指您決定好將目前對於檔案(有時包含目錄)的變更，記錄在版本控制系統，每一筆記錄就是一個修訂版本(Revision)。對版本控制系統來說，它必需準備一個資料庫來存放這些記錄，這個特殊的資料庫被稱作檔案庫(Repository)。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;修訂版本是個相當好的想法，因為它不同於一個時間點。當有人提出 qrtt1 你前天改的那個版本好像有問題？這時就得傷腦筋，前天的那時候的那一個改變產生了問題呢？或是其實不是前天，而是發生在昨天呢？這時版本控制系統能發揮的作用就是在哪一個修訂版出了問題。作為開發者能將修訂版本回溯到指定的修訂版本。這時版本控制系統發揮了作用，讓我們能將專案回復到特定的狀態，而針對確認問題的重演(Reproduce)行為才得以精確實履行。對於專案活動來說，適當地使用版本控制系統。您得先掌握專案議題與修訂版本的關係。新的功能(Feature)實作的可能會產生一連串的修訂版本。當功能達到一個里程碑時，我們也能用修訂版本記錄這個有意義的狀態。要檢驗該功能是否完備，就是要檢查該修訂版本(或該本版本之後)的專案狀態。因此，我們習慣將議題追蹤系統與版本控制系統做若干的結合。至少，我們能在議題完成的評論欄位附註修訂版號。而臭蟲回報(Bug Report)時，也是相類的作法。當 QA 回報什麼功能有問題時，在最新的狀態下若能重演問題，那就能附註在最新的修訂版本下能從演此問題。若該功能能對於至過去的議題，那就能找問題發生的修訂版本，這能輔助面對問題時，找出發生的根源與時間點。修訂版本，持有的資訊不僅是專案的變更記錄，還包含變更者與變更的時間。這讓問題的發現與觀念調整的對象皆同時獲得。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;儘管在我描述中的修訂版本是如此便利，但某些版本控制系統的修訂版本並不適合這樣使用。以 CVS 來說，它的修訂版本是每一個檔案擁有自己的修訂版本。這在使用上會產生許多困擾，因為我們必需找出某個特定的時間點，各別檔案對應的修訂版本將它們各別回復成我們設想的狀態。若您想要擁有一個代表整體改變的修訂版本，在 CVS 下的替代方案就是使用標籤(Tag)。對 CVS 的使用者來說，標籤就是替一群檔案目前修訂版本取別名的動作。但這在使用上算是額外的動作，開發者有可能會遺忘下標籤的作動，喪失在適當的時機建立修訂版本的機會。而 Subversion 與其他較新的版本控制系統就有變更集合(Changeset) 的概念，它將同時被提交的檔案視為一個修訂版本(有些版本控制系統的設計者，甚至以較嚴謹的交易機制來實作，使得提交的具大 atomic 的性質)。因此，這樣的修訂版本就自然地產生出來，您不需要額外進行下標籤的動作。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;回過頭來，先前提過版本控制系統允許您透過提交(Commit)產生修訂版本(Revision)。您可以這樣想：每一個修訂版本都是一份檔案庫記錄的快照(Snapshot)，就像照相一般，將那個時刻的狀態保存下來。我們能利用版本控制系統提取(Checkout)的功能，取出最新的資料庫記錄，或著特定的修訂版本。在某些情況，版本控制系統亦提供利用標籤產生修訂版本別名的功能。別名就如同朋友之間的暱稱，指稱暱稱，如同指稱本人。因此，我們向版本控制系統提取記錄時，可以使用修訂版本代號，也可以使用標籤。除此之外，稍後將介紹的開發支線(Branch)也能作為提取的目標。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;檔案庫(Repository)、修訂版本(Revision)、提取(Checkout)、提交(Commit)與標籤(Tag)，這些概念與動作即為版本控制系統使上用的基本觀念，同時為版本控制系統提供的基礎建設。然而，這些基本的功能是無法滿足軟體開發的實際需要。對開發團隊來說，最舒適的狀態是我們知道我們永遠有能夠穩定的工作版本能使用。而對應到採用版本控制系統的情境來說，我們有個穩定的版本，它的標籤是 Release-0.1 (實際上對應的修訂版號可能是 Rev 1981)。無論什麼時候，我們都能提供客戶最穩定的版本。同時，我們的工作伙伴也能進行新功能的開發。儘管正在開發的功能，還沒告一段落，我們都希望版本控制系統能輔助所有的開發活動能被記錄起來。&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-5394027702894882630?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/5394027702894882630/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2011/02/1.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/5394027702894882630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/5394027702894882630'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2011/02/1.html' title='版本控制隨筆 (1)'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5094259393086141803.post-4476908351077096677</id><published>2009-07-07T16:36:00.000+08:00</published><updated>2009-07-07T17:54:57.543+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='upnp'/><title type='text'>UPnP Actoin Invoke</title><content type='html'>最近開始學習撰寫手機程式，這使我不得不思考過去一些使用 Library 能完成的事項要試著在沒有 Library 的情況下完成。以 UPnP Action 呼叫為例，在 Java 有 &lt;a href="http://cgupnpjava.sourceforge.net/"&gt;CyberLink for Java&lt;/a&gt; Library 能夠使用。&lt;br /&gt;&lt;br /&gt;今天就在想，一個完整的 UPnP Library 包含 UPnP Device、Service 的組裝，並且在執行期負責做 Device、Service 的廣告。另外，它還需要提供 Control Point 與 UPnP Service 打交道。真的要使用 UPnP Service 您必需有 Control Point，UPnP 服務會以 Http Server 的方式，等待來自 Control Point  Actoin Invoke。若僅是寫手持裝置上的 App 實在不需要整套 UPnP Library，頂多有 Control Point 就足夠。&lt;br /&gt;&lt;br /&gt;不過，大家 Library 都相當完整不可分割。只好重新思考自己由底層的 protocol 下手。再次翻閱 UPnP 規格書，要對控制 UPnP Action 其實並不困難：&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_EqsVDY1SWMQ/SlMTZ88WQCI/AAAAAAAAARE/nXKpugDpofo/s1600-h/upnp-action.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 372px; height: 146px;" src="http://3.bp.blogspot.com/_EqsVDY1SWMQ/SlMTZ88WQCI/AAAAAAAAARE/nXKpugDpofo/s320/upnp-action.png" alt="" id="BLOGGER_PHOTO_ID_5355645718450552866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;事實上，有了資料格式，整件事就簡化成了填空題。以操作 UPnP AV Media Server 內 Connection Manager 服務提供的 GetProtocolInfo Action 為例，下圖為 在 UPnP Spy 提供的視窗介面，您可以見到 GetProtocolInfo 有二個回傳變數 (無輸入變數)，一個為 Source，另一個為 Sink，所以您應該要期待即使是使用 Socket 與 UPnP Service 溝通，也要有這項項回傳值：&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_EqsVDY1SWMQ/SlMYbUGTI2I/AAAAAAAAARM/hfYemSpFopw/s1600-h/ActionInvoke.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 215px;" src="http://2.bp.blogspot.com/_EqsVDY1SWMQ/SlMYbUGTI2I/AAAAAAAAARM/hfYemSpFopw/s320/ActionInvoke.png" alt="" id="BLOGGER_PHOTO_ID_5355651239404315490" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;而控制協定要求我們要填寫 POST Path，您可以在 Service Description Language 文件找到，透過 UPnP Spy 能知道此 Service 的控制 URL 為「_urn:schemas-upnp-org:service:ConnectionManager_control」。另外，您還需準備一個 SOAPACTION Header，規則是「serviceType + '#' + actionName」。最後 Http Body，依規格書的建議填上適當的 SOAP 標籤與 ActionName、ActionArgument，需傳送內容如下：&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;POST /_urn:schemas-upnp-org:service:ConnectionManager_control HTTP/1.1&lt;br /&gt;HOST: 192.168.6.18&lt;br /&gt;Content-Type: text/xml; charset="utf-8"&lt;br /&gt;Content-Length: 313&lt;br /&gt;SOAPACTION: "urn:schemas-upnp-org:service:ConnectionManager:1#GetProtocolInfo"&lt;br /&gt;Connection: close&lt;br /&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&amp;gt;&lt;br /&gt;&amp;lt;s:Body&amp;gt;&lt;br /&gt;  &amp;lt;u:GetProtocolInfo xmlns:u="urn:schemas-upnp-org:service:ConnectionManager:1"&amp;gt;&amp;lt;/u:GetProtocolInfo&amp;gt;&lt;br /&gt;&amp;lt;/s:Body&amp;gt;&lt;br /&gt;&amp;lt;/s:Envelope&amp;gt;&lt;/pre&gt;&lt;br /&gt;如果，Action Invoke 成功，它就會回傳下列訊息：&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;HTTP/1.1 200 OK&lt;br /&gt;EXT:&lt;br /&gt;CONTENT-TYPE: text/xml ; charset="utf-8"&lt;br /&gt;SERVER: Windows NT/5.0, UPnP/1.0, Intel CLR SDK/1.0&lt;br /&gt;Content-Length: 521&lt;br /&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"&amp;gt;&lt;br /&gt;&amp;lt;s:Body&amp;gt;&lt;br /&gt;&amp;lt;u:GetProtocolInfoResponse xmlns:u="urn:schemas-upnp-org:service:ConnectionManager:1"&amp;gt;&lt;br /&gt;   &amp;lt;Source&amp;gt;http-get:*:audio/mpeg:*,http-get:*:image/jpeg:*,http-get:*:text/plain:*,http-get:*:application/octet-stream:*,http-get:*:audio/x-mpegurl:*&amp;lt;/Source&amp;gt;&lt;br /&gt;   &amp;lt;Sink /&amp;gt;&lt;br /&gt;&amp;lt;/u:GetProtocolInfoResponse&amp;gt;&lt;br /&gt;&amp;lt;/s:Body&amp;gt;&lt;br /&gt;&amp;lt;/s:Envelope&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;透過簡單的實驗知道，即使不依賴 Library 要實現簡單的 UPnP Control 是可行的。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5094259393086141803-4476908351077096677?l=qrtt1.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://qrtt1.blogspot.com/feeds/4476908351077096677/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://qrtt1.blogspot.com/2009/07/upnp-actoin-invoke.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/4476908351077096677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5094259393086141803/posts/default/4476908351077096677'/><link rel='alternate' type='text/html' href='http://qrtt1.blogspot.com/2009/07/upnp-actoin-invoke.html' title='UPnP Actoin Invoke'/><author><name>reader</name><uri>http://www.blogger.com/profile/12010853206488455233</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_EqsVDY1SWMQ/SlMTZ88WQCI/AAAAAAAAARE/nXKpugDpofo/s72-c/upnp-action.png' height='72' width='72'/><thr:total>0</thr:total></entry></feed>
