Build Qt 5 MySQL Plugin for Android/zh
构建安卓版Qt5 mysql插件原来并不像你希望的那样简单。下面我将介绍完成这个任务的必要步骤。
使用 mariadbclient (选项 1)
设置一般环境
设置以下环境变量:
- SR=/path/to/android/ndk/platforms/android-9/arch-arm
- 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模块需要编译以下内容:
- mysql客户端库。对于mariadb(推荐的mysql实现),你还需要用到:
- openssl
- 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):
- ANDROID_NDK_ROOT (在Windows上使用MSYS,用/d或/c代替D:, C:)
- QT_ROOT (在Windows上使用MSYS,用/d或/c代替D:, C:)
- SR (如果你想为更高版本的安卓系统编译,则可选择)
- pkg (如果你想为更高的OpenSSL/libiconv/mariadbclient/Qt版本编译,则可选)
- output_dir (可选,以防你想改变所需的.so文件的收集位置)
- build_dir (可选,下载和解压临时编译文件的地方,这样我们就不会浪费脚本的目录了)
- BR (如果你是用MSYS在Windows上构建,你可能必须将: linux-x86_64替换为windows-x86_64)
- 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目录,执行以下内容。