Go語(yǔ)言。他主要是在一些網(wǎng)頁(yè)版的服務(wù)器中用于系統(tǒng)編程的一種語(yǔ)言。他是谷歌開(kāi)發(fā)的一種編程語(yǔ)言。在一定程度上,谷歌有一定的壟斷作用。不能隨隨便便的在語(yǔ)言當(dāng)中添加其他的語(yǔ)言成分。
公司主營(yíng)業(yè)務(wù):網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)公司推出元氏免費(fèi)做網(wǎng)站回饋大家。
SQLite是ANSI-C的源代碼。在使用之前必須要編譯成機(jī)器碼。這篇文章是用于各種編譯SQLite方法的指南。
這篇文章不包含編譯SQLite的每個(gè)步驟的反饋,那樣可能會(huì)困難因?yàn)槊糠N開(kāi)發(fā)場(chǎng)景都不同。所以這篇文章描述和闡述了編譯Sqlite的原則。典型的編譯命令已經(jīng)作為例子提供了,以期望應(yīng)用開(kāi)發(fā)者能夠使用這些例子作為完成他們自己定制的編譯過(guò)程的的一個(gè)指南。換句話說(shuō),這篇文章提供了想法和見(jiàn)解,而不是交鑰匙的解決方法。
融合VS單獨(dú)源文件
Sqlite是由超過(guò)一百個(gè)c源碼文件以及眾多的目錄下的腳本構(gòu)建的。Sqlite的實(shí)現(xiàn)是純粹的ANSI-C,但是許多C語(yǔ)言源代碼文件是由輔助的C程序生成或者轉(zhuǎn)換來(lái)的,并且AWK,SED和TCL腳本會(huì)融合到完成的sqlite庫(kù)中。對(duì)Sqlite構(gòu)建需要的C程序和轉(zhuǎn)換和創(chuàng)建C語(yǔ)言源碼是一個(gè)復(fù)雜的過(guò)程。
為了簡(jiǎn)化這些,sqlite也通過(guò)一個(gè)預(yù)打包的合并后的源碼文件:sqlite3.c。這個(gè)合并文件是一個(gè)ANSI-C源碼實(shí)現(xiàn)整個(gè)SQLite庫(kù)的唯一文件。合并后的文件更容易處理。所有的東西都包含在這一個(gè)文件里,所以很容易進(jìn)入一個(gè)更大的C或者C++程序的源碼樹(shù)。所有的代碼生成和轉(zhuǎn)換步驟都已經(jīng)實(shí)現(xiàn)了,因此沒(méi)有輔助的C程序需要去配置和變異,也沒(méi)有腳本需要去運(yùn)行。并且,因此所有哭都包含在一個(gè)翻譯單元,編譯器可以做更多高級(jí)的優(yōu)化從而提升5%到10%的性能。因?yàn)檫@些原因,融合后的源碼文件sqlite3.c對(duì)所有程序來(lái)講都是值得推薦的。
推薦所有的應(yīng)用程序使用融合文件。
直接從單獨(dú)的源碼文件中構(gòu)建sqlite當(dāng)然可以,但是并不推薦。對(duì)一些特殊的應(yīng)用程序,可能需要修改構(gòu)建程序去處理使用那些從網(wǎng)站上下載的預(yù)構(gòu)建的源碼文件不能完成的情況。對(duì)于這些情況,推薦構(gòu)建和使用一個(gè)定制過(guò)的合并文件。換句話說(shuō),即使一個(gè)工程需要以單獨(dú)的源碼文件構(gòu)建sqlite,仍然推薦使用一個(gè)融合后的源碼文件作為一個(gè)中間步驟。
編譯命令行接口(CLI)
構(gòu)建命令行接口需要三個(gè)源碼文件:
sqlite3.c:Sqlite融合的源碼文件
sqlite3.h:匹配sqlite3.c以及定義sqlite的c語(yǔ)言接口的頭文件
shell.c:命令行接口程序本身。這個(gè)c源碼文件包含一個(gè)main()的例程和每輪循環(huán)的用戶輸入的提示符并將輸入傳給sqlite數(shù)據(jù)庫(kù)引擎用于處理。
所有的上述源碼的三個(gè)文件都被包含在下載頁(yè)面的amalgamation tarball中。
為了構(gòu)建CLI,簡(jiǎn)單的將這三個(gè)文件放置在相同的目錄下然后一起編譯他們。用MSVC:
cl shell.c sqlite3.c -Fesqlite3.exe
在unix系統(tǒng)上(或者在windows上用cygwin或者mingw+msys)典型的命令會(huì)有些像這樣:
gcc shell.c sqlite3.c -lpthread -ldl
為了SQLite線程安全,需要pthreads庫(kù)。但是因?yàn)镃LI是一個(gè)單線程的,我們可以指示SQLite構(gòu)建一個(gè)非線程安全的庫(kù)并因此護(hù)綠pthreads庫(kù):
gcc -DSQLITE_THREADSAFE=0 shell.c sqlite3.c -ldl
-ldl庫(kù)是在支持動(dòng)態(tài)裝載時(shí)需要,例如sqlite3_load_extension() 接口和load_extension()
SQL function。如果這些特性都不要求,那么我們也可以使用SQLITE_OMIT_LOAD_EXTENSION編譯時(shí)間選項(xiàng)忽略他們。
gcc -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION shell.c sqlite3.c
有人可能想要提供其他的編譯時(shí)間選項(xiàng)(compile-time options),例如SQLITE_ENABLE_FTS3去全文本搜索或者SQLITE_ENABLE_RTREE用于R*樹(shù)搜索引擎擴(kuò)展。而有人將正常指定一些編譯優(yōu)化開(kāi)關(guān)。(預(yù)編譯的CLI可以從選擇sqlite網(wǎng)站上使用“-Os”下載下來(lái))有無(wú)數(shù)種可能的變數(shù)在這里。
關(guān)鍵點(diǎn)在這里:構(gòu)建CLI需要編譯一起兩個(gè)C語(yǔ)言文件。shell.c文件包含入口的定義和用戶輸入的loop,而sqlite融合文件sqlite3.c包含完整的sqlite庫(kù)的實(shí)現(xiàn)。
編譯TCL接口
sqlite的tcl接口是一個(gè)小的模塊被添加到一般的融合文件中。結(jié)果是一個(gè)新的融合后的源碼文件,稱之為“tclsqlite3.c”。這個(gè)源碼文件是生成一個(gè)可以使用TCL
load命令去加載到一個(gè)標(biāo)準(zhǔn)的tclsh或者wish中,或者隨著sqlite構(gòu)建成功生成一個(gè)單獨(dú)唯一的tclsh的共享庫(kù)所需要的。一個(gè)tcl的融合的副本被包含在下載頁(yè)的TEA
tarball中作為一個(gè)文件。
為了生成一個(gè)linux上的sqlite的TCL-loadable庫(kù),下面的命令需要滿足:
gcc -o libtclsqlite3.so -shared tclsqlite3.c -lpthread -ldl -ltcl
不幸的是構(gòu)建Mac OS X 和 Windows的共享庫(kù)并不是如此簡(jiǎn)單。對(duì)于這些平臺(tái)最好使用包含在TEA tarball中的configure腳本和makefile.
為了生成一個(gè)單獨(dú)的tclsh,可以用于sqlite靜態(tài)鏈接,使用如下的編譯器調(diào)用:
gcc -DTCLSH=1 tclsqlite3.c -ltcl -lpthread -ldl -lz -lm
這里的技巧是-DTCLSH=1選項(xiàng)。sqlite的TCL接口模塊包含一個(gè)main的過(guò)程,用于初始化一個(gè)TCL解釋器并在以-DTCLSH=1編譯后進(jìn)入到一個(gè)命令行l(wèi)oop。上述命令可以工作在Linux和Mac
OS X,雖然有時(shí)可能需要依賴于平臺(tái)調(diào)整庫(kù)選項(xiàng)以及編譯的TCL的哪一個(gè)版本。
構(gòu)建融合文件
下載頁(yè)提供的sqlite融合文件的版本對(duì)大多數(shù)用戶來(lái)說(shuō)是足夠的。然而,一些工程可能想要或者需要構(gòu)建他們自己的融合文件。一個(gè)常見(jiàn)的構(gòu)建一個(gè)定制的融合文件的理由是為了使用特定的compile-time options來(lái)定制sqlite庫(kù)?;叵雜qlite融合文件中包含了許多C代碼由輔助程序和腳本生成。許多的編譯時(shí)間選項(xiàng)影響這一成圣代碼而且必須在融合文件組裝前提供給代碼生成器。這一系列必須傳給代碼生成器的編譯時(shí)間相關(guān)的選項(xiàng)會(huì)使得sqlite的發(fā)布版本各不相同,但是在寫(xiě)這邊文章的時(shí)候,代碼生成器需要知道的這組選項(xiàng)包括:
SQLITE_ENABLE_UPDATE_DELETE_LIMIT
SQLITE_OMIT_ALTERTABLE
SQLITE_OMIT_ANALYZE
SQLITE_OMIT_ATTACH
SQLITE_OMIT_AUTOINCREMENT
SQLITE_OMIT_CAST
SQLITE_OMIT_COMPOUND_SELECT
SQLITE_OMIT_EXPLAIN
SQLITE_OMIT_FOREIGN_KEY
SQLITE_OMIT_PRAGMA
SQLITE_OMIT_REINDEX
SQLITE_OMIT_SUBQUERY
SQLITE_OMIT_TEMPDB
SQLITE_OMIT_TRIGGER
SQLITE_OMIT_VACUUM
SQLITE_OMIT_VIEW
SQLITE_OMIT_VIRTUALTABLE
為了構(gòu)建一個(gè)定制的融合文件,先下載原始的獨(dú)立源碼文件到一個(gè)unix或者類unix開(kāi)發(fā)平臺(tái)。確定獲取的原始源碼文件不是“預(yù)編譯過(guò)的源文件”。任何人都可以通過(guò)到下載頁(yè)或者直接從configuration management system.獲取完整的一套原始源碼文件。
假設(shè)sqlite源碼樹(shù)被存在一個(gè)名為“sqlite”的目錄下。計(jì)劃構(gòu)建一個(gè)平行目錄下的名為“bld”的融合文件。首先通過(guò)運(yùn)行sqlite源碼樹(shù)種的configure腳本運(yùn)行或者通過(guò)制作一份源碼樹(shù)頂層的的makfile模板的一份,來(lái)構(gòu)建一個(gè)合適的makefile.然后手動(dòng)編輯這個(gè)Makfile去包含需要的編譯時(shí)間相關(guān)的選項(xiàng)。最終運(yùn)行:
make sqlite3.c
在windows上使用MSVC:
nmake /f Makefile.msc sqlite3.c
sqlite3.c的make
target會(huì)自動(dòng)構(gòu)造一般的“sqlite3.c”合并的源碼文件,以及它的頭文件“sqlite3.h”,和包含TCL接口的融合源碼文件“tclsqlite3.c”。之后,需要的文件可以被拷貝到文件目錄下然后根據(jù)上述勾勒的過(guò)程編譯。
構(gòu)建一個(gè)windows的動(dòng)態(tài)鏈接庫(kù)DLL
為了在windows構(gòu)建一個(gè)sqlite的dll使用,首先獲取對(duì)應(yīng)的融合過(guò)的源碼文件,sqlit3.c和sqlite.h。這些可以從SQLite website上下載或者和上述告知的一樣去定制生成。
使用工作目錄下的源碼文件,一個(gè)dll可以在msvc中使用如下命令生成:
cl sqlite3.c -link -dll -out:sqlite3.dll
上述命令需要運(yùn)行在msvc的MSVC Native Tools Command
Prompt.如何你已經(jīng)在機(jī)器上安裝了msvc,你可能有多個(gè)版本的這種命令提示符,針對(duì)于x86和x64的自帶構(gòu)建的,或者交叉編譯到ARM的。依賴要求的DLL去使用對(duì)應(yīng)合適的命令提示符工具。
如果使用MinGW編譯器,命令是這樣的:
gcc -shared sqlite3.c -o sqlite3.dll
注意MinGW只生成32位的dll。另有一個(gè)分開(kāi)的MinGW64工程可以用來(lái)生成64位的dll??梢酝茢嗥涿钚姓Z(yǔ)法是類似的。需要注意的是最近的MSVC的版本生成的DLLs可能不能工作到WinXP或者更早版本的windows上。因此為了最大限度的兼容你的生成的dll,推薦MinGW。一個(gè)好的經(jīng)驗(yàn)法則是使用MinGW去生成32位的dlls,使用msvc去生成64位的dlls。
Cgo 使得Go程序能夠調(diào)用C代碼. cgo讀入一個(gè)用特別的格式寫(xiě)的Go語(yǔ)言源文件, 輸出Go和C程序, 使得C程序能打包到Go語(yǔ)言的程序包中.
舉例說(shuō)明一下. 下面是一個(gè)Go語(yǔ)言包, 包含了兩個(gè)函數(shù) -- Random 和 Seed -- 是C語(yǔ)言庫(kù)中random和srandom函數(shù)的馬甲.
package rand
/*
#include stdlib.h
*/ import "C" func Random() int { return int(C.random()) } func Seed(i int) { C.srandom(C.uint(i)) }
我們來(lái)看一下這里都有什么內(nèi)容. 開(kāi)始是一個(gè)包的導(dǎo)入語(yǔ)句.
rand包導(dǎo)入了"C"包, 但你會(huì)發(fā)現(xiàn)在Go的標(biāo)準(zhǔn)庫(kù)里沒(méi)有這個(gè)包. 那是因?yàn)镃是一個(gè)"偽包", 一個(gè)為cgo引入的特殊的包名, 它是C命名空間的一個(gè)引用.
rand 包包含4個(gè)到C包的引用: 調(diào)用 C.random和C.srandom, 類型轉(zhuǎn)換 C.uint(i)還有引用語(yǔ)句.
Random函數(shù)調(diào)用libc中的random函數(shù), 然后回返結(jié)果. 在C中, random返回一個(gè)C類型的長(zhǎng)整形值, cgo把它輪換為C.long. 這個(gè)值必需轉(zhuǎn)換成Go的類型, 才能在Go程序中使用. 使用一個(gè)常見(jiàn)的Go類型轉(zhuǎn)換:
func Random() int { return int(C.random()) }
這是一個(gè)等價(jià)的函數(shù), 使用了一個(gè)臨時(shí)變量來(lái)進(jìn)行類型轉(zhuǎn)換:
func Random() int { var r C.long = C.random() return int(r) }
Seed函數(shù)則相反. 它接受一個(gè)Go語(yǔ)言的int類型, 轉(zhuǎn)換成C語(yǔ)言的unsigned int類型, 然后傳遞給C的srandom函數(shù).
func Seed(i int) { C.srandom(C.uint(i)) }
需要注意的是, cgo中的unsigned int類型寫(xiě)為C.uint; cgo的文檔中有完整的類型列表.
這個(gè)例子中還有一個(gè)細(xì)節(jié)我們沒(méi)有說(shuō)到, 那就是導(dǎo)入語(yǔ)句上面的注釋.
/*
#include stdlib.h
*/ import "C"
Cgo可以識(shí)別這個(gè)注釋, 并在編譯C語(yǔ)言程序的時(shí)候?qū)⑺?dāng)作一個(gè)頭文件來(lái)處理. 在這個(gè)例子中, 它只是一個(gè)include語(yǔ)句, 然而其實(shí)它可以是使用有效的C語(yǔ)言代碼. 這個(gè)注釋必需緊靠在import "C"這個(gè)語(yǔ)句的上面, 不能有空行, 就像是文檔注釋一樣.
Strings and things
與Go語(yǔ)言不同, C語(yǔ)言中沒(méi)有顯式的字符串類型. 字符串在C語(yǔ)言中是一個(gè)以0結(jié)尾的字符數(shù)組.
Go和C語(yǔ)言中的字符串轉(zhuǎn)換是通過(guò)C.CString, C.GoString,和C.GoStringN這些函數(shù)進(jìn)行的. 這些轉(zhuǎn)換將得到字符串類型的一個(gè)副本.
下一個(gè)例子是實(shí)現(xiàn)一個(gè)Print函數(shù), 它使用C標(biāo)準(zhǔn)庫(kù)中的fputs函數(shù)把一個(gè)字符串寫(xiě)到標(biāo)準(zhǔn)輸出上:
package print // #include stdio.h // #include stdlib.h import "C" import "unsafe" func Print(s string) { cs := C.CString(s) C.fputs(cs, (*C.FILE)(C.stdout)) C.free(unsafe.Pointer(cs)) }
在C程序中進(jìn)行的內(nèi)存分配是不能被Go語(yǔ)言的內(nèi)存管理器感知的. 當(dāng)你使用C.CString創(chuàng)建一個(gè)C字符串時(shí)(或者其它類型的C語(yǔ)言內(nèi)存分配), 你必需記得在使用完后用C.free來(lái)釋放它.
調(diào)用C.CString將返回一個(gè)指向字符數(shù)組開(kāi)始處的指錯(cuò), 所以在函數(shù)退出前我們把它轉(zhuǎn)換成一個(gè)unsafe.Pointer(Go中與C的void 等價(jià)的東西), 使用C.free來(lái)釋放分配的內(nèi)存. 一個(gè)慣用法是在分配內(nèi)存后緊跟一個(gè)defer(特別是當(dāng)這段代碼比較復(fù)雜的時(shí)候), 這樣我們就有了下面這個(gè)Print函數(shù):
func Print(s string) { cs := C.CString(s) defer C.free(unsafe.Pointer(cs)) C.fputs(cs, (*C.FILE)(C.stdout)) }
構(gòu)建 cgo 包
如果你使用goinstall, 構(gòu)建cgo包就比較容易了, 只要調(diào)用像平常一樣使用goinstall命令, 它就能自動(dòng)識(shí)別這個(gè)特殊的import "C", 然后自動(dòng)使用cgo來(lái)編譯這些文件.
如果你想使用Go的Makefiles來(lái)構(gòu)建, 那在CGOFILES變量中列出那些要用cgo處理的文件, 就像GOFILES變量包含一般的Go源文件一樣.
rand包的Makefile可以寫(xiě)成下面這樣:
include $(GOROOT)/src/Make.inc
TARG=goblog/rand
CGOFILES=\ rand.go\ include $(GOROOT)/src/Make.pkg
然后輸入gomake開(kāi)始構(gòu)建.
更多 cgo 的資源
cgo的文檔中包含了關(guān)于C偽包的更多詳細(xì)的說(shuō)明, 以及構(gòu)建過(guò)程. Go代碼樹(shù)中的cgo的例子給出了更多更高級(jí)的用法.
一個(gè)簡(jiǎn)單而又符合Go慣用法的基于cgo的包是Russ Cox寫(xiě)的gosqlite. 而Go語(yǔ)言的網(wǎng)站上也列出了更多的的cgo包.
最后, 如果你對(duì)于cgo的內(nèi)部是怎么運(yùn)作這個(gè)事情感到好奇的話, 去看看運(yùn)行時(shí)包的cgocall.c文件的注釋吧.
MacBook Linux Windows等系統(tǒng)安裝sqlite3
MacBook Linux 樹(shù)莓派raspberrypi安裝Golang環(huán)境