Build Qt 5 MySQL Plugin for Android/zh

From Qt Wiki
Jump to navigation Jump to search

En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh

构建安卓版Qt5 mysql插件原来并不像你希望的那样简单。下面我将介绍完成这个任务的必要步骤。

使用 mariadbclient (选项 1)

设置一般环境

设置以下环境变量:

  1. SR=/path/to/android/ndk/platforms/android-9/arch-arm
  2. BR=/path/to/android/ndk/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin/arm-linux-androideabi-

注意:我一直无法让openssl在x86版的android上编译,因此采用上面的ARMv7变量。如果您成功地让openssl在x86版android上编译,请务必更新这个维基。 :).

先决条件

mysql模块需要编译以下内容:

  1. mysql客户端库。对于mariadb(推荐的mysql实现),你还需要用到:
    1. openssl
    2. libiconv

编译 OpenSSL

我已经用openssl 1.0.1e版本测试过了。这在未来的版本中应该也能使用,但我不能保证。

首先,抓取源码并解压。然后,进入源码目录并执行以下操作。

RANLIB="$BR"ranlib CC="$BR"gcc ./Configure android-armv7 --prefix=$SR/usr
ANDROID_DEV=$SR/usr make
make install

注意: 应该有一种方法可以让openssl只编译和安装库,但我还没有找到合适的make目标。上面的方法可以让openssl编译和安装所有的东西,而不是只安装库。

如果只安装二进制文件的话:

make install_sw

编译 libiconv

我已经用libiconv 1.14版测试过了。同样,这在未来的版本中也应该可以使用,但我不能保证这么多。

首先,抓取源代码并解压。然后,进入源码目录并执行以下操作。

STRIP="$BR"strip RANLIB="$BR"ranlib OBJDUMP="$BR"objdump AR="$BR"ar CC="$BR"gcc CFLAGS=--sysroot=$SR CPP="$BR"cpp CPPFLAGS=$CFLAGS ./configure --build=x86_64 --host=arm --prefix=$SR/usr --with-sysroot=$SR && make install

编译 mariadbclient

经过测试,我发现发布的源码与mysql不兼容(截至1.0.0版本)。它们缺少了Qt mysql插件使用的mysql_library_init函数。然而,bazaar源码与mysql兼容,并包含这个函数。从 bazaar 中抓取 mariadb 源,配置、编译并安装 mariadb 连接器客户端库,如下所述。


bzr branch lp:mariadb-native-client
cd maria-native-client

由于缺少一些pthread例程,mariadb单元测试会失败,并停止编译mariadb。我建议将单元测试注释出来,这样它们就不会阻碍安装。修改基础源目录下的CMakeLists.txt,并注释掉下面一行。

ADD_SUBDIRECTORY(unittest/libmariadb)

更新:从连接器2.0.0版本开始,mariadb与发布的版本完全兼容。如果你下载了2.0.0(或更高版本)的连接器,你可以从这一点开始。你将不需要集市源,也不需要禁用上面的单元测试。

现在你可以通过cmake进行配置。

mkdir build && cd build
PKG_CONFIG_PATH=$SR/usr/lib/pkgconfig cmake -DCMAKE_AR="$BR"ar -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER="$BR"gcc -DCMAKE_C_FLAGS=--sysroot=$SR -DCMAKE_INSTALL_PREFIX=$SR/usr -DCMAKE_LINKER="$BR"ld -DCMAKE_NM="$BR"nm -DCMAKE_OBJCOPY="$BR"objcopy -DCMAKE_OBJDUMP="$BR"objdump -DCMAKE_RANLIB="$BR"ranlib -DCMAKE_STRIP="$BR"strip -DWITH_EXTERNAL_ZLIB=ON -DICONV_INCLUDE_DIR=$SR/usr/include -DICONV_LIBRARIES=$SR/usr/lib/libiconv.a -DZLIB_INCLUDE_DIR=$SR/usr/include -DZLIB_LIBRARY=$SR/usr/lib/libz.so ../

