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