2011年9月12日 星期一

Mac 強制螢幕直接進入睡眠模式

打算離開電腦前一陣子,想直接讓螢幕休眠,可以直接按 Shift + Control + Eject。

另外一個方法是設定螢幕保護程式的螢幕熱點,螢幕四個角落隨便挑一個設定成強制螢幕進入睡眠模式。

Mac 電腦光碟無法退片

使用mac常碰到光碟片沒辦法退片的問題,這個文章圖文並茂提供了好幾個解決方法。
http://ken-macosx.blogspot.com/2010/12/mac_02.html

1. 利用磁碟工具程式來退片。
2. 直接安裝 DiscEject 來退片。
3. 利用燒錄程式來退片,例如 Toast 跟 Recorder。
4. 直接按右上角的退片鈕。
5. 開機按著滑鼠左鍵,或是觸控板下方。

2010年12月9日 星期四

讓 VIM 記住上次游標的位置

如果安裝的vim每次開檔案的時候,游標都不在上次編輯的位置,那請編輯檔案 /etc/vim/vimrc 。

將下面這幾行前的註解拿掉
" Uncomment the following to have Vim jump to the last position when
" reopening a file
if has("autocmd")
au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif

2010年12月4日 星期六

GNU Makefile 入門

常使用 linux/unix 平台來發展軟體的程式撰寫者常常需要使用 Makefile 來管理和編輯程式碼,在這邊就簡單介紹一下。

會用到Makefile通常就是程式碼越寫越大,檔案數越來越多,為了編譯方便而使用 Makefile 來管理。下面的例子先從沒有Makefile的方法來寫程式,然後開始使用。

開始發展程式內容

先寫一個C程式 test.c 如下
// The content of test.c
#include <stdio.h>
int main(int argc,char **argv)
{
printf( Hello World!\n );
return 0;
}

// The content of a.c
void a(void)
{
printf( Hi there, it s a good day.\n );
}
怎麼編譯?
一般使用cc compiler 的使用方法如下:
cc {CFLAGS} -c -o {cfile.o} {cfile.c}
cc -o {executive_file} {objfile1.o} {objfile2.o}
按照 cc compiler 的使用發法,編譯test.c 的方法和執行結果如下:
[~/code]# cc -c -o test.o test.c

[~/code]# cc -o test test.o
[~/code]# ./test
Hello World!

第一個 makefile

加入檔案Makefile內容如下,在這邊特別注意一下,指令前方要用Tab來空格
# Comment # , This is a Makefile
# Notice that the space before cc must be a TAB, NOT four spaces !!
test:
[TAB]cc -o test test.o
目前我們已經有兩個檔案 test.c 和 Makefile了,在命令列直接打make指令,就會參考Makefile內容來編譯test.c ,執行內容如下:
[~/code]# make

cc -c -o test1.o test1.c
cc -o test1 test1.o
指令 make 會先找目前目錄底下的 Makefile,然後找 makefile ,否則會顯示以下錯誤訊息。
make: *** No targets specified and no makefile found. Stop.
你也可以指定makefile檔案名稱,請依照以下方法:
make -f {Your Makefile Name}
指令 make 後面可以指定規則 rule ,規則指的是 makefile 裡的執行項目。
make rule1 rule2

規則初探 (Rule)

規則 (rule) 的格式寫在 makefile 裡頭,格式如下,注意所有指令前方都要用一個tab來空格:

RuleName: Dependent_Rule1 Dependent_Rule2
[TAB]Your_Command_Here

假設我們寫一個 makefile 內容如下,包含兩個規則,分別只印出一則訊息。
rule1:
    @echo "Hello Rule1"
rule2: rule1
    @echo "Hello Rule2"
執行結果
[~/code]# make
Hello Rule1
[~/code]# make rule2
Hello Rule1
Hello Rule2
參照結果,你會發現 (1) 如果 make 指令不指定規則,那就會找檔案開始的第一個規則,(2) 執行目標規則之前,會先執行相依的規則。所以 make rule1 就只先執行 rule1的命令,而make rule2 則會先跑 rule1的指令再跑 rule2。

使用變數

為了方便起見,makefile裡頭也可以使用變數,在 makefile裡頭變數是字串的集合,字串之間用空白分隔。因此變數可以擁有零個、一個到多個字串。

VARIABLE = STR1 STR2 STR3

定義一個變數CC,並在規則中使用。
# It s a Makefile. Define and use variable.
CC = gcc
# First Rule
# Second Rule with one dependency
test.o:
    $(CC) -c -o test.o test.c
test: test.o
    $(CC) -o test test.o
執行結果
[~/code] # make test
gcc -c -o test.o test.c
gcc -o test test.o
[~/code] # make test.o
gcc -c -o test.o test.c
[~/code] # make
gcc -c -o test.o test.c

make test 執行了第二個規則(test)前,先執行相依規則(test.o)。當然我們也可以個別執行每個規則。