在这一点上,你需要对源码做一些小的修改,使它们能在android上编译。首先,android编译器不知道 "ushort"是什么。所以,在include/my_global.h中的#define _global_h块中定义它。

#ifndef ushort
#define ushort uint16
#endif

现在你已经准备好编译和安装了。

make install

部署

你需要将mariadb共享对象库文件添加到APK镜像的附加库列表中。编译Qt mysql插件后,使用以下命令获取所需文件的准确名称。

"$BR"objdump -p libqsqlmysql.so | grep NEEDED

要正确地创建一个mariadb.so文件,而不是mariadb.so.1,请编辑libmariadb/CMakeLists.txt,并注释出该块。

 SET_TARGET_PROPERTIES(libmariadb PROPERTIES VERSION ${CPACK_PACKAGE_VERSION_MAJOR} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})

使用 mariadbclient (选项 2)

现在也可以用MSYS在Windows上运行了。 使用这个shell脚本,你可以很容易地建立插件后,修改以下变量(使用 "clean "参数将删除生成的文件。) ./build_libsqlmariadb.so.sh clean):

  1. ANDROID_NDK_ROOT (在Windows上使用MSYS,用/d或/c代替D:, C:)
  2. QT_ROOT (在Windows上使用MSYS,用/d或/c代替D:, C:)
  3. SR (如果你想为更高版本的安卓系统编译,则可选择)
  4. pkg (如果你想为更高的OpenSSL/libiconv/mariadbclient/Qt版本编译,则可选)
  5. output_dir (可选,以防你想改变所需的.so文件的收集位置)
  6. build_dir (可选,下载和解压临时编译文件的地方,这样我们就不会浪费脚本的目录了)
  7. BR (如果你是用MSYS在Windows上构建,你可能必须将: linux-x86_64替换为windows-x86_64)
  8. no_skips (如果你把它设置为false,它将在脚本开始时构建所有的东西,而不会跳过已经构建好的lib的重新构建过程)

译者注:您可以在Windows下编译libiconv、openssl,但不建议在Windows下编译mariadb,这往往是不能成功的。Windows的Linux子系统是一个可以考虑的解决办法。除此之外,如果要用更高版本的Qt(比如5.15),您可能在qmake环节需要使用更高版本的NDK,比如r21(并将之前生成的库复制到对应的位置)。如果你已经下载好对应的源代码(包括Qt MySQL插件的源代码),你可以将下面代码中下载环节改为你已经下载的路径。

#!/bin/bash
# Usage: ./build_libsqlmariadb.so.sh [clean]
# 2016-07-22 T-bond (https://t-bond.hu) Added Windows-MSYS build support, Added clean option, and output to dir. Added temp build dir. Fixed no getpwent error. More cleaner compile output. Skip for already built libs

# Based on https://gist.github.com/RazZziel/fd607459c1f07a43cdf9
output_dir="libmariadb_so_output"
build_dir="build_libmariadb_temp"

export ANDROID_NDK_ROOT="/d/SDKs/Android/ndk-bundle"
export QT_ROOT="/d/SDKs/Qt/5.7"
 
SR="$ANDROID_NDK_ROOT/platforms/android-17/arch-arm/"
BR="$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-"
no_skips=true

#Clean build and outputs
if [ "$1" = "clean" ]; then
	echo "Cleaning..."
	rm -r -f $build_dir $output_dir
	echo "Cleaned."
else
pushd () {
    command pushd "$@" > /dev/null
}

popd () {
    command popd "$@" > /dev/null
}

if [ ! -d $build_dir ]; then
mkdir $build_dir
fi
pushd $build_dir
 
here="$(dirname "$0")"
find "$SR" > "$here/rootfs_files_before.txt" || exit 1
checkChanges() {
        find "$SR" > "$here/rootfs_files_after.txt" || exit 1
        echo "Changes:"
        diff -u "$here/rootfs_files_before.txt" "$here/rootfs_files_after.txt"
}
trap checkChanges EXIT
 
# OpenSSL
pkg=openssl-1.0.2h.tar.gz
dir=$(basename $pkg .tar.gz)
 
