2.1 GNU的由來與發展
GNU 是由“GNU's Not Unix”所遞規定義出的首字母縮寫語。GNU計划是由Richard Stallman在1983年9月27日公開發起的。它的目標是創建一套完全自由的操作系統。Richard Stallman最早是在net.unix-wizards新聞組上公布該消息,並附帶一份《GNU宣言》解釋為何發起該計划的文章,其中一個重要的理由就是要“重現當年軟件界合作互助的團結精神”。GNU工程已經開發了一個被稱為“GNU”的、對Unix向上兼容的完整的自由軟件系統(free software system)。由Richard Stallman完成的最初的GNU工程的文檔被稱為“GNU宣言”。
每個計算機的使用者都需要一個操作系統;如果沒有自由的操作系統,那么如果他不求助於私有軟件,就甚至不能開始使用一台計算機。所以自由軟件議事日程的第一項就是自由的操作系統。一個操作系統不僅僅是一個內核;它還包括編譯器、編輯器、電子郵件軟件,和許多其他東西。因此,創作一個完整的操作系統是一項十分龐大的工作。由於Unix的全局設計已經得到認證並且廣泛流傳,所以GNU開發者們決定使操作系統與Unix兼容。同時這種兼容性使Unix的使用者可以容易地轉移到GNU上來。
在1991年Linux的第一個版本公開發行時,GNU計划已經完成除操作系統內核之外的大部分軟件,比如GNU Bash,GCC等等。於是Linus的內核和GNU軟件的合理組合,構成了GNU/Linux這一優異的操作系統:一個基於Linux的GNU系統。
GCC(GNU Compiler Collection)是GNU組織開發的一個編譯器。目前支持的語言有 C、C++、Objective-C、Fortran、Java和Ada等。
自由軟件可以走多遠?這沒有限制,除非諸如版權法之類的法律完全地禁止自由軟件。最終的目的是,讓自由軟件完成計算機用戶希望完成的所有工作--從而導致自由軟件的過時。
2.2 編譯器
2.2.1 GCC簡介
Linux系統下的GCC是GNU推出的功能強大、性能優越的多平台編譯器,是GNU的代表作品之一。GCC是可以在多種硬體平台上編譯出可執行程序的超級編譯器,其執行效率與一般的編譯器相比平均效率要高20%~30%。
最初,GCC只是一個C語言編譯器,當時是“GNU C Compiler”的英文縮寫。隨着眾多開發者的加入和GCC自身的發展,如今的GCC已經是一個包含眾多語言的編譯器了,其中包括 C,C++,Ada,Object-C和Java等。所以,GCC的全稱也由原來的“GNU C Compiler”演變為現在的“GNU Compiler Collection”,即GNU編譯器家族的意思。
2.2.2 GCC特點
GCC不僅是GNU/Linux上的標准編譯器,而且它也是嵌入式系統開發的標准編譯器,這是因為GCC支持各種不同的目標架構。本書將專注於FPGA平台的嵌入式系統開發,其中的軟件部分運行在Microblaze或者PowerPC處理器上,為了使我們的應用程序能夠運行在不同的目標機上,我們使用交叉編譯工具對程序進行交叉編譯。所謂交叉編譯就是在某個主機平台上(比如PC上)編譯出可在其他平台上(比如ARM上)運行代碼的過程。GCC提供了40種不同的結構體系。其中包括X86,RS6000,Arm,PowerPC等等,用戶可以根據實際的項目平台來進行應用程序的開發。
GCC編譯器能將C、C++語言源程序、匯編程序和目標程序編譯、鏈接成可執行文件,如果沒有給出可執行文件的名字,GCC將生成一個名為a.out的文件。在Linux系統中,可執行文件沒有統一的后綴,系統從文件的屬性來區分可執行文件和不可執行文件。而GCC則通過后綴來區別輸入文件的類別,下面我們來介紹GCC所遵循的部分約定規則:
.c為后綴的文件,C語言源代碼文件;
.a為后綴的文件,是由目標文件構成的檔案庫文件;
.C、.cc或.cxx 為后綴的文件,是C++源代碼文件;
.h為后綴的文件,是程序所包含的頭文件;
.i 為后綴的文件,是已經預處理過的C源代碼文件;
.ii為后綴的文件,是已經預處理過的C++源代碼文件;
.m為后綴的文件,是Objective-C源代碼文件;
.o為后綴的文件,是編譯后的目標文件;
.s為后綴的文件,是匯編語言源代碼文件;
.S為后綴的文件,是經過預編譯的匯編語言源代碼文件。
2.2.3 GCC執行過程
雖然我們稱GCC是C語言的編譯器,但使用GCC由C語言源代碼文件生成可執行文件的過程不僅僅是編譯的過程,而是要經歷四個相互關聯的步驟∶預處理(也稱預編譯,Preprocessing)、編譯(Compilation)、匯編(Assembly)和鏈接(Linking),如圖2-1所示。在對程序進行開發的過程中,我們可以通過添加參數對程序單獨執行其中的某個過程。
圖2-1 GCC執行過程
命令GCC首先調用cpp進行預處理,在預處理過程中,對源代碼文件中的文件包含(include)、預編譯語句(如宏定義define等)進行分析。接着調用cc1或g++進行編譯,這個階段根據輸入文件生成以.o為后綴的目標文件。匯編過程是針對匯編語言的步驟,調用as進行工作,一般來講,.s為后綴的匯編語言文件經過預編譯和匯編之后都生成以.o為后綴的目標文件。當所有的目標文件都生成之后,GCC就調用ld命令來完成最后的關鍵性工作,這個階段就是鏈接,當然,也可以使用GCC命令直接完成鏈接功能。在鏈接階段,所有的目標文件被安排在可執行程序中的恰當的位置,同時,該程序所調用到的庫函數也從各自所在的檔案庫中連到合適的地方。
2.2.4 GCC基本用法與選項
在使用GCC編譯器的時候,我們必須給出一系列必要的調用參數和文件名稱。GCC編譯器的調用參數大約有100多個,但其中多數參數很少會用到,所以這里只介紹其中最基本、最常用的參數。
GCC最基本的用法是∶gcc [options] [filenames] ,其中options就是編譯器所需要的參數,filenames給出相關的文件名稱,表2-1列出了常用參數的意義。
選項 |
解釋 |
-ansi |
只支持 ANSI 標准的 C 語法 |
-c |
只編譯並生成目標文件 |
-DMACRO |
以字符串“1”定義 MACRO 宏 |
-DMACRO=DEFN |
以字符串“DEFN”定義 MACRO 宏 |
-E |
只運行 C 預編譯器 |
-g |
生成調試信息 |
-IDIRECTORY |
指定額外的頭文件搜索路徑DIRECTORY |
-LDIRECTORY |
指定額外的函數庫搜索路徑DIRECTORY |
-lLIBRARY |
連接時搜索指定的函數庫LIBRARY |
-o FILE |
生成指定的輸出文件 |
-O0 |
不進行優化處理 |
-O 或 -O1 |
優化生成代碼 |
-O2 |
進一步優化 |
-O3 |
比 -O2 更進一步優化,包括 inline 函數 |
-static |
禁止使用共享連接 |
-w |
不生成任何警告信息 |
-Wall |
生成所有警告信息 |
表2-1 GCC常用參數
上面我們簡要介紹了GCC編譯器最常用的功能和主要參數選項,更為詳盡的資料可以參考http://gcc.gnu.org/。 假定我們有一個程序名為test.c的C語言源代碼文件,要生成一個可執行文件,最簡單的辦法就是:
gcc test.c
這時,預編譯、編譯鏈接一次完成,生成一個系統預設的名為a.out的可執行文件,對於稍為復雜的情況,比如有多個源代碼文件、需要鏈接檔案庫或者有其他比較特別的要求,就要給定適當的調用選項參數。再看一個簡單的例子。
整個源代碼程序由兩個文件test1.c 和test2.c組成,程序中使用了系統提供的數學庫,同時希望給出的可執行文件為test,這時的編譯命令可以是∶
gcc test1.c test2.c -lm -o test
其中,-lm表示鏈接系統的數學庫libm.a。
2.2.5 Gdb調試器
調試是所有程序員都會面臨的問題。如何提高程序員的調試效率,更好更快的定位程序中的問題從而加快程序開發的進度,是大家共同面對的。就如讀者熟知的Windows下的一些調試工具,如VC自帶的如設置斷點、單步跟蹤等,都受到了廣大用戶的贊賞。那么,在Linux下有什么很好的調試工具呢?
Gdb調試器是一款GNU開發組織並發布的UNIX/Linux下的程序調試工具。雖然,它沒有圖形化的友好界面,但是它強大的功能也足以與微軟的VC工具等媲美。
首先,打開Linux下的編輯器Vi或者Emacs,編輯如下代碼。
/*test.c*/
#include <stdio.h>
int sum(int m);
int main()
{
int i,n=0;
sum(50);
for(i=1; i<=50; i++)
{
n += i;
}
printf("The sum of 1-50 is %d \n", n );
}
int sum(int m)
{
int i,n=0;
for(i=1; i<=m;i++)
n += i;
printf("The sum of 1-m is %d\n", n);
}
在保存退出后首先使用Gcc對test.c進行編譯,注意一定要加上選項”-g”,這樣編譯出的可執行代碼中才包含調試信息,否則之后Gdb無法載入該可執行文件。
[root@localhost Gdb]# gcc -g test.c -o test
雖然這段程序沒有錯誤,但調試完全正確的程序可以更加了解Gdb的使用流程。接下來就啟動Gdb進行調試。注意,Gdb進行調試的是可執行文件,而不是如”.c”的源代碼,因此,需要先通過Gcc編譯生成可執行文件才能用Gdb進行調試。
[root@localhost Gdb]# gdb test
GNU Gdb Red Hat Linux (6.3.0.0-1.21rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb)
可以看出,在Gdb的啟動畫面中指出了Gdb的版本號、使用的庫文件等信息,接下來就進入了由“(gdb)”開頭的命令行界面了。
(1)查看文件
在Gdb中鍵入”l”(list)就可以查看所載入的文件,如下所示:
(Gdb) l
1 #include <stdio.h>
2 int sum(int m);
3 int main()
4 {
5 int i,n=0;
6 sum(50);
7 for(i=1; i<=50; i++)
8 {
9 n += i;
10 }
(Gdb) l
11 printf("The sum of 1~50 is %d \n", n );
12
13 }
14 int sum(int m)
15 {
16 int i,n=0;
17 for(i=1; i<=m;i++)
18 n += i;
19 printf("The sum of 1~m is = %d\n", n);
20 }
可以看出,Gdb列出的源代碼中明確地給出了對應的行號,這樣就可以大大地方便代碼的定位。
(2)設置斷點
設置斷點是調試程序中是一個非常重要的手段,它可以使程序到一定位置暫停它的運行。因此,程序員在該位置處可以方便地查看變量的值、堆棧情況等,從而找出代碼的症結所在。
在Gdb中設置斷點非常簡單,只需在”b”后加入對應的行號即可(這是最常用的方式,另外還有其他方式設置斷點)。如下所示:
(Gdb) b 6
Breakpoint 1 at 0x804846d: file test.c, line 6.
要注意的是,在Gdb中利用行號設置斷點是指代碼運行到對應行之前將其停止,如上例中,代碼運行到第五行之前暫停(並沒有運行第五行)。
(3)查看斷點情況
在設置完斷點之后,用戶可以鍵入”info b”來查看設置斷點情況,在Gdb中可以設置多個斷點。
(Gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804846d in main at test.c:6
(4)運行代碼
接下來就可運行代碼了,Gdb默認從首行開始運行代碼,可鍵入”r”(run)即可(若想從程序中指定行開始運行,可在r后面加上行號)。
(Gdb) r
Starting program: /root/workplace/Gdb/test
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x5fb000
Breakpoint 1, main () at test.c:6
6 sum(50);
可以看到,程序運行到斷點處就停止了。
(5)查看變量值
在程序停止運行之后,程序員所要做的工作是查看斷點處的相關變量值。在Gdb中只需鍵入”p”+變量值即可,如下所示:
(Gdb) p n
$1 = 0
(Gdb) p i
$2 = 134518440
在此處,為什么變量”i”的值為如此奇怪的一個數字呢?原因就在於程序是在斷點設置的對應行之前停止的,那么在此時,並沒有把”i”的數值賦為零,而只是一個隨機的數字。但變量”n”是在第四行賦值的,故在此時已經為零。
(6)單步運行
單步運行可以使用命令”n”(next)或”s”(step),它們之間的區別在於:若有函數調用的時候,”s”會進入該函數而”n”不會進入該函數。因此,”s”就類似於VC等工具中的”step in”,”n”類似與VC等工具中的”step over”。它們的使用如下所示:
(Gdb) n
The sum of 1-m is 1275
7 for(i=1; i<=50; i++)
(Gdb) s
sum (m=50) at test.c:16
16 int i,n=0;
可見,使用”n”后,程序顯示函數sum的運行結果並向下執行,而使用”s”后則進入到sum函數之中單步運行。
(7)恢復程序運行
在查看完所需變量及堆棧情況后,就可以使用命令”c”(continue)恢復程序的正常運行了。這時,它會把剩余還未執行的程序執行完,並顯示剩余程序中的執行結果。以下是之前使用”n”命令恢復后的執行結果:
(Gdb) c
Continuing.
The sum of 1-50 is :1275
Program exited with code 031.
可以看出,程序在運行完后退出,之后程序處於“停止狀態”。
2.3 自動編譯
2.3.1 Make工程管理
到此為止,我們已經了解了如何在Linux下使用編輯器編寫代碼,如何使用Gcc把代碼編譯成可執行文件,還學習了如何使用Gdb來調試程序,那么,所有的工作看似已經完成了,為什么還需要Make這個工程管理器呢?
所謂工程管理器,顧名思義,是指管理較多的文件的。可以試想一下,有一個上百個文件的代碼構成的項目,如果其中只有一個或少數幾個文件進行了修改,按照之前所學的Gcc編譯工具,就不得不把這所有的文件重新編譯一遍,因為編譯器並不知道哪些文件是最近更新的,而只知道需要包含這些文件才能把源代碼編譯成可執行文件。於是,程序員就不能不再重新輸入數目如此龐大的文件名以完成最后的編譯工作。
但是,仔細回想一下本書在2.2.3節中所闡述的編譯過程,編譯過程是分為編譯、匯編、鏈接不同階段的,其中編譯階段僅檢查語法錯誤以及函數與變量的聲明是否正確聲明了,在鏈接階段則主要完成是函數鏈接和全局變量的鏈接。因此,那些沒有改動的源代碼根本不需要重新編譯,而只要把它們重新鏈接進去就可以了。所以,人們就希望有一個工程管理器能夠自動識別更新了的文件代碼,同時又不需要重復輸入冗長的命令行,這樣,Make工程管理器也就應運而生了。
實際上,Make工程管理器也就是個“自動編譯管理器”,這里的“自動”是指它能夠根據文件時間戳自動發現更新過的文件而減少編譯的工作量,同時,它通過讀入Makefile文件的內容來執行大量的編譯工作。用戶只需編寫一次簡單的編譯語句就可以了。它大大提高了實際項目的工作效率,而且幾乎所有Linux下的項目編程均會涉及到它。
2.3.2 Makefile結構
makefile描述了整個工程的編譯規則,通過make命令自動化編譯。
Make是一個解釋makefile 中指令的命令工具,大多數的IDE都有這個命令,比如:
- • Delphi的make,
- • Visual C++的nmake
- • Linux下GNU的make
Makefile是Make讀入的惟一配置文件,因此本節的內容實際就是講述Makefile的編寫規則。在一個Makefile中通常包含如下內容:
· target:是一個目標文件,可以是Object File,也可以是可執行文件,還可以是一個標簽(Label);
· prerequisites:要生成那個target所需要的文件或是目標;
· Command:make需要執行的命令 。
它的格式為:
Target:prerequisites
Command
例如,有兩個文件分別為hello.c和hello.h,創建的目標體為hello.o,執行的命令為gcc編譯指令:gcc –c hello.c,那么,對應的Makefile就可以寫為:
hello.o: hello.c hello.h
gcc –c hello.c –o hello.o
接着就可以使用make了。使用make的格式為:make target,這樣make就會自動讀入Makefile(也可以是首字母小寫makefile)並執行對應target的command語句,並會找到相應的依賴文件。如下所示:
[root@localhost makefile]# make hello.o
gcc –c hello.c –o hello.o
[root@localhost makefile]# ls
hello.c hello.h hello.o Makefile
可以看到,Makefile執行了“hello.o”對應的命令語句,並生成了“hello.o”目標體。
注意:每一個命令的第一個字符必須是“tab”鍵,不可使用8個“space”鍵替代,否則make會顯示出錯信息
2.3.3 makefile變量
上面示例的Makefile在實際中是幾乎不存在的,因為它過於簡單,僅包含兩個文件和一個命令,在這種情況下完全不必要編寫Makefile而只需在Shell中直接輸入即可,在實際中使用的Makefile往往是包含很多的文件和命令的,這也是Makefile產生的原因。下面就可給出稍微復雜一些的Makefile(2個頭文件,5個C文件)進行講解:
edit : main.o kbd.o
cc -o edit main.o kbd.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
clean :
rm edit main.o kbd.o
在這個Makefile中有三個目標體(target),分別為edit、main.o和kbd.o,其中第一個目標體的依賴文件就是后兩個目標體。如果用戶使用命令“make edit”,則make管理器就是找到edit目標體開始執行。
這時,make會自動檢查相關文件的時間戳。首先,在檢查“main.o”、“kbd.o”和“edit”三個文件的時間戳之前,它會向下查找那些把“main.o”或“kbd.o”做為目標文件的時間戳。比如,“main.o”的依賴文件為:“main.c”、“defs.h”。如果這些文件中任何一個的時間戳比“main.o”新,則命令“gcc –Wall –O -g –c main.c -o main.o”將會執行,從而更新文件“main.o”。在更新完“main.o”或“kbd.o”之后,make會檢查最初的“main.o”、“kbd.o”和“edit”三個文件,只要文件“main.o”或“kbd.o”中的任比文件時間戳比“edit”新,則第二行命令就會被執行。這樣,make就完成了自動檢查時間戳的工作,開始執行編譯工作。這也就是Make工作的基本流程。
接下來,為了進一步簡化編輯和維護Makefile,make允許在Makefile中創建和使用變量。變量是在Makefile中定義的名字,用來代替一個文本字符串,該文本字符串稱為該變量的值。在具體要求下,這些值可以代替目標體、依賴文件、命令以及makefile文件中其它部分。在Makefile中的變量定義有兩種方式:一種是遞歸展開方式,另一種是簡單方式。
遞歸展開方式定義的變量是在引用在該變量時進行替換的,即如果該變量包含了對其他變量的應用,則在引用該變量時一次性將內嵌的變量全部展開,雖然這種類型的變量能夠很好地完成用戶的指令,但是它也有嚴重的缺點,如不能在變量后追加內容(因為語句:CFLAGS = $(CFLAGS) -O在變量擴展過程中可能導致無窮循環)。
為了避免上述問題,簡單擴展型變量的值在定義處展開,並且只展開一次,因此它不包含任何對其它變量的引用,從而消除變量的嵌套引用。
遞歸展開方式的定義格式為:VAR=var
簡單擴展方式的定義格式為:VAR:=var
Make中的變量使用均使用格式為:$(VAR)
變量名是不包括“:”、“#”、“=”結尾空格的任何字符串。同時,變量名中包含字母、數字以及下划線以外的情況應盡量避免,因為它們可能在將來被賦予特別的含義。
變量名是大小寫敏感的,例如變量名“foo”、“FOO”、和“Foo”代表不同的變量。
推薦在makefile內部使用小寫字母作為變量名,預留大寫字母作為控制隱含規則參數或用戶重載命令選項參數的變量名。
下面給出了上例中用變量替換修改后的Makefile,這里用OBJS代替main.o和kbd.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。這樣在以后修改時,就可以只修改變量定義,而不需要修改下面的定義實體,從而大大簡化了Makefile維護的工作量。
經變量替換后的Makefile如下所示:
OBJS = main.o kbd.o
CC = cc
edit : $(OBJS)
$(CC) $(OBJS) -o edit
main.o : main.c defs.h
$(CC) -c main.c
kbd.o : kbd.c defs.h command.h
$(CC) -c kbd.c
可以看到,此處變量是以遞歸展開方式定義的。
Makefile中的變量分為用戶自定義變量、預定義變量、自動變量及環境變量。如上例中的OBJS就是用戶自定義變量,自定義變量的值由用戶自行設定,而預定義變量和自動變量為通常在Makefile都會出現的變量,其中部分有默認值,也就是常見的設定值,當然用戶可以對其進行修改。
預定義變量包含了常見編譯器、匯編器的名稱及其編譯選項。下表2-2列出了Makefile中常見預定義變量及其部分默認值。
命 令 格 式 |
含 義 |
AR |
庫文件維護程序的名稱,默認值為ar |
AS |
匯編程序的名稱,默認值為as |
CC |
C編譯器的名稱,默認值為cc |
CPP |
C預編譯器的名稱,默認值為$(CC) –E |
CXX |
C++編譯器的名稱,默認值為g++ |
FC |
FORTRAN編譯器的名稱,默認值為f77 |
RM |
文件刪除程序的名稱,默認值為rm –f |
ARFLAGS |
庫文件維護程序的選項,無默認值 |
ASFLAGS |
匯編程序的選項,無默認值 |
CFLAGS |
C編譯器的選項,無默認值 |
CPPFLAGS |
C預編譯的選項,無默認值 |
CXXFLAGS |
C++編譯器的選項,無默認值 |
FFLAGS |
FORTRAN編譯器的選項,無默認值 |
表2-2 Makefile中常見預定義變量
可以看出,上例中的CC和CFLAGS是預定義變量,其中由於CC沒有采用默認值,因此,需要把“CC=Gcc”明確列出來。
由於常見的Gcc編譯語句中通常包含了目標文件和依賴文件,而這些文件在Makefile文件中目標體的一行已經有所體現,因此,為了進一步簡化Makefile的編寫,就引入了自動變量。自動變量通常可以代表編譯語句中出現目標文件和依賴文件等,並且具有本地含義(即下一語句中出現的相同變量代表的是下一語句的目標文件和依賴文件)。下表2-3列出了Makefile中常見自動變量。
命 令 格 式 |
含 義 |
$* |
不包含擴展名的目標文件名稱 |
$+ |
所有的依賴文件,以空格分開,並以出現的先后為序,可能包含重復的依賴文件 |
$< |
第一個依賴文件的名稱 |
$? |
所有時間戳比目標文件晚的依賴文件,並以空格分開 |
$@ |
目標文件的完整名稱 |
$^ |
所有不重復的依賴文件,以空格分開 |
$% |
如果目標是歸檔成員,則該變量表示目標的歸檔成員名稱 |
表2-3 Makefile中常見自動變量
自動變量的書寫比較難記,但是在熟練了之后會非常的方便,請讀者結合下例中的自動變量改寫的Makefile進行記憶。
OBJS = main.o kbd.o
CC = cc
edit : $(OBJS)
$(CC) $^ -o $@
main.o : main.c defs.h
$(CC) -c $< -o $@
kbd.o : kbd.c defs.h command.h
$(CC) -c $< -o $@
另外,在Makefile中還可以使用環境變量。使用環境變量的方法相對比較簡單,make在啟動時會自動讀取系統當前已經定義了的環境變量,並且會創建與之具有相同名稱和數值的變量。但是,如果用戶在Makefile中定義了相同名稱的變量,那么用戶自定義變量將會覆蓋同名的環境變量。
2.3.4 makefile規則
Makefile的規則是Make進行處理的依據,它包括了目標體、依賴文件及其之間的命令語句。一般的,Makefile中的一條語句就是一個規則。在上面的例子中,都顯示地指出了Makefile中的規則關系,如“$(CC) $(CFLAGS) -c $< -o $@”,但為了簡化Makefile的編寫,make還定義了隱式規則和模式規則,下面就分別對其進行講解。
1.隱式規則
隱含規則能夠告訴make怎樣使用傳統的技術完成任務,這樣,當用戶使用它們時就不必詳細指定編譯的具體細節,而只需把目標文件列出即可。Make會自動搜索隱式規則目錄來確定如何生成目標文件。如上例就可以寫成:
OBJS = main.o kbd.o
CC = cc
edit: $(OBJS)
$(CC) $^ -o $@
main.o : main.c defs.h
kbd.o : kbd.c defs.h command.h
為什么可以省略后兩句呢?Make具有自動推導文件以及文件依賴關系后面的命令,沒必要在每一個.o文件后寫同名的.c文件,以及編譯命令,此既是make的“隱式規則”。
下表2-4給出了常見的隱式規則目錄:
對應語言后綴名 |
規 則 |
C編譯:.c變為.o |
$(CC) –c $(CPPFLAGS) $(CFLAGS) |
C++編譯:.cc或.C變為.o |
$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) |
Pascal編譯:.p變為.o |
$(PC) -c $(PFLAGS) |
Fortran編譯:.r變為-o |
$(FC) -c $(FFLAGS) |
表2-4 Makefile中常見隱式規則目錄
2.模式規則
模式規則是用來定義相同處理規則的多個文件的。它不同於隱式規則,隱式規則僅僅能夠用make默認的變量來進行操作,而模式規則還能引入用戶自定義變量,為多個文件建立相同的規則,從而簡化Makefile的編寫。
模式規則的格式類似於普通規則,這個規則中的相關文件前必須用“%”標明。使用模式規則修改后的Makefile的編寫如下:
OBJS = main.o kbd.o
CC = cc
edit: $(OBJS)
$(CC) $^ -o $@
%.o : %.c
$(CC) -c $< -o $@
2.3.5 makefile規則
使用make管理器非常簡單,只需在make命令的后面鍵入目標名即可建立指定的目標,如果直接運行make,則建立Makefile中的第一個目標。
此外make還有豐富的命令行選項,可以完成各種不同的功能。下表2-5列出了常用的make命令行選項。
命令格式 |
含 義 |
-C dir |
讀入指定目錄下的Makefile |
-f file |
讀入當前目錄下的file文件作為Makefile |
-i |
忽略所有的命令執行錯誤 |
-I dir |
指定被包含的Makefile所在目錄 |
-n |
只打印要執行的命令,但不執行這些命令 |
-p |
顯示make變量數據庫和隱含規則 |
-s |
在執行命令時不顯示命令 |
-w |
如果make在執行過程中改變目錄,則打印當前目錄名 |
表2-5 make的命令行選項
2.3.6使用autotools
Makefile可以幫助make完成它的使命,但要承認的是,編寫Makefile確實不是一件輕松的事,尤其對於一個較大的項目而言更是如此。那么,有沒有一種輕松的手段生成Makefile而同時又能讓用戶享受make的優越性呢?本節要講的autotools系列工具正是為此而設的,它只需用戶輸入簡單的目標文件、依賴文件、文件目錄等就可以輕松地生成Makefile了,這無疑是廣大用戶的所希望的。另外,這些工具還可以完成系統配置信息的收集,從而可以方便地處理各種移植性的問題。也正是基於此,現在Linux上的軟件開發一般都用autotools來制作Makefile。
autotools是系列工具,讀者首先要確認系統是否裝了以下工具(可以用which命令進行查看)。
aclocal
autoscan
autoconf
autoheader
automake
使用autotools主要就是利用各個工具的腳本文件以生成最后的Makefile。其總體流程是這樣的:
使用aclocal生成一個“aclocal.m4”文件,該文件主要處理本地的宏定義;
改寫“configure.scan”文件,並將其重命名為“configure.in”,並使用autoconf文件生成configure文件。
用戶不再需要定制不同的規則,而只需要輸入簡單的文件及目錄名即可,這樣就大大方便了用戶的使用。下面的圖2-1總結了上述過程:
圖2-1 autotools生成Makefile流程圖
2.4 版本控制
版本控制(Revision Control),也被稱為Version Control (System)或(Source) Code Management,用來管理同一信息單元的不同版本。它常用於軟件開發過程中,用來管理諸如源代碼、文檔或其它被整個開發人員所共有的資源,藉以在開發的過程中,確保由不同人所編輯的同一檔案都得到更新。版本控制會記錄所有對源代碼或文檔的改動,並會用一個數字來加以標記,這個標記被稱為版本(revision)。例如:一種簡單的版本控制形式如下:最初的版本指定為“1”,當做了改變之后,版本編號增加為“2”,以此類推。
2.4.1版本管理模型
1.集中式模型
傳統的版本管理系統都是采用集中式模型,即所有的版本控制活動都發生在一個共享的中心版本庫上。如果兩個開發者在同一時間下嘗試更改同一份文件,並且沒有采取有效措施,那么他們很可能會互相覆蓋各自的改動。我們有兩種方法來解決這種情況,根據解決方法的不同,集中式版本管理系統又分為兩種模型:文件鎖定和版本合並。
1) 文件鎖定
解決上述這個問題最簡單的方法就是在一個時間段里版本庫的一個文件只允許被一個人修改。首先在修改之前,先到的人會“鎖定”這個文件,那么另一個人就無法修改這個文件,在先到的人釋放這個“鎖”之前,他只能閱讀文件。這又被稱為“鎖定-修改-解鎖”模型。早期的VSS就是使用這種模型。
鎖定-修改-解鎖模型的缺點就是限制太多:
l 如果一個人獲得了“鎖”,而他長時間沒有釋放“鎖”,那么其他開發者就無法進行正常開發。
l 鎖定可能導致無必要的線性化開發,如果兩個開發者對同一文件的修改根本不沖突,他們完全可以同時對代碼進行修改。
2)版本合並
許多版本控制系統,如CVS、Subversion,允許許多開發者在同一時間對同一文件進行修改。第一個提交的開發者在更改完后“提交”到中心版本庫時總是成功的。在其它開發者提交的時候,版本控制系統有能力能把所有的修改合並進中心版本庫。但是,如果有兩個開發者的修改重疊了,那么就會引起“沖突”,這時你就會看到一組沖突集,你可以選擇修改自己的代碼或和其他開發者進行協商來解決沖突。
2.分布式模型
和集中式模型的客戶-服務器的方法相反,分布式模型采用一種點對點的方法。通常的集中式管理系統,如 CVS,Subversion 已經得到廣泛應用,但是集中式的管理存在相應的缺陷,例如對唯一的版本庫過分依賴:一旦不能正常連接到集中式的版本庫,整個系統陷入癱瘓。分布式模型最大的能力就在於可以維護分布式的版本庫,分散的開發人員可以通過分布式版本管理建立遠程的 CVS,Subversion,P4 協議的版本庫鏡像,選擇工作在自己合適的鏡像版本庫,這個鏡像甚至可以是本地的,整個工作可以離線進行,然后在需要的時候同步鏡像版本庫到主版本庫。目前,采用分布式模型的版本管理系統有Bazaar、Git、Mercurial等。
2.4.2常用術語介紹
在具體介紹版本管理之前,首先介紹一下在進行版本管理時常用的術語:
l 分支(Branch): 在一個時間點,復制一份處於版本控制之下的文件,從這之后,這兩份拷貝就可以獨立的互不干擾的進行各自開發。
l 取出(Check-out): 一次“取出”,就是在本地創建一份倉庫的工作拷貝。
l 提交(Commit): 一次“提交”,將本地的修改寫回到倉庫或合並到倉庫。
l 沖突(Conflict): 當開發者們同時提交對同一文件的修改,而且版本系統不能把它們合並到一起,就會引起沖突,就需要人工來進行合並。
l 匯出(Export): 匯出和取出非常相似,只是匯出的文件不再處於版本控制之下,常用於發布代碼。
l 匯入(Import): 匯入就是在第一次的時候,把本地的文件拷貝到倉庫中,使它們處於版本控制之下。
l 合並(Merge): 合並就是把所有對文件的修改統一到文件里
l 倉庫(Repository): 倉庫就是當前的和歷史的處於版本控制之下的文件所在的地方,通常在服務器端。
l 工作版本(Working copy):從檔案庫中取出一個本地端的復制,所有在檔案庫中的檔案更動,都是從一個工作版本中修改而來的,這也是這名稱的由來。
2.4.3 CVS的使用
1. CVS在服務器端安裝:
CVS服務器端可以工作在linux或windows下。windows下CVS服務器端軟件為cvsnt。 RH linux通常情況下缺省安裝了CVS,
可以在終端輸入:
> rpm –qa | grep ‘cvs’ 來檢查是否安裝了cvs。
如果返回cvs版本號則表示已經安裝。
若未安裝cvs可以使用如下命令安裝cvs包:
>rpm –ivh /mnt/cdrom/RedHat/RPMS/cvs-x.x.x-x.i386.rpm
創建一個目錄作為cvsserver的根目錄。
>mkdir -p /home/cvs/cvsroot
2. 初始化cvs 服務器,在系統中建立一個cvs用戶組:
>groupadd pjt_faceit
在系統中增加一個用戶:
>useradd zhaofeng
並用passwd修改初始密碼。
>passwd zhaofeng
修改cvs根目錄的所有者和權限。
>chown zhaofeng.pjt_faceit /home/cvs/cvsroot/ -R
>chown 775 /home/cvs/cvsroot/ -R
初始化cvs服務器:
>cvs –d /home/cvs/cvsroot/ init
初始化完成后,/home/cvs/cvsroot下生成CVSROOT目錄,里面包含cvs服務器端的配置文件。
在系統服務中添加cvs服務:
使用文本編輯器打開/etc/services 查找如下內容
cvspserver 2401/tcp
cvspserver 2401/udp
注意:2401是cvs默認的通訊端口,tcp和udp為cvs所啟用的網絡服務。可以設定為其它閑置端口,並在客戶端做同樣設定。
如果沒有上述內容則在文件中添加上述語句。
3.使用xinetd啟用cvs服務
將cvs服務添加進xinetd,並設置啟動參數:
在/etc/xinetd.d 目錄下添加cvs服務的啟動文件,文件名cvs或cvspserver。RH-linux通常包含一個名為cvs的文件,只需檢查內容,並按自己要求加以修改。
文件內容:
service cvspserver
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/bin/cvs
server_args = -f --allow-root=/home/cvs/cvsroot pserver
log_on_failure += USERID
disable = no
}
注意: 這里-allow-root參數的值應該和前面創建的cvsserver的根目錄一致。否則后面login時會出現: no such repository的錯誤。如果系統本來就含有cvs配置文件,通常這個目錄需要修改。flags的標志是用於setsockopt設置socket的一些屬性,這里的REUSE表示socket關閉后可以立即重用,而不用等到超時后才能重用。如果配置文件已經存在一般只需要將disable的值修改為no,默認多為yes。server_args后的-f與–allow-root之間要用space分隔,否則login時會出錯。
重新啟動xinetd服務
>/sbin/service xinetd restart
或 /etc/init.d/xinetd restart
注意:若xinetd沒有安裝,在重起服務時提示xinetd not recognized service。輸入:yum install xinetd 安裝xinetd服務,或者掛入RH的CD,使用rpm –ihv XXXX 安裝
檢查cvspserver服務是否已經啟動
輸入:
>netstat –l | grep cvspserver
應該有如下結果,表明cvs服務器安裝成功:
tcp 0 0 *:cvspserver *:* LISTEN
4.設置CVSROOT環境變量
>export CVSROOT=:pserver:zhaofeng:123456@localhost:/home/cvs/cvsroot
>cvs login
輸入剛才設定的密碼登入服務器端,如果沒有錯誤提示,則安裝成功。
5. CVS的日常使用
l 取出文件到工作目錄
cvs checkout project_name
將在當前目錄建立project_name的工作目錄
l 將所有文件同步到最新的版本
cvs update
l 提交修改寫入到CVS庫里
cvs commit -m "write some comments here" file_name
-m 參數指定這次提交的注釋
l 添加文件
創建好新文件后,比如:touch new_file
cvs add new_file
注意:對於圖片,Word文檔等非純文本的項目,需要使用cvs add –kb
選項按2進制文件方式導入(k表示擴展選項,b表示binary),否則有可能出現文件被破壞的情況。
比如:
cvs add -kb new_file.gif
cvs add -kb readme.doc
然后提交修改並注釋
cvs ci -m "write some comments here"
l 刪除文件
將某個源文件物理刪除掉,比如:rm file_name
cvs rm file_name
然后確認修改並注釋
cvs ci -m "write some comments here"
以上面前2步合並的方法為:
cvs rm -f file_name
cvs ci -m "why delete file"
注意:很多cvs命令都有縮寫形式:commit=>ci; update=>up; checkout=>co/get; remove=>rm;
l 添加目錄
cvs add dir_name
l 查看修改歷史
cvs log file_name
cvs history file_name
l 查看當前文件不同版本的區別
cvs diff -r1.3 -r1.5 file_name
查看當前文件(可能已經修改了)和庫中相應文件的區別
cvs diff file_name
l 恢復舊版本的方法:
cvs update -p -r1.2 file_name >file_name
恢復到版本號1.2
l 移動文件/文件重命名
cvs里沒有cvs move或cvs rename,因為這兩個操作是可以由先cvs remove old_file_name,然后cvs add new_file_name實現的。
l 匯出源代碼文件
cvs export
l 給代碼打上tag
cvs tag prj_rc1
2.4.4 Subversion
1.Subversion簡介
Subversion是一個自由/開源的版本控制系統。Subversion管理文件和目錄,而且會記錄所有對文件和目錄的改動。於是我們就可以籍此將數據回復到以前的版本,並可以查看數據更改的歷史。正因為如此,許多人認為版本控制系統是一種神奇的“時間機器”。
Subversion 的版本庫可以通過網絡訪問,從而使用戶可以在不同的電腦上進行操作。從某種意義上來說,這種讓用戶能在各自地方修改和管理同一組數據的能力促進了團隊協作。因為不再是像在一個管子里流動,開發進度會進展迅速。此外,由於所有的工作都已版本化,也就不必擔心由於錯誤的修改而影響軟件質量—如果出現不正確的修改,僅僅要做的就是撤銷那次修改。
Subversion是一個通用系統,可以處理任何類型的文件集。對你來說,這些文件只可能是源程序—而對別人,則可能是一個貨物清單或者是數字電影。
Subversion將很多新特性引入到版本控制領域。為了更好的理解Subversion的好處,我們常常拿它和CVS作比較:
l 版本化目錄
CVS只跟蹤單個文件的歷史,但是Subversion實現的“虛擬”版本化文件系統則一直跟蹤所有對目錄樹作的修改。在Subversion中,文件和目錄都是版本化的。
l 真實的版本歷史
由於只跟蹤單個文件的變更,CVS無法支持如文件拷貝和改名這些常見的操作--這些操作改變了目錄的內容。同樣,在CVS中,目錄下的文件只要名字相同就擁有相同的歷史,即使這些同名文件在歷史上毫無關系。而在Subversion中,可以對文件或目錄進行增加、拷貝和改名操作,也解決了同名而無關的文件之間的歷史聯系問題。
l 原子提交
一系列相關的修改,要么全部提交到版本庫,要么一個也不提交。這樣可以防止出現部分修改提交到版本庫中,而另一部分則沒有的情況。
l 版本化的元數據
每一個文件和目錄都有自己的一組屬性--鍵和它們的值。你可以根據需要建立並存儲任何鍵/值對。和文件本身的內容一樣,屬性也在版本控制之下。
l 可選的網絡層
Subversion在版本庫的訪問的實現上具有較高的抽象程度,利於人們實現新的網絡訪問機制。Subversion可以作為一個擴展模塊嵌入到 Apache之中。這種方式在穩定性和交互性方面有很大的優勢,可以直接使用服務器的成熟技術—認證、授權和傳輸壓縮等等。此外,Subversion自身也實現了一個輕型的,可獨立運行的服務器軟件。這個服務器使用了一個特定的協議,這個協議可以輕松的用SSH封裝。
l 一致的數據操作
Subversion用一個二進制差異算法描述文件的變化,對於文本(可讀)和二進制(不可讀)文件其操作方式是一致的。這兩種類型的文件壓縮存儲在版本庫中,而差異信息則在網絡上雙向傳遞。
l 高效的分支和標簽操作
在Subversion中,分支與標簽操作的開銷與工程的大小無關。Subversion的分支和標簽操作用只是一種類似於硬鏈接的機制拷貝整個
工程。因而這些操作通常只會花費很少且相對固定的時間。
1)倉庫(repository)
Subversion也是使用傳統的集中式管理模型,它的核心是“倉庫(repository)”。倉庫里存放了所有的數據,所有的修改最終都被提交到倉庫,所有的客戶端都是從“倉庫”讀取數據的。如下圖2.4.4.1.1所示:
圖2.4.4.1.1 一個典型的客戶端/服務器端系統
2)修訂版本(revision)
一次“提交”操作就是一次原子操作,它將所有對任意數量文件和目錄的修改提交到版本庫里。Subversion努力保持原子性以應對程序錯誤、系統錯誤、網絡問題和其他用戶行為, 確保一次“提交”要么所有的修改全部提交上去,要么倉庫沒有發生改變。每當版本倉庫接受了一次提交,那么文件系統進入了一個新的狀態,叫做一次修訂(revision),每一個修訂版本都被賦予一個獨一無二的自然數,自然增長。版本倉庫的初始修訂號是0,只創建了一個空目錄,沒有任何內容。可以形象的把版本庫看作一系列樹,想象有一組修訂號,從0開始,從左到右,每一個修訂號有一個目錄樹掛在它下面,每一個樹好像是一次提交后的版本庫“快照”。如下圖2.4.4.1.3所示:
圖2.4.4.1.3 版本倉庫
2. Subversion快速入門
Subversion本身只是一個命令行工具集,我們常用到的Subversion的兩個命令是:
- svn -- 命令行的 Subversion 客戶端程序
- svnadmin -- 該工具用於創建、調整、以及修補 Subversion 存儲庫
1)安裝
第一個步驟就是安裝Subversion,Windows用戶可以到Subversion的官方網站下載安裝包: http://subversion.tigris.org/。如果你是使用Linux操作系統,大部分的發行版都會提供rpm包或deb包,請自行安裝。下面以Ubuntu為例,安裝Subversion:
sudo apt-get install subversion |
Windows用戶還需把Subversion的命令行工具集添加到PATH環境變量里,這樣就可以在命令行(cmd)下使用:
|
svn和svnadmin命令行有許多的選項和子命令,但是svn有個非常優秀的幫助系統,讓我們不用擔心不知道命令怎么用。我們可以用svn help,來查看svn支持的命令和選項。如果想知道的更多,譬如想知道svn mkdir怎么用,可以使用svn help mkdir,就可以獲得詳細的關於svn mkdir的幫助信息。下面的示例,都以在Windows環境下為准。
2)創建一個倉庫
我們第一個要用到的命令就是svnadmin,這個命令行工具是Subversion的管理工具,常用來創建倉庫、備份等等。下面我們來創建一個倉庫,首先打開命令行,在Windows下,點擊開始,然后運行,輸入“cmd”,確定。
|
這樣,我們就創建了一個倉庫,你的所有的文件、工程信息和版本信息都會存放在倉庫里。所以倉庫非常重要,你最好將倉庫建立在一個安全的地方。
3)導入文件
現在我們有了倉庫,下一步我們將使用svn工具來導入工程。首先,在倉庫里創建一個文件夾:
|
Subversion會打開你的默認文本編輯器,你可以輸入你的日志消息。
下一步,導入你的工程到倉庫。改變你的目錄到你的工程目錄myproj,創建三個空文件夾trunk、tag和branch,如果你的工程目錄已經有工程文件了,把他們全部剪切到trunk文件夾。創建trunk、tag和branch三個文件夾,是遵循一個約定俗成的規矩,就是trunk下放的是當前正在開發的版本,tag下放的是里程碑版本或是你自己打標簽的版本,而branch下放的是分支版本,即用來測試新的想法、新的特性等等,都可以放在branch目錄下,這樣就可以在不影響主開方版本的情況下測試新的特性,等新特性成熟了,就可以“合並(merge)”到主開發版中。
|
這樣就會把myproj下的文件和文件夾導入到倉庫中。現在你的所有工程文件都在repo/myproj/trunk下了。
4)取出、修改、提交
首先,我們需要取出工程文件,在本地創建一個“工作副本”,然后就可以修改、添加等,首先改變目錄到你希望創建“工作副本”的目錄,下面所有的操作都是在“工作副本”目錄下。
|
這樣就會創建一個myproj的文件夾,里面包含所有的工程文件。這個myporj是處於Subversion的管理之下的,你可以放心的將以前的沒有處於Subversion管理之下的文件夾myproj徹底刪除了。
如果,我想查看一個倉庫里的內容,可以:
|
在我們對“工作副本”作了些修改,並且想保存這個時候的狀態時,我們就需要把修改“提交”到倉庫了。注意,沒有提交到倉庫里的修改,在將來的某個時候,我們是無法將它恢復的。
|
但是,如果我們在“工作副本”里添加了新的文件,並且,我們也希望它也被提交到倉庫里,那么僅僅“svn commit”是不夠的。
|
5)同步
如果同時有其他開發者一起開發的化,你需要經常更新你的“工作版本”,以便把其他開發者提交的修改更新到你的工作版本中。
svn update #在修改前,盡可能先同步倉庫,防止和其它開發者沖突 svn update –r R #更新到指定的revision |
查看文件或目錄的歷史:
svn log svn log <file name> svn log <directory nam> |
3.TortoiseSVN介紹
TortoiseSVN是Subversion版本控制系統在Windows下的一個免費開源客戶端,它最大的特色是和Windows資源管理器緊密結合,讓使用版本控制系統變得和操作普通文件夾一樣簡單。TortoiseSVNl另一個最直觀的功能就是圖標覆蓋圖,我們可以很清楚地看到各個文件的狀態。如圖2.4.4.3-1所示。我們可以到http://tortoisesvn.tigris.org/獲得最新的版本。
圖2.4.4.3-1 Subversion管理下各個文件的圖標
安裝好TortoiseSVN后,在系統的任意地方右擊右鍵,就會出現TortoiseSVN的菜單,如果我們右擊已經處於源代碼管理下的文件夾,就會出現如update、commit等命令菜單,就可以直接對這個文件夾進行源代碼管理。如下圖2.4.4.3-2和2.4.4.3-3所示。
圖2.4.4.3-2 TortoiseSVN菜單 圖2.4.4.3-3 處於源代碼管理下文件夾的菜單
1) Tortoise日常使用
(1)創建一個倉庫
我們仍從創建倉庫開始。首先,在你想要創建倉庫的位置新建一個文件夾myrepo,右擊這個文件夾,選擇TortoiseSVNàCreate repository here…,在彈出的對話框里選擇Native filesystem(FSFS),點擊OK!倉庫創建好了。不再需要敲擊命令行,不需要記住命令,完完全全的Windows操作方式,通過敲擊鼠標完成所有的操作。
(2)導入文件
在創建好倉庫后,我們很自然的需要把原先的工程導入到倉庫里。右擊要導入的文件夾譬如prj,選擇TortoiseSVNàImport…,在彈出的對話框中,通過瀏覽文件夾的方式選擇倉庫,還可以在Import message里寫上信息,點擊OK完成。注意:TortoiseSVN導入的是你右擊的這個文件夾里面的內容,不包括這個文件夾本身。
(3)取出文件
從倉庫里取出文件同樣簡單。在你想要創建工作目錄的地方,右擊空白地方,選擇SVN Checkout…,在彈出的對話框里完成操作。
(4)更新文件等
在取出工作版本后,以后所有的源代碼管理都可以通過右擊工作目錄或工作文件並點擊相應的命令,非常方便。就不在一一敘述。
4. Google源代碼托管服務
前面介紹了,通過TortoiseSVN,我們可以很方便對自己的文檔、工程進行管理。下面,將簡單介紹一下如何通過Google提供的源代碼托管服務對整個項目組的工程進行管理。
在互聯網流行的今天,不同地理位置的人需要協同起來對同一個項目進行開發的事非常普遍,因此,我們就需要一個能在互聯網上創建倉庫的地方,然后所有的開發者就可以通過互聯網來訪問倉庫、更新倉庫。Google源代碼托管服務提供的正是這樣一個功能。你可以通過http://code.google.com/hosting/來訪問Google提供的這項服務。所有擁有Google帳號的用戶都可以使用這項服務。
在http://code.google.com/hosting/頁面點擊Create a new project來創建一個項目。在項目創建好后,如果要添加其它開發者,在項目面板下選AdministeràProject Members加入他們的Google帳號。在工程創建好后,屬於你這個工程的SVN倉庫也就創建好了。在項目面板下選Source,可以看到兩條svn checkout語句,其中使用https協議的是給開發者用的,其它的人使用http協議。如果你安裝了Subversion命令行,你可以在Windows下直接用命令行,輸入Source面板里的svn checkout語句來取出工程。當然在Windows下,更方便的是使用我們前面介紹的TortoiseSVN圖形客戶端,在任意位置右擊,選擇svn Checkout…,在彈出的對話框里填上https或http網址,如果使用https網址,會要求你輸入密碼,直接點擊Source面板上的googlecode.com password,就可以獲得密碼。這樣我們就把Google倉庫里的工程取出到了本地(一開始是空的),然后你在這個文件夾里進行添加、修改等操作,最后就可以把他們提交到Google倉庫里。這樣其它人在更新的時候就會獲得你的修改。這些都和前面提到的操作沒有什么不同。