cmake:用于搜索mysql的FindMySQL.cmake及bug溯源及修复

最近设计一个需要mysql的项目,构建项目的时候需要find_package查找mysql 库安装位置,cmake默认没有提供mysql的find_package支持.好在mysql官方是提供了,在github上mysql-connector-odbc项目找到了FindMySQL.cmake:

https://github.com/mysql/mysql-connector-odbc/blob/master/cmake/FindMySQL.cmake

有它就好办了。

CMakeLists中添加mysql库支持

在CMakeLists.txt 添加如下代码

# 查找 MySQL SDK
#set(MYSQLCLIENT_STATIC_LINKING true)
#set(FINDMYSQL_DEBUG true)
find_package(MySQL MODULE REQUIRED)

设置环境变量MYSQL_DIR

如下是FindMySQL.cmake源码中关于可选参数的说明:

##########################################################################
#
# Configuration variables, all optional, are
#
#   MYSQL_DIR         - Set in environment or as parameter to "cmake",
#                       this is the top directory of the MySQL Server or
#                       Connector/C install
#   MYSQL_INCLUDE_DIR - Set in environment or as parameter to "cmake",
#                       this is the include directory where to find
#                       the client library
#   MYSQL_LIB_DIR     - Set in environment or as parameter to "cmake",
#                       this is the library directory where to find
#                       the client library
#   MYSQLCLIENT_STATIC_LINKING
#                     - Specify that you want static linking, dynamic
#                       linking is the default
#   MYSQLCLIENT_NO_THREADS
#                     - Specify to link against the single threaded
#                       library, "libmysqlclient". Note that in 5.5
#                       and up "libmysqlclient" is multithreaded and
#                       "libmysqlclient_r" just a soft link to it
#   MYSQL_CONFIG_EXECUTABLE
#                     - "mysql_config" executable to use
#   MYSQL_CXX_LINKAGE - Specify that client library needs C++ linking
#   MYSQL_EXTRA_LIBRARIES
#                     - Libraries to add to the linkage
#   MYSQL_CFLAGS      - C compiler flags
#   MYSQL_CXXFLAGS    - C++ compiler flags
#   MYSQL_LINK_FLAGS  - User defined extra linkage flags
#   FINDMYSQL_DEBUG   - Set if want debug output from this script

通过上面的代码可知,FindMySQL允许通过环境变量或参数MYSQL_DIR来指定MySQL Server的安装位置。所以windows下可以通过如下脚本查找mysqld,计算mysql-server的安装位置,并设置MYSQL_DIR环境变量,然后

where mysqld
if errorlevel 1 (
	echo mysqld NOT FOUND.
	exit /B -1
)
echo mysqld found
:: 获取mysql安装路径
FOR /F "delims=*" %%i in ('where mysqld') do set mysqld_bin=%%~dpi
::echo mysqld_bin=%mysqld_bin%
:: 删除最后的 '\'
set mysqld_bin=%mysqld_bin:~0,-1%
::echo mysqld_bin=%mysqld_bin%
for /f "delims=*" %%i in ("%mysqld_bin%") DO set MYSQL_DIR=%%~dpi
::echo MYSQL_DIR=%MYSQL_DIR%
:: 设置MYSQL_DIR环境变量
set MYSQL_DIR=%MYSQL_DIR:~0,-1%
echo MYSQL_DIR=%MYSQL_DIR%

执行

:: 生成Visual Studio 2015工程
cmake .  -G "Visual Studio 14 2015 Win64"

如果执行成功就会看到如下输出

-- You will link dynamically to the MySQL client library (set with -DMYSQLCLIENT_STATIC_LINKING=<bool>)
-- Searching for dynamic libraries with the base name(s) "libmysql"
-- MYSQL_LIB_DIR_LIST =
-- MySQL client environment/cmake variables set that the user can override
--   MYSQL_DIR                   : D:/mysql-5.6.37-winx64
--   MYSQL_INCLUDE_DIR           : D:/mysql-5.6.37-winx64/include
--   MYSQL_LIB_DIR               : D:/mysql-5.6.37-winx64/lib
--   MYSQL_PLUGIN_DIR            : D:/mysql-5.6.37-winx64/lib/plugin
--   MYSQL_CONFIG_EXECUTABLE     :
--   MYSQL_CXX_LINKAGE           :
--   MYSQL_CFLAGS                :
--   MYSQL_CXXFLAGS              :
--   MYSQLCLIENT_STATIC_LINKING  :
--   MYSQLCLIENT_NO_THREADS      :
-- MySQL client optional environment/cmake variables set by the user
--   MYSQL_EXTRA_LIBRARIES       :
--   MYSQL_LINK_FLAGS            :
-- MySQL client settings that the user can't override
--   MYSQL_VERSION               : 5.6.37
--   MYSQL_VERSION_ID            : 50637
--   MYSQL_LIB                   : D:/mysql-5.6.37-winx64/lib/libmysql.lib
--   MYSQL_LIBRARIES             : mysql_sys;mysql_strings;D:/mysql-5.6.37-winx64/lib/libmysql.lib

BUG

然而事实上你会看到如下输出:

-- You will link dynamically to the MySQL client library (set with -DMYSQLCLIENT
_STATIC_LINKING=<bool>)
-- Searching for dynamic libraries with the base name(s) "libmysql"
CMake Error at cmake/Modules/FindMySQL.cmake:824 (message):
  Could not determine the MySQL Server version
