php-gd擴展庫交叉編譯
runsisi AT hust
@2012/10/05
為了給一做web的同事解決交叉編譯的問題,長假的第一天看了下,所以有了這個東東~
1. 編譯環境
OS: LinuxDeepin 12.06 (based on ubuntu 12.04) 32bit(以下的討論亦只限于GNU/Linux環境下)
HW: Intel Pentium processor T4300, DDRII 667 3GB
編譯器:arm-linux-gnueabi-gcc,理論上其他powerpc,mingw等GCC均可以
Src version:
Php: v5.4.7 zlib: v1.2.7 libpng: v1.5.12 libjpeg: v8d
2. 交叉編譯和本地編譯的區別
1)輸入
相同,同樣是那些源代碼,配置腳本
2)輸出
不同,交叉編譯的輸出一般為在其他cpu架構/系統上(更確切的說是具有不同ABI接口的系統上)運行或加載的可執行文件或庫,它的輸出一般情況下不能在交叉編譯器運行的平臺下運行或被加載
3)工具
不能簡單的說相同或者不同,對于編譯鏈接等用到的工具鏈肯定不同,例如在我的機器上裝了四個針對不同目標平臺的gcc編譯器:
①runsisi@runsisi-Aspire-4736Z:~/Desktop$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.6/lto-wrapper
Target: i686-linux-gnu
②runsisi@runsisi-Aspire-4736Z:~/Desktop$ arm-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabi/4.6/lto-wrapper
Target: arm-linux-gnueabi
③COLLECT_GCC=/opt/eldk-5.2.1/powerpc/sysroots/i686-eldk-linux/usr/bin/powerpc-linux/powerpc-linux-gcc
COLLECT_LTO_WRAPPER=/opt/eldk-5.2.1/powerpc/sysroots/i686-eldk-linux/usr/libexec/powerpc-linux/gcc/powerpc-linux/4.6.4/lto-wrapper
Target: powerpc-linux
④runsisi@runsisi-Aspire-4736Z:~/Desktop$ i686-w64-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=i686-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-w64-mingw32/4.6/lto-wrapper
Target: i686-w64-mingw32
注意每一個打印的最后一行”Target“,表明該編譯器的輸出是針對該平臺的。
編譯用到的工具除了編譯工具鏈之外,還有configure腳本等(當然對于不是使用autotools進行源代碼發布的開源工程,本段就沒有討論的意義了),對于交叉編譯而言,configure腳本需要使用不同的參數進行調用,有兩個最重要的參數:1)指定交叉編譯器,可以在執行configure腳本之前執行export CC=arm-linux-gnueabi-gcc,或者像這樣:./configure CC=arm-linux-gnueabi-gcc作為configure腳本參數進行指定;2)由于交叉編譯出來的可執行文件在當前的平臺無法運行,所以另外一個參數是告訴configure腳本,我們當前是要進行交叉編譯,一些需要通過編譯測試程序然后運行測試程序才能得到編譯參數的操作就不要進行了,當然還有其他一些編譯參數可能針對不同的平臺有默認值,該指定交叉編譯目標平臺的參數以configure腳本參數的形式進行指定,如:./configure –-host=arm-linux,具體—host之后指定的目標平臺是arm-linux, powerpc-linux還是別的貌似不是太重要,只要不是當前編譯器所在平臺可以運行的平臺就可以了,但是我們還是針對自己需要的目標平臺指定為好
4)gcc默認搜索路徑
gcc編譯器在編譯和調用ld進行鏈接的時候會去默認路徑下搜索頭文件和庫文件,可以使用gcc的 -print-search-dirs參數打印當前的默認搜索路徑,如:
runsisi@runsisi-Aspire-4736Z:~/Desktop$ i686-w64-mingw32-gcc -print-search-dirs
install: /usr/lib/gcc/i686-w64-mingw32/4.6/
programs: =/usr/lib/gcc/i686-w64-mingw32/4.6/:/usr/lib/gcc/i686-w64-mingw32/4.6/:/usr/lib/gcc/i686-w64-mingw32/:/usr/lib/gcc/i686-w64-mingw32/4.6/:/usr/lib/gcc/i686-w64-mingw32/:/usr/lib/gcc/i686-w64-mingw32/4.6/../../../../i686-w64-mingw32/bin/i686-w64-mingw32/4.6/:/usr/lib/gcc/i686-w64-mingw32/4.6/../../../../i686-w64-mingw32/bin/
libraries: =/usr/lib/gcc/i686-w64-mingw32/4.6/:/usr/lib/gcc/i686-w64-mingw32/4.6/../../../../i686-w64-mingw32/lib/i686-w64-mingw32/4.6/:/usr/lib/gcc/i686-w64-mingw32/4.6/../../../../i686-w64-mingw32/lib/../lib/:/usr/lib/gcc/i686-w64-mingw32/4.6/../../../../i686-w64-mingw32/lib/
從打印可以看出,交叉編譯器并不會從當前系統目錄下搜索頭文件和庫,所以不需要擔心在交叉編譯器會搞混。注意:如果在CFLAGS中使用—sysroot指定root的位置的話,用戶自定義的-I,-L都會以--sysroot指定的root為root
3. gd擴展庫編譯方式
對于編譯gd擴展庫,存在兩種方法,下面分別說明:
1)直接和php集成,編譯進最終的php可執行文件中,在php v4.x.x版本之后php的源代碼里面就帶了gd擴展庫的源代碼(在源代碼樹ext/gd下面)(具體版本為多少不關心,我也不是搞php的,反正v5.x.x肯定是這樣的~),在執行./configure編譯php時帶上—with-gd的選項就表明將gd編譯進php,不過php官方說gd庫還依賴libpng和libjpeg,而libpng又依賴libz,所以在./configure的選項中必須同時指定這三個庫的位置,以下為我的編譯選項:
runsisi@runsisi-Aspire-4736Z:~/Desktop/php/php-5.4.7$ export CC=arm-linux-gnueabi-gcc
runsisi@runsisi-Aspire-4736Z:~/Desktop/php/php-5.4.7$./configure --prefix=/home/runsisi/target --with-gd --disable-libxml --disable-dom --with-zlib-dir=/home/runsisi/target --with-jpeg-dir=/home/runsisi/target --with-png-dir=/home/runsisi/target --host=arm-linux --disable-simplexml --disable-xml --disable-xmlreader --disable-xmlwriter --without-pear –disable-phar
注意五個用特殊顏色標記出來了的部分,紫色部分指定交叉編譯器;粉紅色—prefix指定make install時php被安裝到的位置,這個非常重要,如果不指定,會安裝到系統默認的/usr/local下面,而我們交叉編譯的php在當前系統是根本無法執行的,這會把系統的php搞亂,所以一定要指定自己的安裝路徑;綠色的—with-gd表示我們要集成gd庫;深紅色的選項指定zlib,jpeg,png三個庫的位置,注意這三個庫也必須是使用交叉編譯器編譯出來的庫;藍色的—host表明我們現在在進行交叉編譯;其他選項不要問我,我只是為了在編譯時不出現找不到相應庫的錯誤所以屏蔽掉了那些擴展,具體這些擴展是做什么我也不懂~,最后一個–disable-phar選項簡單說下,如果不屏蔽掉,在make最后階段會出錯,解決方法是使用本機的php去打包(具體打包什么我也不知道,makefile里面用到了phar這個擴展功能),因為交叉編譯的php不可能運行,所以肯定會出錯,phar應該就是個和jar,rar類似的功能,屏蔽掉應該也沒什么問題。
2)既然gd庫只是個擴展,那么肯定可以單獨編譯,這應該就是網上所說的什么追加方式進行編譯。gd的官網都已經掛了,那么肯定直接使用php維護的gd進行編譯。
編譯的步驟如下:①切換到php源代碼目錄ext/gd下面,執行php安裝目錄下的phpize ②執行configure,make一系列操作,在ext/gd/modules目錄下就有gd.so存在了,如果在configure時指定prefix為php的安裝目錄,那么make install就會把gd的頭文件和gd.so都拷貝到php安裝目錄下去.
所有人都說在編譯之前需要執行phpize這個腳本,但是從來沒人說為什么,其實這個腳本做的就是得到一些宏定義,為擴展庫生成configure腳本等,注意要打開這個文件,對最開始兩行的prefix和datarootdir根據當前php實際所在的位置進行修改,比如我當初是把php make install安裝在/home/runsisi/target下面,但是后來我把它移到/home/runsisi/Desktop/php-target下面了,那就要做下面相應的修改:
orig:
prefix='/home/runsisi/target'
datarootdir='/home/runsisi/target/php'
modified:
prefix='/home/runsisi/Desktop/php-target'
datarootdir='/home/runsisi/Desktop/php-target/php'
4. 幾點討論
1)使用直接集成gd庫的方法時,zlib,png,jpeg庫可以是靜態庫,也可以是動態庫,但單獨編譯gd庫時zlib,png,jpeg必須全部是動態庫,因為此時gd被編譯成動態庫,具體原因我這點linker&loader的知識還不夠解釋:),但拿windows下生成庫的種類來看,可以知道具體鏈接什么庫是很很有講究的。。。
windows下vc生成庫時,一般有如下幾種選擇:
靜態庫/動態庫 Ⓧ debug版/release版 Ⓧ 靜態鏈接/動態鏈接C運行時庫
2)在php的Makefile中有一處宏定義CFLAGS_CLEAN硬編碼成了-I/usr/include -g -O2 -fvisibility=hidden,這個地方可能需要修改,其實把-I/usr/include這句去掉都無所謂;在EXTRA_LDFLAGS_PROGRAM后面要加上-ldl,不然會鏈接出錯
3)查看交叉編譯好的程序依賴什么庫,由于沒有現成的ldd,可以使用其他工具,如:
runsisi@runsisi-Aspire-4736Z:~/Desktop/target/bin$ arm-linux-gnueabi-readelf -d php | grep NEEDED
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
4)靜態庫的鏈接順序很重要,如果liba依賴libb,請以這樣指定鏈接順序:-la -lb
5)如果靜態庫的鏈接存在循環依賴問題,請使用ld的--start-group archives –end-group選項
5. 具體的編譯步驟,分上述的兩種方式進行
1)集成進php的方式(默認都動態鏈接zlib,png,jpeg等庫,如果目錄下同時存在靜態庫和動態庫,gcc會默認選擇靜態庫進行鏈接)(假設php,libz,libpng,libjpeg的源代碼壓縮包都在同一個目錄下)
首先設置交叉編譯器(以arm gcc為例)
$ export CC=arm-linux-gnueabi-gcc
①編譯zlib
$ tar xvzf zlib-1.2.7.tar.gz
$ cd zlib-1.2.7/
$ ./configure –prefix=/home/runsisi/target
出現錯誤:
./ztest8154: 1: ./ztest8154: Syntax error: word unexpected (expecting ")")
Looking for a four-byte integer type... Not found.
由于zlib并非使用的標準autotools,沒有考慮交叉編譯的情況,這個錯誤是執行測試程序導致的,用于檢測32bit整型是int還是long還是別的,該錯誤無關緊要,忽略即可
$ make
$ make install
②編譯libpng
$ cd ..
$ tar xvzf libpng-1.5.12.tar.gz
$ cd libpng-1.5.12/
注意使用CFLAGS指定zlib的位置
$ ./configure --prefix=/home/runsisi/target CFLAGS="-I/home/runsisi/target/include -L/home/runsisi/target/lib" –host=arm-linux
$ make
$ make install
③編譯libjpeg
$ cd ..
$ tar xvzf jpegsrc.v8d.tar.gz
$ cd jpeg-8d/
$ ./configure --prefix=/home/runsisi/target –host=arm-linux
$ make
$ make install
④編譯php
$ cd ..
$ tar xvf php-5.4.7.tar
$ ./configure --prefix=/home/runsisi/target --with-gd --disable-libxml --disable-dom --with-zlib-dir=/home/runsisi/target --with-jpeg-dir=/home/runsisi/target --with-png-dir=/home/runsisi/target --host=arm-linux --disable-simplexml --disable-xml --disable-xmlreader --disable-xmlwriter --without-pear –disable-phar
打開php-5.4.7/Makefile
修改67行附近CFLAGS_CLEAN = -I/usr/include -g -O2 -fvisibility=hidden為:
CFLAGS_CLEAN = -g -O2 -fvisibility=hidden
修改76行附近EXTRA_LDFLAGS_PROGRAM = -L/home/runsisi/target/lib為:
EXTRA_LDFLAGS_PROGRAM = -L/home/runsisi/target/lib -ldl
$ make
$ make install
⑤將/home/runsisi/target目錄下php及依賴的動態庫libz,libpng,libjpeg等打包好即可
查看php依賴的動態庫如下:
runsisi@runsisi-Aspire-4736Z:~/target/bin$ arm-linux-gnueabi-readelf -d php | grep NEEDED
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [libpng15.so.15]
0x00000001 (NEEDED) Shared library: [libz.so.1]
0x00000001 (NEEDED) Shared library: [libjpeg.so.8]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
整個安裝目錄(/home/runsisi/target)下的文件如下:
.
├── bin
│ ├── cjpeg
│ ├── djpeg
│ ├── jpegtran
│ ├── libpng15-config
│ ├── libpng-config -> libpng15-config
│ ├── php
│ ├── php-cgi
│ ├── php-config
│ ├── phpize
│ ├── rdjpgcom
│ └── wrjpgcom
├── include
│ ├── jconfig.h
│ └── ...
├── lib
│ ├── libjpeg.a
│ ├── libjpeg.la
│ ├── libjpeg.so -> libjpeg.so.8.4.0
│ ├── libjpeg.so.8 -> libjpeg.so.8.4.0
│ ├── libjpeg.so.8.4.0
│ ├── libpng15.a
│ ├── libpng15.la
│ ├── libpng15.so -> libpng15.so.15.12.0
│ ├── libpng15.so.15 -> libpng15.so.15.12.0
│ ├── libpng15.so.15.12.0
│ ├── libpng.a -> libpng15.a
│ ├── libpng.la -> libpng15.la
│ ├── libpng.so -> libpng15.so
│ ├── libz.a
│ ├── libz.so -> libz.so.1.2.7
│ ├── libz.so.1 -> libz.so.1.2.7
│ ├── libz.so.1.2.7
│ ├── php
│ │ └── build
│ │ ├── acinclude.m4
│ │ ├── config.guess
│ │ ├── config.sub
│ │ ├── libtool.m4
│ │ ├── ltmain.sh
│ │ ├── Makefile.global
│ │ ├── mkdep.awk
│ │ ├── phpize.m4
│ │ ├── run-tests.php
│ │ ├── scan_makefile_in.awk
│ │ └── shtool
│ └── pkgconfig
│ ├── libpng15.pc
│ ├── libpng.pc -> libpng15.pc
│ └── zlib.pc
├── php
│ └── ...
└── share
└── ...
42 directories, 309 files
2)單獨編譯gd擴展庫的方式
①首先設置交叉編譯器
②libz,libpng,libjpeg同樣需要按照第一種方式進行交叉編譯
③假設libz,libpng,libjpeg安裝在/home/runsisi/libs-target中,而php編譯make install時的位置在/home/runsisi/target中,但后來移動至/home/runsisi/Desktop/php-target中
④打開/home/runsisi/Desktop/php-target/bin/phpize,在第4行附近,修改:
prefix='/home/runsisi/target'
datarootdir='/home/runsisi/target/php'
為:
prefix='/home/runsisi/Desktop/php-target'
datarootdir='/home/runsisi/Desktop/php-target/php'
⑤打開/home/runsisi/Desktop/php-target/bin/php-config,
第4行附近,修改:
prefix="/home/runsisi/target"
datarootdir="/home/runsisi/target/php"
為:
prefix="/home/runsisi/Desktop/php-target"
datarootdir="/home/runsisi/Desktop/php-target/php"
在第11行附近,修改:
ldflags=" -L/home/runsisi/target/lib"
為指向libz,libpng,libjpeg的安裝位置
ldflags=" -L/home/runsisi/target/libs-target"
在13行附近,修改
extension_dir='/home/runsisi/target/lib/php/extensions/no-debug-non-zts-20100525'
為:
extension_dir='/home/runsisi/Desktop/php-target/lib/php/extensions/no-debug-non-zts-20100525'
⑥開始編譯gd擴展庫
$ tar xvf php-5.4.7.tar
$ cd php-5.4.7/ext/gd
$ /home/runsisi/Desktop/php-target/bin/phpize
$ ./configure --prefix=/home/runsisi/Desktop/php-target --host=arm-linux --with-php-config=/home/runsisi/Desktop/php-target/bin/php-config --with-zlib-dir=/home/runsisi/libs-target --with-png-dir=/home/runsisi/libs-target --with-jpeg-dir=/home/runsisi/libs-target
$ make
$ make install
查看gd.so依賴的動態庫如下:
runsisi@runsisi-Aspire-4736Z:~/Desktop/php-target/lib/php/extensions/no-debug-non-zts-20100525$ arm-linux-gnueabi-readelf -d gd.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libpng15.so.15]
0x00000001 (NEEDED) Shared library: [libz.so.1]
0x00000001 (NEEDED) Shared library: [libjpeg.so.8]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
php-target下面的文件如下:
.
├── bin
│ ├── php
│ ├── php-cgi
│ ├── php-config
│ └── phpize
├── include
│ └── ...
├── lib
│ └── php
│ ├── build
│ │ ├── acinclude.m4
│ │ ├── config.guess
│ │ ├── config.sub
│ │ ├── libtool.m4
│ │ ├── ltmain.sh
│ │ ├── Makefile.global
│ │ ├── mkdep.awk
│ │ ├── phpize.m4
│ │ ├── run-tests.php
│ │ ├── scan_makefile_in.awk
│ │ └── shtool
│ └── extensions
│ └── no-debug-non-zts-20100525
│ └── gd.so
└── php
└── ...
37 directories, 263 files
/Files/runsisi/php-gd安裝.pdf