if [ ! -d $dir ]; then
        wget -c http://www.openssl.org/source/$pkg
        tar -xf $pkg || exit 1
fi
 
pushd $dir
		if [ ! -f "skip" ] || [ "$no_skips" = true ]; then
	        RANLIB="$BR"ranlib CC="$BR"gcc ./Configure android-armv7 --prefix=$SR/usr
	        ANDROID_DEV=$SR/usr make || exit 1
	        make build_libs || exit 1
        touch skip
    	fi
popd
 
# libiconv
pkg=libiconv-1.14.tar.gz
dir=$(basename $pkg .tar.gz)
 
if [ ! -d $dir ]; then
        wget -c http://ftp.gnu.org/pub/gnu/libiconv/$pkg
        tar -xf $pkg || exit 1
fi
 
pushd $dir
		if [ ! -f "skip" ] || [ "$no_skips" = true ]; then
			#Fix for outdated scripts on Windows T-bond.
			wget "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD" -O new_config.guess
			wget "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD" -O new_config.sub
			\cp new_config.guess build-aux/config.guess
			\cp new_config.guess libcharset/build-aux/config.guess
			\cp new_config.sub build-aux/config.sub
			\cp new_config.sub libcharset/build-aux/config.sub
			#Fix for outdated scripts on Windows T-bond. END
			STRIP="$BR"strip RANLIB="$BR"ranlib OBJDUMP="$BR"objdump AR="$BR"ar CC="$BR"gcc CFLAGS=--sysroot=$SR CPP="$BR"cpp CPPFLAGS=$CFLAGS ./configure --host=arm --prefix=$SR/usr --with-sysroot=$SR
	        make || exit 1
	        make install || exit 1
	        touch skip
    	fi
popd
 
 
# mariadbclient
 
version=2.0.0
pkg=mariadb_client-$version-src.tar.gz
url=http://archive.mariadb.org/client-native-$version/src/$pkg
 
# FIXME: This one fails to build, linker errors
#version=2.1.0
#pkg=mariadb-connector-c-$version-src.tar.gz
#url=https://downloads.mariadb.org/f/connector-c-$version/source-tgz/$pkg
 
dir=$(basename $pkg .tar.gz)
if [ ! -d $dir ]; then
        wget -c $url
        tar -xf $pkg || exit 1
fi