Call Stack (most recent call first):
  src/CMakeLists.txt:8 (find_package)

Could not determine the MySQL Server version 出错啦!
同样的代码在linux下却是正常的。
这可是官方提供的FindMySQL.cmake脚本哦,难道有Bug?

问题溯源

进一步阅读FindMySQL.cmake的源码,找到如下位置

if(MYSQL_INCLUDE_DIR AND NOT MYSQL_VERSION)

  # Write the C source file that will include the MySQL headers
  set(GETMYSQLVERSION_SOURCEFILE "${CMAKE_CURRENT_BINARY_DIR}/getmysqlversion.c")
  file(WRITE "${GETMYSQLVERSION_SOURCEFILE}"
       "#include <mysql.h>\n"
       "#include <stdio.h>\n"
       "int main() {\n"
       "  printf(\"%s\", MYSQL_SERVER_VERSION);\n"
       "}\n"
  )

  # Compile and run the created executable, store output in MYSQL_VERSION
  try_run(_run_result _compile_result
    "${CMAKE_BINARY_DIR}"
    "${GETMYSQLVERSION_SOURCEFILE}"
    CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${MYSQL_INCLUDE_DIR}"
    RUN_OUTPUT_VARIABLE MYSQL_VERSION
  )

  if(FINDMYSQL_DEBUG)
    if(NOT _compile_result)
      message("DBG: Could not compile \"getmysqlversion.c\"")
    endif()
    if(_run_result)
      message("DBG: Running \"getmysqlversion\" returned ${_run_result}")
    endif()
  endif()

endif()

可以看到FindMySQL.cmake为了获取mysql版本号,临时生成了一个getmysqlversion.c文件编译并执行它,通过getmysqlversion.exe的输出获取msyql.h中定义的MYSQL_SERVER_VERSION

getmysqlversion.c

#include <mysql.h>
#include <stdio.h>
int main() {
  printf("%s", MYSQL_SERVER_VERSION);
}

于是我在cmake生成的工程文件中找到getmysqlversion.c,在命令行对它用MSVC的cl编译器进行编译

cl getmysqlversion.c /I d:\mysql-5.6.37-winx64\include

果然报错了

用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.00.24215.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

getmysqlversion.c
d:\mysql-5.6.37-winx64\include\mysql_com.h(321): error C2061: 语法错误: 标识符“
SOCKET”
d:\mysql-5.6.37-winx64\include\mysql_com.h(365): error C2059: 语法错误:“}”
d:\mysql-5.6.37-winx64\include\mysql_com.h(482): error C2143: 语法错误: 缺少“)”(在“*”的前面)
d:\mysql-5.6.37-winx64\include\mysql_com.h(482): error C2143: 语法错误: 缺少“{”(在“*”的前面)
d:\mysql-5.6.37-winx64\include\mysql_com.h(482): error C2371: “Vio”: 重定义;不同的基类型
d:\mysql-5.6.37-winx64\include\mysql_com.h(307): note: 参见“Vio”的声明
d:\mysql-5.6.37-winx64\include\mysql_com.h(482): error C2143: 语法错误: 缺少“;”(在“*”的前面)
d:\mysql-5.6.37-winx64\include\mysql_com.h(482): error C2059: 语法错误:“)”
d:\mysql-5.6.37-winx64\include\mysql_com.h(483): error C2143: 语法错误: 缺少“)”(在“*”的前面)
d:\mysql-5.6.37-winx64\include\mysql_com.h(483): error C2143: 语法错误: 缺少“{”(在“*”的前面)
....
d:\mysql-5.6.37-winx64\include\mysql.h(404): fatal error C1003: 错误计数超过 100;正在停止编译

进一步分析mysql.h的源码,及MYSQL_SERVER_VERSION宏定义的位置,发现MYSQL_SERVER_VERSION其实定义在mysq_version.h中,而报错则与mysql_version.h不相关的mysql_com.h。所以实际上只需要include mysql_version.h

#include <mysql.h>改为#include <mysql_version.h>则编译成功

getmysqlversion.c

#include <mysql_version.h>
#include <stdio.h>
int main() {
  printf("%s", MYSQL_SERVER_VERSION);
}
>cl getmysqlversion.c /I d:\mysql-5.6.37-winx64\include
用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.00.24215.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

getmysqlversion.c
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:getmysqlversion.exe
getmysqlversion.obj

J:\mysql-listener\project.vs2015\src>getmysqlversion.exe
5.6.37

哈哈,果然是个bug,于是将FindMySQL.cmake中对应的代码修改为,

  file(WRITE "${GETMYSQLVERSION_SOURCEFILE}"
       "#include <mysql_version.h>\n"
       "#include <stdio.h>\n"
       "int main() {\n"
       "  printf(\"%s\", MYSQL_SERVER_VERSION);\n"
       "}\n"
  )

再执行就会看到成功输出了

:: 生成Visual Studio 2015工程
cmake .  -G "Visual Studio 14 2015 Win64"

Pull Request

上面的修改已经向mysql-connector-odbc官方提交PR:

https://github.com/mysql/mysql-connector-odbc/pull/8

如果被批准,则你可以从https://github.com/mysql/mysql-connector-odbc/blob/master/cmake/FindMySQL.cmake下载修复后的脚本,
否则要自己修改,或从https://gitee.com/l0km/mysql-listener/blob/master/cmake/Modules/FindMySQL.cmake下载修复后的脚本

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值