mysql 1.1 uni.v溯源MYSQL_MAJOR_VERSION

前言

背景:宏UNIV_INLINE定义于univ.i中,univ顾名思义即universal,是各个头文件通用的宏定义文件。因此先学习该文件。
目的:弄清楚univ.i中MYSQL_MAJOR_VERSION的来源,并学习cmake知识(包括configure_file、cmake宏)
PS:作者在阅读代码时出现过5.5到5.7版本间的切换,所以可能在个别代码细节上与你的不一致,但学到知识就好。

源码探索

univ.i

univ.i(方便起见,可能只显示文件名而省略具体路径)的开头,存在如下三段代码:

#include <mysql_version.h>

#define INNODB_VERSION_MAJOR	MYSQL_MAJOR_VERSION
#define INNODB_VERSION_MINOR	MYSQL_MINOR_VERSION

...

/* Include a minimum number of SQL header files so that few changes
made in SQL code cause a complete InnoDB rebuild.  These headers are
used throughout InnoDB but do not include too much themselves.  They
support cross-platform development and expose comonly used SQL names. */

# include <my_global.h>
# include <my_thread.h>

...

/* Include the header file generated by CMake */
#ifndef _WIN32
# ifndef UNIV_HOTBACKUP
#  include "my_config.h"
# endif /* UNIV_HOTBACKUP */
#endif
  1. 在第一段中的MYSQL_MAJOR_VERSION不是在文件内定义的,它是来自引入的其它头文件,
    我们试图弄清楚其来源。
  2. 第二段引入了<my_global.h>,my顾名思义mysql
  3. 第三段根据_WIN32判断引入my_config.h,这里_WIN32在windows平台下的编译器会预定义。如果是unix平台下运行不到这句话。

关于_WIN32

Which Cross Platform Preprocessor Defines? (WIN32 or __WIN32 or WIN32 )?得知,大部分windows编译器都会预定义_WIN32,使用即可。(c++代码阅读常常需要一些约定俗成的知识,这也是它难学的原因。)

most of Windows compilers predefine _WIN32

Is #if defined MACRO equivalent to #ifdef MACRO? 提到两者是等价的。

config.h.cmake

利用vs全局搜索,我们发现在config.h.cmake中:

#define MYSQL_MAJOR_VERSION @MAJOR_VERSION@

@var@的做法是引入cmake变量,它应当在某处的在Cmake配置文件中定义了。

CmakeLists.txt

在主目录配置文件mysql-server5/CmakeLists.txt中,存在CONFIGURE_FILE命令:

CONFIGURE_FILE(config.h.cmake   ${CMAKE_BINARY_DIR}/include/my_config.h)
CONFIGURE_FILE(config.h.cmake   ${CMAKE_BINARY_DIR}/include/config.h)

cmake遇到该命令会编译第一个参数名的文件,输出到第二个参数文件名中。

也有include命令的运用,include命令可以帮助复用变量和,但这里sslinstall_layout.cmake等文件基本上只使用一次,相当于分拆职责了:

# Add macros
INCLUDE(character_sets)
INCLUDE(zlib)
INCLUDE(ssl)
INCLUDE(readline)
INCLUDE(mysql_version)
INCLUDE(libutils)
INCLUDE(dtrace)
INCLUDE(plugin)
INCLUDE(install_macros)
INCLUDE(install_layout)
INCLUDE(mysql_add_executable)

install_layout.cmake

此处分心一下,顺着INCLUDE(install_layout)来到install_layout.cmake

SET(INSTALL_LAYOUT "${DEFAULT_INSTALL_LAYOUT}"
CACHE STRING "Installation directory layout. Options are: TARGZ (as in tar.gz installer), WIN (as in zip installer), STANDALONE, RPM, DEB, SVR4, FREEBSD, GLIBC, OSX, SLES")

我们看文件内另外一段:

IF(WIN32)
  SET(VALID_INSTALL_LAYOUTS "TARGZ" "STANDALONE" "WIN")
  LIST(FIND VALID_INSTALL_LAYOUTS "${INSTALL_LAYOUT}" ind)
  IF(ind EQUAL -1)
    MESSAGE(FATAL_ERROR "Invalid INSTALL_LAYOUT parameter:${INSTALL_LAYOUT}."
    " Choose between ${VALID_INSTALL_LAYOUTS}" )
  ENDIF()