這邊我們注意到變數的使用是在變數名稱前加上錢字號 $,但是因為變數名稱是多個字母組合在一起,所以使用時用左右括號把名稱包起來。如果沒用左右括號則 $ 號後只會把第一個字母認作變數名稱。如下面範例所示,$AB 其實就是把變數A改成內容字串a加上後面的B,輸出的結果變成aB。
# 變數使用範例
A = a
AB = ab
all:
    @echo "$AB"
    @echo "$(AB)"
執行結果
[~/code] # make
aB
ab
makefile中也有一些用在規則內的自動變數,我列出三個比較常用的,等等會有範例展示用法。

$@ 表示目前的規則名稱
$< 表示第一個相依規則名稱
$^ 表示所有的相依規則名稱


萬用字元(wildcard)

makefile 中所用的萬用字元是 % ,代表所有可能的字串,前後可以接指定的字串來表示某些固定樣式的字串。例如%.c 表示結尾是 .c 的所有字串。因此我們改寫 makefile 如下
# Using pattern
CC = gcc
OBJS = a.o b.o c.o

all: test

%.o: %.c
    $(CC) -c -o $@ $<

test: $(OBJS)
    $(CC) -o test $^
當我們執行make,執行all然後是test,相依的規則是OBJS變數指定的字串 a.o b.o c.o,因此會尋找 a.o b.o c.o 這三個規則去執行,而 %.o 這個規則符合需求。執行結果如下:
[~/code] # make
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -c -o c.o c.c
gcc -o test a.o b.o c.o
這個例子中剛好也使用到自動變數 $@ $< $^ ,請參考前面的定義。

內建變數和規則(Implicit Variables and Rules)

因為makefile是為了幫助我們編譯程式,所以有些制式變數和規則就直接內建了,我們不需要在makefile裡面言明。如果想要覆蓋原本的定義,那麼在makefile中用相同的名稱來定義自己的變數和規則就可以了。
# 一個 implicit rule 例子
%.o:%.c
    $(CC) $(CFLAGS) -c -o $@ $<

# 一些內建變數
AR = ar
AS = as
CC = cc
CXX = g++
RM = rm -f
ARFLAGS = rv
CFLAGS =
CXXFLAGS =

使用函數(Functions)

當然makefile也提供了一些function可以使用,這邊提供幾個常用的。
wildcard 使用方法如下,注意的是wildcard裡面的參數就跟我們在命令列用的一樣,用*.c 表示所有.c結尾的檔名。請看下面範例,filelist1 等於所有 .c 結尾的檔名,例如 "a.c b.c c.c",但是變數 filelist2 中,實際表示的是字串 "*.c" ,
# function $(wildcard *.c)
# filelist1 would be "test1.c test2.c test3.c ...
# List all .c files in current directory
filelist1 = $(wildcard *.c)
# filelist2 would be "*.c"
filelist2 = *.c

Function patsubst 字串名稱取代
# Find matched strings in variable “filelist1” and change them into strings end with “.o”
# objsfilelist1 would be “test1.o test2.o test3.o …”
objsfilelist1 := $(patsubst %.c,%.o,$(filelist1))
# This statement is equal to the previous one
objsfilelist2 := $(filelist1:%.c=%.o)

Function subst 一樣是取代
# Set VPATH as our included paths. All paths are sperated by “;”
VPATH_STR = /mips/uclib.mips;/mips/ffmpeg/include;~/

# Find “;” in VPATH_STR and change them into “ “
# VPATH would be “/mips/uclib.mips /mips/ffmpeg/include ~/”
VPATH = $(subst ;, ,$(VPATH_STR))

# INCLUDES would be “-I/mips/uclib.mips -I/mips/ffmpeg/include -I~/”
INCLUDES= $(patsubst %,-I%, $(VPATH))

Example

觀摩範例是學習最好的方法,這邊是一個Makefile範例。

寫一個 Makefile 編譯目錄中所有 .c 檔,並且針對編譯出的.o檔 objdump(輸出反組譯資訊)。
# Makefile example. Compile all .c files and build execution file
QUIET = @
ECHO = echo
CC = gcc
RM = rm -rf
OBJDUMP= objdump -S –l

CFLAGS = -Os
LDFLAGS = -lm
TARGET = a.out
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
OBJSDUMP_LOG= $(patsubst %.c,%.objdump,$(wildcard *.c))

all: info $(TARGET)

$(TARGET): $(OBJS) $(OBJSDUMP_LOG)
    $(QUIET)$(ECHO) "Building " $@
    $(QUIET)$(CC) -o $@ $(OBJS) $(LDFLAGS)

%.o: %.c
    $(QUIET)$(ECHO) "Compliling " $<
    $(QUIET)$(CC) $(CFLAGS) -c $< -o $@

%.objdump: %.o
    $(QUIET)$(ECHO) "Objdumping " $<
    $(QUIET)$(OBJDUMP) $< > $@

info:
    $(QUIET)$(ECHO) "CC=$(CC)"
    $(QUIET)$(ECHO) "INFO: CFLAGS=$(CFLAGS)"
    $(QUIET)$(ECHO) "LDFLAGS=$(LDFLAGS)"