pushd $dir
		if [ ! -f "skip" ] || [ "$no_skips" = true ]; then
	        sed -i -e "s|ADD_SUBDIRECTORY(unittest/libmariadb)|#ADD_SUBDIRECTORY(unittest/libmariadb)|" CMakeLists.txt
	        sed -i -e "N; s|typedef unsigned short ushort;\n#endif|#endif\ntypedef unsigned short ushort;|" include/my_global.h
	        sed -i -e "N; s|SET_TARGET_PROPERTIES(libmariadb PROPERTIES VERSION.*||" libmariadb/CMakeLists.txt
	        sed -i -e "N; s|SOVERSION \${CPACK_PACKAGE_VERSION_MAJOR})||" libmariadb/CMakeLists.txt # Pig disgusting: the previous multiline thingie should've done it
	        sed -i -e "N; s|\${CPACK_PACKAGE_VERSION_MAJOR}||" libmariadb/CMakeLists.txt # Pig disgusting: what the fuck
	        sed -i '/#ifdef HAVE_GETPWNAM/c#if defined(HAVE_GETPWNAM) && defined(HAVE_GETPWENT)' libmariadb/mf_pack.c #T-bond: Fix the no getpwent error. http://forum.kodi.tv/showthread.php?tid=280134
	        #DCMAKE_CXX_COMPILER DCMAKE_SYSTEM_VERSION DCMAKE_SYSTEM_NAME WINDOWS ONLY!!!
	        if [ ! -d "build" ]; then
	        	mkdir build
	    	fi
	        pushd build
	        PKG_CONFIG_PATH=$SR/usr/lib/pkgconfig cmake \
			-DCMAKE_BUILD_TYPE=Release \
			-DCMAKE_C_FLAGS=--sysroot="$SR" \
			-DCMAKE_INSTALL_PREFIX="$SR/usr" \
			-DCMAKE_C_COMPILER="$BR"gcc \
			-DCMAKE_SYSTEM_NAME="Android" \
			-DCMAKE_SYSTEM_VERSION=1 \
			-DCMAKE_CXX_COMPILER="$BR"g++ \
			-DCMAKE_LINKER="$BR"ld \
			-DCMAKE_AR="$BR"ar \
			-DCMAKE_NM="$BR"nm \
			-DCMAKE_OBJCOPY="$BR"objcopy \
			-DCMAKE_OBJDUMP="$BR"objdump \
			-DCMAKE_RANLIB="$BR"ranlib \
			-DCMAKE_STRIP="$BR"strip \
			-DICONV_INCLUDE_DIR="$SR/usr/include" \
			-DICONV_LIBRARIES="$SR/usr/lib/libiconv.a" \
			-DWITH_EXTERNAL_ZLIB=ON \
			-DZLIB_INCLUDE_DIR="$SR/usr/include" \
			-DZLIB_LIBRARY="$SR/usr/lib/libz.so" ../ || exit 1

	        make install || exit 1
	        popd
	        cp build/libmariadb/*.{a,so} "$SR/usr/lib/mariadb/" || exit 1
	        touch skip
	    fi
popd
 
 
# qt
qmake="$QT_ROOT/android_armv7/bin/qmake"
[ ! -f "$qmake" ] && { echo "Could not find qmake in '$qmake'"; exit 1; }
[ ! -x "$qmake" ] && { echo "Qmake is not executable in '$qmake'"; exit 1; }
qtVersion=5.7.0
pkg=qtbase-opensource-src-$qtVersion.tar.gz
 
dir=$(basename $pkg .tar.gz)
 
if [ ! -d $dir ]; then
        wget -c http://download.qt.io/official_releases/qt/${qtVersion%.*}/$qtVersion/submodules/$pkg
        tar -xf $pkg || exit 1
fi
 
pushd $dir/src/plugins/sqldrivers/mysql/
		#$qmake "QMAKE_CXX=$BR"g++ "QMAKE_LINK=$BR"g++ "INCLUDEPATH+=$SR/usr/include/mariadb" "LIBS+=$SR/usr/lib/mariadb/libmariadbclient.a $SR/usr/lib/libssl.a $SR/usr/lib/libcrypto.a $SR/usr/lib/libiconv.a" "LIBPATH+=$SR/usr/lib/mariadb" -o Makefile mysql.pro
                $qmake "INCLUDEPATH+=$SR/usr/include/mariadb" "LIBS+=-L$SR/usr/lib/mariadb -lmysqlclient_r" -o Makefile mysql.pro
        make || exit 1
        make install || exit 1
popd 
popd

if [ ! -d $output_dir ]; then
	mkdir $output_dir
fi
cp $SR/usr/lib/mariadb/libmariadb.so $output_dir/libmariadb.so
cp $QT_ROOT/android_armv7/plugins/sqldrivers/libqsqlmysql.so $output_dir/libqsqlmysql.so
 
echo
echo "BOOYAH!!!"
echo
fi

Deployment Note

You've to add an external library to your .pro file because the generated plugin depends on libmariadb.so

contains(ANDROID_TARGET_ARCH,armeabi-v7a) {
    ANDROID_EXTRA_LIBS = \
        # modify the path
        $$PWD/../../../mysqllibandroid/mariadb_client-2.0.0-src/build/libmariadb/libmariadb.so
}

使用 mysqlclient (目前还不可行的选项)

存根,供人试验和更新本页面。


编译 Qt mysql plugin

你不能从android的configure脚本中编译mysql插件--在写这篇文章的时候,这个脚本似乎已经坏了。然而,你可以从源码中手动编译它。从git中抓取Qt5源码,像正常的那样为android配置它们(没有qt-sql-mysql或plugin-sql-mysql标志),制作,并像正常的那样安装。然后进入qtbase/src/plugins/sqldrivers/mysql目录,执行以下内容。