ENDIF()

从代码可知以下信息:

  1. cmake 内置变量可知,WIN32UNIX都是cmake内置变量。
  2. 在这里SET(VALID_INSTALL_LAYOUTS "TARGZ" "STANDALONE" "WIN")是设置变量列表(参阅简书博客“定义列表”一章)。
  3. 根据Cmake命令之list介绍LIST(FIND VALID_INSTALL_LAYOUTS "${INSTALL_LAYOUT}" ind)是在列表变量VALID_INSTALL_LAYOUTS中搜索指定字符串,并输出到变量ind

config.h

顺着上文代码,找到编译出来的my_config.hconfig.h。两个文件中都有:

#define MYSQL_MAJOR_VERSION 5

所以编译后的头文件确实是有定义该变量,且能被使用的。

那么上文提到的config.h.cmake中有如下片段:

#define MYSQL_VERSION_MAJOR @MAJOR_VERSION@

其中的MAJOR_VERSION来自哪里呢?应该来自某个CMake配置文件中。

cmake\mysql_version.cmake

普通的人会全局搜索MAJOR_VERSIONset(MAJOR_VERSION,但都找不到结果,因为该变量不是直接调用set设置的

全局搜索" MAJOR_VERSION"(以空格开头),我们看到子cmake配置文件cmake\mysql_version.cmake中存在以下片段:

MACRO(GET_MYSQL_VERSION)
  MYSQL_GET_CONFIG_VALUE("MYSQL_VERSION_MAJOR" MAJOR_VERSION)
  MYSQL_GET_CONFIG_VALUE("MYSQL_VERSION_MINOR" MINOR_VERSION)
  MYSQL_GET_CONFIG_VALUE("MYSQL_VERSION_PATCH" PATCH_VERSION)
  MYSQL_GET_CONFIG_VALUE("MYSQL_VERSION_EXTRA" EXTRA_VERSION)

这里调用了预定义的宏MYSQL_GET_CONFIG_VALUE,它定义于上文:

MACRO(MYSQL_GET_CONFIG_VALUE keyword var)
 IF(NOT ${var})
   FILE (STRINGS ${CMAKE_SOURCE_DIR}/VERSION str REGEX "^[ ]*${keyword}=")
   IF(str)
     STRING(REPLACE "${keyword}=" "" str ${str})
     STRING(REGEX REPLACE  "[ ].*" ""  str "${str}")
     SET(${var} ${str})
   ENDIF()
 ENDIF()
ENDMACRO()

这段代码做了如下事情:

  1. FILE (STRINGS ${CMAKE_SOURCE_DIR}/VERSION str REGEX "^[ ]*${keyword}=")从VERSION文件读取字符串放到变量str,并匹配符合"^[ ]*${keyword}="的部分。^[ ]*表示以0个或多个空格开头。
  2. STRING(REPLACE "${keyword}=" "" str ${str})从变量str中删除掉${keyword}=的部分
  3. STRING(REGEX REPLACE "[ ].*" "" str "${str}")删除掉开头的1个空格,和任意个字符。

上面的语句3可能编写错误了,会在下文分析。但总而言之,这段宏代码会从VERSION文件读取keyword字段在=号后面的值。 我们再看下VERSION文件:

MYSQL_VERSION_MAJOR=5
MYSQL_VERSION_MINOR=7
MYSQL_VERSION_PATCH=17
MYSQL_VERSION_EXTRA=

终于真相大白,MYSQL_VERSION_MAJOR原来是从这里来的

可能存在的bug

我怀疑上文的代码写错了,首先我们看下VERSION文件如下:

MYSQL_VERSION_MAJOR=5
MYSQL_VERSION_MINOR=7
MYSQL_VERSION_PATCH=17
MYSQL_VERSION_EXTRA=

这样跑完代码是没问题的,因为没有前缀空白符号,也就是语句3.没发挥作用

我们看到SET(${var} ${str})就明白了,利用预定义的宏,把MAJOR_VERSION设置成了MYSQL_VERSION_MAJOR
我们写个demo测试一下,
CMakeLists.txt

cmake_minimum_required (VERSION 3.8)

project ("cpp_demo")

# 将源代码添加到此项目的可执行文件。
add_executable (cpp_demo "cpp_demo.cpp" "cpp_demo.h")

set(keyword "MYSQL_VERSION_MAJOR")

FILE (STRINGS ./VERSION str)
message(STATUS "0. version file:${str}")
FILE (STRINGS ./VERSION str REGEX "^[ ]*${keyword}=")
message(STATUS "1. change version file to ${str}")
IF(str)
    STRING(REPLACE "${keyword}=" "" str ${str})
    message(STATUS "2. change version file to ${str}")
    STRING(REGEX REPLACE  "[ ].*" ""  str "${str}")
    message(STATUS "3. change version file to ${str}")
ENDIF()

对上文的VERSION文件处理后,输出:
普通VERSION文件

然后,我们为VERSION文件第一、二行各自添加1、2个前导空格,变成:

 MYSQL_VERSION_MAJOR=5
  MYSQL_VERSION_MINOR=7
MYSQL_VERSION_PATCH=17
MYSQL_VERSION_EXTRA=

根据上文代码,搜索keyword为MYSQL_VERSION_MAJOR:
j1.加了前导空格
然后通过set(keyword "MYSQL_VERSION_MINOR")改成搜索keyword为MYSQL_VERSION_MINOR:
2.加了前导空格
明明读取文件时会匹配前导空格,但只要加了前导空格,就提取不出来数字了,这显然是设计失误

事实上这也是cmake的麻烦之处,要背专门的语法,还不好调试,写了一大堆只能肉眼debug,或者跑一遍。为何不在程序运行后用c++代码读取配置文件呢?省时省力,还好调试。

总结

  1. 关于MYSQL_MAJOR_VERSION的来源
    1. univ.i中使用的MYSQL_MAJOR_VERSION变量来自config.h.cmake文件
    2. 后者引用了mysql_version.cmake中利用macro从VERSION文件中MYSQL_VERSION_MAJOR字段读取出的值,设置到了MAJOR_VERSION字段。
    3. mysql_version.cmake因为被主cmake配置文件调用了include命令而生效。
  2. mysql_version.cmake中MYSQL_GET_CONFIG_VALUE的macro可能写错了,只是因为配置文件没前导空格才没报错。
  3. 平台编译器会预定义宏(比如_WIN32),cmake也会内置与平台相关的变量(比如WIN),。

附录

cmake如何设置变量

如何达到下图的效果,可以自定义cmake变量,使得能够在cmake-gui中设置呢?
缓存变量

cmake缓存变量 提到,在set命令中用CACHE描述符缓存变量,用FORCE固定变量。在cmake官网也能看到同样表述:

Sets the given cache <variable> (cache entry). Since cache entries are meant to provide user-settable values this does not overwrite existing cache entries by default. Use the FORCE option to overwrite existing entries.

加上FORCE后,在windows cmake、ccmake等都无法修改参数。

configure_file命令

configure_file可以在cmake编译时,根据设定的配置文件添加头文件。

观看Hello, Configure File! Generating a C++ Configure File with CMake "basic_116"并查阅配套的代码cnruby/w3h1_cmake可知,在cmake配置文件中利用configure_file,可以读取文件config.h.in并生成头文件config.hxx

configure_file(
    ${PROJECT_SOURCE_DIR}/cmake/config.h.in
    ${PROJECT_SOURCE_DIR}/config/config.hxx
    # Restrict the form @VAR@ of template variable, NOT the form ${var}
    @ONLY
)

cmake导入缓存变量
Q&A:

实践案例

为了验证configure_file的功能,我们建立了一个cmake项目demo。

你也可以仿照【Visual Studio 2019】创建 导入 CMake 项目来创建cmake项目,但如果先写好cmake配置文件,再基于windows cmake gui生成项目也是可以的。

git项目的configure_file_demo可以看到样例。实践尽量遵守格式,在src内建立cmake子配置文件,并在外层引用。

PS:曾试图不在src目录添加cmake子配置文件,在cmake主配置文件直接add_executable,或者将.cpp文件放在外层并以add_executable引用。两种做法都失败了,会导致找不到头文件,原因未知。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值