clean:
    $(QUIET)$(RM) $(OBJS) $(TARGET)
執行結果
[~/code] make
CC=gcc
INFO: CFLAGS=-Os
LDFLAGS=-lm
Compliling main.c
Compliling test1.c
Compliling test2.c
Objdumping main.o
Objdumping test1.o
Objdumping test2.o
Building a.out

參考資料
gnu make

2010年11月8日 星期一

Mount LVM partition

如果想在ubuntu下掛載LVM的磁區,請照以下步驟作,依序安裝所需套件。
sudo -i
apt-get install lvm2
modprobe dm-mod
vgchange -a y

2010/11/10補充:
依據Heero的解說,LVM (Logical Volume Manager) 是可以將多個磁區視為一個單一的磁區的一種方法,並不是磁區的一種類別。

2010年10月26日 星期二

原始碼 diff and patch

開發專案的時候常常需要協同合作,可以不用把所有原始碼寄給對方,只把寄自己修改過的內容就可以。下面是一個操作實例,修正內容我們可以用diff 指令產生,而對方用 patch 把你修正的內容加入原始碼。


1. 自己開發的程式內容
檔案列表如下
test/Makefile
test/test.c
test.c 內容如下
#include <stdio.h>
int main(int argc, char **argv)
{
    printf("Hello World!\n");
    return 0;
}
2. 打包寄給協同開發者
將 test 整個打包
tar czvf test.tgz test/
3. 協同開發者開始更改
tar zxvf test.tgz
cp test test1 -rf
檔案列表如下
test1/Makefile
test1/test.c
test1/README
README 內容如下
Hello! It's README file.
修改 test.c 內容如下
#include <stdio.h>
int main(int argc, char **argv)
{
        printf("Hello World!\n");
        printf("Hello c program\n");

        return 0;
}
4. 產生 patch 檔
語法如下
diff -Naur [from-file] [to-file] > [YourFileName.patch]
-N    In  directory comparison, if a file is found in only one directory, treat it as present but empty in the other directory.
-a    Treat  all  files as text and compare them line-by-line, even if they do not seem to be text.
-u    Use the unified output format.
-r    When comparing directories, recursively compare any subdirectories found.
依我們的情況下指令如下,產生 test.patch 檔
diff -Naur test test1 > test.patch
test.patch 內容如下
diff -Naur test/README test1/README
--- test/README 1970-01-01 08:00:00.000000000 +0800
+++ test1/README        2010-06-09 16:01:32.000000000 +0800
@@ -0,0 +1 @@
+Hello! It's README file.
diff -Naur test/test.c test1/test.c
--- test/test.c 2010-06-09 16:01:06.000000000 +0800
+++ test1/test.c        2010-06-09 16:00:25.000000000 +0800
@@ -3,5 +3,6 @@
 int main(int argc, char **argv)
 {
        printf("Hello World!\n");
+       printf("Hello c program\n");
        return 0;
 }
將 test.patch 寄回給原開發者
5. 開始 patch
patch 語法如下
patch -p0 < [patchfile]
將之前的test.tgz解開
tar zxvf test.tgz
patch -p0 < test.patch
[~]# patch -p0 < test.patch
patching file test/README
patching file test/test.c
6. 移除 patch 內容
再作一次patch即可,程式會詢問你
或者也可以增加參數 -R 來回覆修改
patch -p0 < test.patch
或是
patch -R -p0 < test.patch
[~]# patch -p0 < test.patch
The next patch would create the file test/README,
which already exists!  Assume -R? [n] y
patching file test/README
patching file test/test.c
Reversed (or previously applied) patch detected!  Assume -R? [n] y


終端機下 mount windows 網路芳鄰資料夾

將windows分享資料夾(或是用samba分享的linux資料夾)mount成一個資料夾,如此一來登錄一個Server,就能連結全世界!!

假設你的分享路徑及使用者密碼如下:
//jimmy-desktop/share
username: public
password: public

1. 建立空目錄給 mount 用,假設我mount在/mnt/share。
mkdir /mnt/share
2. 下指令,記得linux路徑要把倒斜線改成斜線,指令如下
# mount -t cifs [Windows/Samba分享路徑] [Mount資料夾] -o user=USERNAME
# -o 後接選項,填上帳號跟密碼。
mount -t cifs //jimmy-desktop/share /mnt/share -o user=public -o password=public
3. 成功! 資料夾 share 就是我自己電腦分享的資料夾。

2010/11/04 補充說明:
如果想要開機順便就mount起來,那麼可以參考下面作法。

#依照前面範例,修改 /etc/fstab,在檔案最後加上下面設定。
//jimmy-desktop/share /mnt/share smbfs user=public,password=public, 0 0

2011/02/09 補充說明:
如果在mount的時候發現 wrong fs type 錯誤訊息,請安裝cifs-utils套件。


參考資料:
1. mount.cifs
2. http://www.automaticable.com/2008-01-18/how-to-mount-a-network-drive-in-ubuntu/