support oceanbase rpm build when LTO is on
This commit is contained in:
		
							
								
								
									
										2
									
								
								build.sh
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								build.sh
									
									
									
									
									
								
							| @ -201,7 +201,7 @@ function build | |||||||
|         ;; |         ;; | ||||||
|       xrpm) |       xrpm) | ||||||
|         STATIC_LINK_LGPL_DEPS_OPTION=OFF |         STATIC_LINK_LGPL_DEPS_OPTION=OFF | ||||||
|         do_build "$@" -DOB_BUILD_RPM=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOB_USE_LLD=$LLD_OPTION -DENABLE_FATAL_ERROR_HANG=OFF -DENABLE_AUTO_FDO=ON -DOB_STATIC_LINK_LGPL_DEPS=$STATIC_LINK_LGPL_DEPS_OPTION |         do_build "$@" -DOB_BUILD_RPM=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOB_USE_LLD=$LLD_OPTION -DENABLE_FATAL_ERROR_HANG=OFF -DENABLE_AUTO_FDO=ON -DENABLE_THIN_LTO=ON -DOB_STATIC_LINK_LGPL_DEPS=$STATIC_LINK_LGPL_DEPS_OPTION | ||||||
|         ;; |         ;; | ||||||
|       xenable_smart_var_check) |       xenable_smart_var_check) | ||||||
|         do_build "$@" -DCMAKE_BUILD_TYPE=Debug -DOB_USE_LLD=$LLD_OPTION -DENABLE_SMART_VAR_CHECK=ON -DOB_ENABLE_AVX2=ON |         do_build "$@" -DCMAKE_BUILD_TYPE=Debug -DOB_USE_LLD=$LLD_OPTION -DENABLE_SMART_VAR_CHECK=ON -DOB_ENABLE_AVX2=ON | ||||||
|  | |||||||
| @ -51,6 +51,8 @@ set(CPACK_RPM_SPEC_MORE_DEFINE | |||||||
| # | # | ||||||
| # - PATH is relative to the **ROOT directory** of project other than the cmake directory. | # - PATH is relative to the **ROOT directory** of project other than the cmake directory. | ||||||
|  |  | ||||||
|  | set(BITCODE_TO_ELF_LIST "") | ||||||
|  |  | ||||||
| ## server | ## server | ||||||
| install(PROGRAMS | install(PROGRAMS | ||||||
|   tools/import_time_zone_info.py |   tools/import_time_zone_info.py | ||||||
| @ -105,6 +107,17 @@ endif() | |||||||
|  |  | ||||||
| ## oceanbase-sql-parser | ## oceanbase-sql-parser | ||||||
| if (OB_BUILD_LIBOB_SQL_PROXY_PARSER) | if (OB_BUILD_LIBOB_SQL_PROXY_PARSER) | ||||||
|  |  | ||||||
|  |   if (ENABLE_THIN_LTO) | ||||||
|  |     message(STATUS "add libob_sql_proxy_parser_static_to_elf") | ||||||
|  |     add_custom_command( | ||||||
|  |       OUTPUT libob_sql_proxy_parser_static_to_elf | ||||||
|  |       COMMAND ${CMAKE_SOURCE_DIR}/cmake/script/bitcode_to_elfobj --ld=${OB_LD_BIN} --input=${CMAKE_BINARY_DIR}/src/sql/parser/libob_sql_proxy_parser_static.a --output=${CMAKE_BINARY_DIR}/src/sql/parser/libob_sql_proxy_parser_static.a | ||||||
|  |       DEPENDS ob_sql_proxy_parser_static | ||||||
|  |       COMMAND_EXPAND_LISTS) | ||||||
|  |     list(APPEND BITCODE_TO_ELF_LIST libob_sql_proxy_parser_static_to_elf) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|   install(PROGRAMS |   install(PROGRAMS | ||||||
|     ${CMAKE_BINARY_DIR}/src/sql/parser/libob_sql_proxy_parser_static.a |     ${CMAKE_BINARY_DIR}/src/sql/parser/libob_sql_proxy_parser_static.a | ||||||
|     DESTINATION lib |     DESTINATION lib | ||||||
| @ -298,6 +311,17 @@ install(FILES | |||||||
|   COMPONENT table) |   COMPONENT table) | ||||||
|  |  | ||||||
| if (OB_BUILD_LIBOBTABLE) | if (OB_BUILD_LIBOBTABLE) | ||||||
|  |  | ||||||
|  |   if (ENABLE_THIN_LTO) | ||||||
|  |     message(STATUS "add libobtable_static_to_elf") | ||||||
|  |     add_custom_command( | ||||||
|  |       OUTPUT libobtable_static_to_elf | ||||||
|  |       COMMAND ${CMAKE_SOURCE_DIR}/cmake/script/bitcode_to_elfobj --ld=${OB_LD_BIN} --input=${CMAKE_BINARY_DIR}/src/libtable/src/libobtable_static.a --output=${CMAKE_BINARY_DIR}/src/libtable/src/libobtable_static.a | ||||||
|  |       DEPENDS obtable_static | ||||||
|  |       COMMAND_EXPAND_LISTS) | ||||||
|  |       list(APPEND BITCODE_TO_ELF_LIST libobtable_static_to_elf) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|   install(PROGRAMS |   install(PROGRAMS | ||||||
|     ${CMAKE_BINARY_DIR}/src/libtable/src/libobtable.so |     ${CMAKE_BINARY_DIR}/src/libtable/src/libobtable.so | ||||||
|     ${CMAKE_BINARY_DIR}/src/libtable/src/libobtable.so.1 |     ${CMAKE_BINARY_DIR}/src/libtable/src/libobtable.so.1 | ||||||
| @ -335,4 +359,6 @@ add_custom_target(rpm | |||||||
|   COMMAND +make package |   COMMAND +make package | ||||||
|   DEPENDS |   DEPENDS | ||||||
|   observer obcdc_tailf obtable obtable_static |   observer obcdc_tailf obtable obtable_static | ||||||
|   ob_admin ob_error ob_sql_proxy_parser_static) |   ob_admin ob_error ob_sql_proxy_parser_static | ||||||
|  |   ${BITCODE_TO_ELF_LIST} | ||||||
|  |   ) | ||||||
|  | |||||||
							
								
								
									
										255
									
								
								cmake/script/bitcode_to_elfobj
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										255
									
								
								cmake/script/bitcode_to_elfobj
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,255 @@ | |||||||
|  | #!/usr/bin/python | ||||||
|  | # coding=utf8 | ||||||
|  | # author wenxingsen.wxs | ||||||
|  |  | ||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | import uuid | ||||||
|  | import threading | ||||||
|  | import time | ||||||
|  | import datetime | ||||||
|  | import subprocess | ||||||
|  | import getopt | ||||||
|  |  | ||||||
|  | # 统计开始时间 | ||||||
|  | GLOBAL_START_TIME = datetime.datetime.now() | ||||||
|  |  | ||||||
|  | def get_cost_time(): | ||||||
|  |     ''' | ||||||
|  |     获取当前运行时间 | ||||||
|  |     ''' | ||||||
|  |     cost_time_sec = (datetime.datetime.now() - GLOBAL_START_TIME).seconds | ||||||
|  |     return "%dm%.2ds" % (cost_time_sec / 60, cost_time_sec % 60) | ||||||
|  |  | ||||||
|  | def print_log(log_str): | ||||||
|  |     ''' | ||||||
|  |     打印日志函数 | ||||||
|  |     ''' | ||||||
|  |     print("[%s %s] %s" % (time.strftime("%H:%M:%S", time.localtime()), get_cost_time(), log_str)) | ||||||
|  |     sys.stdout.flush() | ||||||
|  |  | ||||||
|  | class GlobalConf(): | ||||||
|  |     ''' | ||||||
|  |     编译配置类 | ||||||
|  |     ''' | ||||||
|  |     def __init__(self): | ||||||
|  |         # 输入静态库路径 | ||||||
|  |         self.origin_static_abs_path = "" | ||||||
|  |         # 输出静态库路径 | ||||||
|  |         self.target_static_abs_path = "" | ||||||
|  |         # 链接器路径 | ||||||
|  |         self.ld_bin_abs_path = "" | ||||||
|  |         self.workspace = "bitcode_to_elfobj_dir_%s" % str(uuid.uuid1()) | ||||||
|  |  | ||||||
|  | GLOBAL_CONF = GlobalConf() | ||||||
|  |  | ||||||
|  | class ERROR_CODE(): | ||||||
|  |     ''' | ||||||
|  |     错误码 | ||||||
|  |     ''' | ||||||
|  |     # 通用成功和失败 | ||||||
|  |     COMMON_SUCCESS = 0 | ||||||
|  |     COMMON_ERROR = 1 | ||||||
|  |  | ||||||
|  | def shell_run_command(command_str, need_print_all=True, need_print_output=True, no_time=False): | ||||||
|  |     ''' | ||||||
|  |     运行shell命令 | ||||||
|  |     ''' | ||||||
|  |     if need_print_all: | ||||||
|  |         print_log("[运行命令]: %s" % command_str) | ||||||
|  |         if not need_print_output: | ||||||
|  |             print_log("[运行输出]: 日志过多已经忽略输出") | ||||||
|  |     result = dict() | ||||||
|  |     result["return_code"] = 1 | ||||||
|  |     result["return_message"] = [] | ||||||
|  |     result["cmd"] = command_str | ||||||
|  |     is_frist = False | ||||||
|  |     ps = subprocess.Popen(command_str, | ||||||
|  |                           stdin=subprocess.PIPE, | ||||||
|  |                           stdout=subprocess.PIPE, | ||||||
|  |                           stderr=subprocess.STDOUT, | ||||||
|  |                           shell=True, | ||||||
|  |                           close_fds=True) | ||||||
|  |     while True: | ||||||
|  |         data = ps.stdout.readline() | ||||||
|  |         if data == b'': | ||||||
|  |             if ps.poll() is not None: | ||||||
|  |                 break | ||||||
|  |         result["return_message"].append(data) | ||||||
|  |  | ||||||
|  |         if not need_print_all: | ||||||
|  |             continue | ||||||
|  |  | ||||||
|  |         if need_print_output and len(data.strip()) > 1: | ||||||
|  |             if not is_frist: | ||||||
|  |                 print_log("[运行输出]: %s" % data.replace("\n", "")) | ||||||
|  |                 is_frist = True | ||||||
|  |             else: | ||||||
|  |                 data_str = "            %s" % data.replace("\n", "") | ||||||
|  |                 if no_time: | ||||||
|  |                     print(data_str) | ||||||
|  |                 else: | ||||||
|  |                     print_log("            %s" % data.replace("\n", "")) | ||||||
|  |  | ||||||
|  |     result["return_code"] = ps.returncode | ||||||
|  |     return result | ||||||
|  |  | ||||||
|  | def print_help(): | ||||||
|  |     ''' | ||||||
|  |     打印帮助信息 | ||||||
|  |     ''' | ||||||
|  |     print("使用说明:") | ||||||
|  |     print("./bitcode_to_elfobj --ld=/usr/bin/ld --input=/xxx/absolude_path/demo.a --output=/xxx/absolude_path/demo.a") | ||||||
|  |  | ||||||
|  | def parse_arg(): | ||||||
|  |     ''' | ||||||
|  |     解析命令行参数 | ||||||
|  |     ''' | ||||||
|  |     global GLOBAL_CONF | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         # sys.argv[1:] 过滤掉第一个参数(它是脚本名称,不是参数的一部分) | ||||||
|  |         opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "input=", "output=","ld="]) | ||||||
|  |  | ||||||
|  |         if args: | ||||||
|  |             print_log("不符合预期输入,请重试\n") | ||||||
|  |             print_help() | ||||||
|  |             exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |  | ||||||
|  |         for cmd, arg in opts: | ||||||
|  |             if cmd in ("-h", "--help"): | ||||||
|  |                 print_help() | ||||||
|  |                 exit(ERROR_CODE.COMMON_SUCCESS) | ||||||
|  |             elif cmd in ("--input",): | ||||||
|  |                if not arg.startswith("/") or not os.path.exists(arg): | ||||||
|  |                     print("[ERROR] 输入路径[%s]不是绝对路径或者不存在" % arg) | ||||||
|  |                     exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |                GLOBAL_CONF.origin_static_abs_path = arg | ||||||
|  |             elif cmd in ("--output",): | ||||||
|  |                 if not arg.startswith("/"): | ||||||
|  |                     print("[ERROR] 输入路径[%s]不是绝对路径" % arg) | ||||||
|  |                     exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |                 GLOBAL_CONF.target_static_abs_path = arg | ||||||
|  |             elif cmd in ("--ld",): | ||||||
|  |                 if not arg.startswith("/") or not os.path.exists(arg): | ||||||
|  |                     print("[ERROR] 输入路径[%s]不是绝对路径或者不存在" % arg) | ||||||
|  |                     exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |                 GLOBAL_CONF.ld_bin_abs_path = arg | ||||||
|  |  | ||||||
|  |     except getopt.GetoptError as ex: | ||||||
|  |         print_log("[ERROR] getopt.GetoptError 解析参数失败,请合法输入, %s" % ex) | ||||||
|  |         exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |     except ValueError as ex: | ||||||
|  |         print_log("[ERROR] ValueError 解析参数失败,请合法输入, %s" % ex) | ||||||
|  |         exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |  | ||||||
|  |     if not GLOBAL_CONF.origin_static_abs_path or not GLOBAL_CONF.target_static_abs_path or not GLOBAL_CONF.ld_bin_abs_path: | ||||||
|  |         print("[ERROR]输入参数不完整") | ||||||
|  |         print_help() | ||||||
|  |         exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |  | ||||||
|  | def trigger_one_cmd(cmd): | ||||||
|  |     ''' | ||||||
|  |     多线程调用命令函数 | ||||||
|  |     ''' | ||||||
|  |     result = shell_run_command(cmd, need_print_all=False) | ||||||
|  |  | ||||||
|  | def main(): | ||||||
|  |     ''' | ||||||
|  |     main函数入口 | ||||||
|  |     ''' | ||||||
|  |     # 解析参数 | ||||||
|  |     parse_arg() | ||||||
|  |  | ||||||
|  |     # 问题背景: | ||||||
|  |     # 将一个静态库中的bitcode obj转成 elf obj的难点解决静态库中重名问题 | ||||||
|  |     # 例如hello.a 由 a.o b.o b.o三个目标组成,按照普通解压 ar -x hello.a,只会保留 a.o b.o,目标文件将丢失,而且顺序也丢失 | ||||||
|  |     # 在OB联合编译之后,存在大量的重复 0_cxx.cxx,1_cxx.cxx等,其外此转化工具自身也要要求解决重复目标文件等能力 | ||||||
|  |     # 解决方案: | ||||||
|  |     # 利用ar的解压count指定特定目标文件能力,主要是N参数 | ||||||
|  |     #   ar -xN 1 hello.a a.o 并放到 0/a.o下面去 | ||||||
|  |     #   ar -xN 1 hello.a b.o 并放到 1/b.o下面去 | ||||||
|  |     #   ar -xN 2 hello.a b.o 并放到 2/b.o下面去 | ||||||
|  |     # 依次调用 ld.lld -r a.o -o a.o 将bitcode转成elf | ||||||
|  |     # 最后再按照原有顺序拼接静态库: | ||||||
|  |     #   ar -qc hello.a 0/a.o 1/b.o 2/b.o | ||||||
|  |  | ||||||
|  |     static_lib_name = os.path.basename(GLOBAL_CONF.origin_static_abs_path) | ||||||
|  |     print_log("开始使用链接器(%s)将bitcode obj(%s)转换为elf obj(%s)" % (GLOBAL_CONF.ld_bin_abs_path, GLOBAL_CONF.origin_static_abs_path, GLOBAL_CONF.target_static_abs_path)) | ||||||
|  |     time.sleep(0.1) | ||||||
|  |  | ||||||
|  |     # 提取 目标文件 | ||||||
|  |     print_log("开始分析静态库%s..." % static_lib_name) | ||||||
|  |  | ||||||
|  |     result = shell_run_command("ar -t %s" % (GLOBAL_CONF.origin_static_abs_path), need_print_all=False) | ||||||
|  |     if result['return_code'] != ERROR_CODE.COMMON_SUCCESS: | ||||||
|  |         print(result['return_message']) | ||||||
|  |         print_log("[ERROR]获取目标文件列表失败,运行命令为: %s" % result['cmd']) | ||||||
|  |         exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |  | ||||||
|  |     # 用于统计同名目标文件出现的次数 | ||||||
|  |     object_count_dict = {} | ||||||
|  |     # 按照顺序的全部目标文件 | ||||||
|  |     all_object_list = [] | ||||||
|  |  | ||||||
|  |     for one_object in result['return_message']: | ||||||
|  |         one_object = one_object.strip() | ||||||
|  |         if one_object not in object_count_dict: | ||||||
|  |             object_count_dict[one_object] = 1 | ||||||
|  |         else: | ||||||
|  |             object_count_dict[one_object] += 1 | ||||||
|  |  | ||||||
|  |         all_object_list.append({"id":len(all_object_list), "object": one_object, "count": object_count_dict[one_object]}) | ||||||
|  |  | ||||||
|  |     # 创建工作目录 | ||||||
|  |     result = shell_run_command("rm -rf %s && mkdir -p %s" % (GLOBAL_CONF.workspace, GLOBAL_CONF.workspace), need_print_all=False) | ||||||
|  |     if result['return_code'] != ERROR_CODE.COMMON_SUCCESS: | ||||||
|  |         print_log("[ERROR]创建文件夹失败") | ||||||
|  |         exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |  | ||||||
|  |     # 开始解压文件 | ||||||
|  |     print_log("开始提取静态库%s..." % static_lib_name) | ||||||
|  |     ar_after_object_list = [] | ||||||
|  |     for one_object in all_object_list: | ||||||
|  |         result = shell_run_command("cd %s && rm -rf %s && mkdir -p %s && cd %s && ar -xN %s %s %s" % (GLOBAL_CONF.workspace, one_object['id'], one_object['id'], one_object['id'], one_object['count'], GLOBAL_CONF.origin_static_abs_path, one_object['object']), need_print_all=False) | ||||||
|  |         if result['return_code'] != ERROR_CODE.COMMON_SUCCESS: | ||||||
|  |             print(result['return_message']) | ||||||
|  |             print_log("[ERROR]解压静态库失败, 运行命令为: %s" % result['cmd']) | ||||||
|  |             exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |         ar_after_object_list.append(os.path.join(GLOBAL_CONF.workspace, str(one_object['id']), one_object['object'])) | ||||||
|  |  | ||||||
|  |     print_log("开始转换%s的目标文件..." % static_lib_name) | ||||||
|  |     thread_list = list() | ||||||
|  |     # 开始bitcode转elfobj过程 | ||||||
|  |     for one_object in ar_after_object_list: | ||||||
|  |         cmd = "%s -r %s -o %s" % (GLOBAL_CONF.ld_bin_abs_path, one_object, one_object) | ||||||
|  |         t = threading.Thread(target=trigger_one_cmd, args=(cmd, )) | ||||||
|  |         thread_list.append(t) | ||||||
|  |         t.start() | ||||||
|  |         # 防止内存不足,加一点时延 | ||||||
|  |         time.sleep(0.2) | ||||||
|  |  | ||||||
|  |     # 等待所有线程结束 | ||||||
|  |     for t in thread_list: | ||||||
|  |         t.join() | ||||||
|  |  | ||||||
|  |     # 最后链接过程 | ||||||
|  |     print_log("开始链接静态库%s..." % static_lib_name) | ||||||
|  |     cmd_ar_finial = "rm -rf %s && ar qc %s " % (GLOBAL_CONF.target_static_abs_path, GLOBAL_CONF.target_static_abs_path) | ||||||
|  |     for one_object in ar_after_object_list: | ||||||
|  |         cmd_ar_finial += "%s " % one_object | ||||||
|  |  | ||||||
|  |     result = shell_run_command(cmd_ar_finial, need_print_all=False) | ||||||
|  |     if result['return_code'] != ERROR_CODE.COMMON_SUCCESS: | ||||||
|  |         print(result['return_message']) | ||||||
|  |         print_log("[ERROR] 生成elf格式obj失败,运行命令为: %s" % result['cmd']) | ||||||
|  |         exit(ERROR_CODE.COMMON_ERROR) | ||||||
|  |  | ||||||
|  |     print_log("生成静态库!路径位于位于:%s" % GLOBAL_CONF.target_static_abs_path) | ||||||
|  |     return ERROR_CODE.COMMON_SUCCESS | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     ''' | ||||||
|  |     __main__入口 | ||||||
|  |     ''' | ||||||
|  |     main() | ||||||
| @ -20,7 +20,7 @@ target_link_libraries(obtable | |||||||
|   PUBLIC obtable_base_objects |   PUBLIC obtable_base_objects | ||||||
|   PRIVATE |   PRIVATE | ||||||
|   -Wl,--whole-archive |   -Wl,--whole-archive | ||||||
|   $<TARGET_OBJECTS:obtable_base_objects> |   obtable_base_objects | ||||||
|   -Wl,--no-whole-archive |   -Wl,--no-whole-archive | ||||||
|   -Wl,--allow-multiple-definition) |   -Wl,--allow-multiple-definition) | ||||||
| set_target_properties(obtable PROPERTIES SOVERSION 1 VERSION 1.0.0) | set_target_properties(obtable PROPERTIES SOVERSION 1 VERSION 1.0.0) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 wenxingsen
					wenxingsen