准備工作
C語言的運行和構建工具介紹
C語言是編譯型語言,需要編譯生成.o
的目標文件,然后用鏈接器連接目標代碼才能生成可執行文件,Windows下就是生成.exe
文件。如果文件之間互相依賴,則需要把所有源文件都編譯才可運行。
然而大型項目中,源文件非常多,需要使用make
工具來批量編譯源文件,通過編寫規則文件Makefile
來告訴make
工具如何進行編譯,但是Makefile
文件是強依賴平台的,在Linux上的Makefile
文件並不能在Widnwos上面跑,而且大型項目的Makefile
文件的編寫也會非常復雜。
redis項目是基於Makefile
的,而且基於Linux平台,所以要想在Windows上面編譯運行,還需要借助另外一個工具cmake
。
cmake
工具是用來自動生成跨平台Makefile
文件的工具,用來生成跨平台的Makefile
,同時避免復雜的Makefile
編寫過程。
同樣的,cmake
工具相應的需要編寫CMakeLists.txt
規則來告訴cmake
工具如何生成Makefile
文件。
Windows平台安裝cygwin
cygwin
工具可以在window平台上運行Linux上面的命令等,方便編譯和運行,而且該工具能讓我們使用很多Linux的強大命令。下載地址: https://cygwin.com/install.html。64位Windows下載setup-x86_64.exe
。下載之后直接雙擊安裝,步驟如下:
下一步 -> 選擇下載源(install from Internet) -> 選擇安裝跟目錄 -> 選擇本地包路徑 -> 選擇網絡連接(Direct Connection) -> 選擇下載鏡像 -> 選擇需要安裝的包 -> 下一步。
可以搜索安裝包,然后在Skip一列點擊選擇安裝最新穩定版本,本次需要安裝的包如下,可以按需安裝其他包:
- wget: 通過http或者ftp下載文件
- gcc-core: GNU編譯工具集(C,OpenMP)
- gcc-g++: GNU編譯工具集(C++)
- make: GNU構建工具
- cmake: 跨平台makefile生成工具
- gdb: GNU調試工具
- binutils: GNU編譯和連接工具
為了方便使用cygwin
中的命令,還需要將其bin
目錄添加到系統環境變量,添加環境變量的方法如下:
我的電腦右鍵 -> 選擇屬性 -> 高級系統設置 -> 高級tab -> 環境變量 -> 系統變量中Path雙擊 -> 新建一條填入cygwin
安裝bin目錄。
如果有需要,可以安裝包管理工具apt-cyg
,類似於Linux上的apt-get
或者yum
等,方便安裝和管理包,直接從github: https://github.com/transcode-open/apt-cyg下載zip,解壓后將其中的apt-cyg
文件放到剛才安裝的cygwin
根目錄下的bin目錄中即可。
安裝CLion並設置編譯運行環境
然后安裝運行IDE,本次使用Jetbrains
全家桶中的CLion
進行源碼調試和閱讀,由於Clion
自帶的MinGW
會有些庫缺失問題,所以還行需要設置使用cygwin
作為編譯運行環境。
文件 -> 設置 -> Build, Execution, Deployment -> Add Cygwin -> Toolset那一欄選擇cygwin
安裝目錄。
導入redis源碼和配置
redis官方源碼地址為: https://github.com/redis/redis,可以fork到自己的倉庫,然后基於自己倉庫的代碼進行修改,這個可以把一些注釋提交方便后續查看,也可以直接下載release里面的穩定版本的源碼。這兒下載最新發布版本https://github.com/redis/redis/archive/refs/tags/6.2.6.zip。
下載redis源碼之后,在CLion中選擇 文件 -> 打開,選擇下載好的源碼文件夾,CLion會自動識別項目。
注意一定要選擇CMake
項目,否則以Makefile
方式加載項目進入后,添加CMakeLists
文件是不能構建的,此時右鍵CMakeLists.txt
沒有Load CMake Project
的選項,如果加載的方式錯了,則需要推出CLion
,然后把redis
根目錄下的.idea
文件全部刪掉,再重新加載。
配置CMakeLists.txt文件
由於redis
是基於Linux上的Makefile
,所以Windows上需要配置CMakeLists.txt
使用cmake
工具編譯運行。github上已經有人嘗試編寫CMakeLists.txt
文件,項目地址為: https://github.com/LHRchina/redis。本文也是參考該項目的CMakeLists.txt
文件,然后把缺少的庫補上成功編譯運行的。
redis根目錄下配置CMakeLists.txt
,文件內容如下:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(redis VERSION 6.0)
#set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../src)
message(CMAKE_RUNTIME_OUTPUT_DIRECTORY is:${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
#if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type defined; defaulting to 'Debug'")
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
"The type of build. Possible values are: Debug, Release,
RelWithDebInfo and MinSizeRel.")
#endif()
message(STATUS "Host is: ${CMAKE_HOST_SYSTEM}. Build target is:
${CMAKE_SYSTEM}")
get_filename_component(REDIS_ROOT "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
message(STATUS "Project root directory is: ${REDIS_ROOT}")
# Just for debugging when handling a new platform.
if (false)
message("C++ compiler supports these language features:")
foreach (i ${CMAKE_CXX_COMPILE_FEATURES})
message(" ${i}")
endforeach ()
endif ()
message(STATUS "Generating release.h...")
execute_process(
COMMAND sh -c ./mkreleasehdr.sh
WORKING_DIRECTORY ${REDIS_ROOT}/src/
)
add_subdirectory(deps)
add_subdirectory(src/modules)
set(SRC_SERVER_TMP
src/crcspeed.c
src/crcspeed.h
src/sha256.c
src/sha256.h
src/connection.c
src/connection.h
src/acl.c
src/timeout.c
src/tracking.c
src/tls.c
src/adlist.c
src/ae.c
src/anet.c
# windows屏蔽掉下面兩個文件,mac系統不需要屏蔽,這兩個是mac環境多路復用的庫
# /usr/local/include/event.h
# src/ae_kqueue.c
src/mt19937-64.c
src/mt19937-64.h
src/monotonic.c
src/monotonic.h
src/dict.c
src/sds.c
src/zmalloc.c
src/lzf_c.c
src/lzf_d.c
src/pqsort.c
src/zipmap.c
src/sha1.c
src/ziplist.c
src/release.c
src/networking.c
src/util.c
src/object.c
src/db.c
src/replication.c
src/rdb.c
src/t_string.c
src/t_list.c
src/t_set.c
src/t_zset.c
src/evict.c
src/defrag.c
src/module.c
src/quicklist.c
src/expire.c
src/childinfo.c
src/redis-check-aof.c
src/redis-check-rdb.c
src/lazyfree.c
src/geohash.c
src/rax.c
src/geohash_helper.c
src/siphash.c
src/geo.c
src/t_hash.c
src/config.c
src/aof.c
src/pubsub.c
src/multi.c
src/debug.c
src/sort.c
src/intset.c
src/syncio.c
src/cluster.c
src/crc16.c
src/endianconv.c
src/slowlog.c
src/scripting.c
src/bio.c
src/rio.c
src/rand.c
src/memtest.c
src/crc64.c
src/bitops.c
src/sentinel.c
src/notify.c
src/setproctitle.c
src/blocked.c
src/hyperloglog.c
src/latency.c
src/sparkline.c
src/t_stream.c
src/lolwut.c
src/lolwut.h
src/lolwut5.c
src/lolwut6.c
src/listpack.c
src/localtime.c
src/gopher.c
)
set(SRC_SERVER src/server.c ${SRC_SERVER_TMP})
set(SRC_CLI
src/anet.c
src/sds.c
src/adlist.c
src/redis-cli.c
src/zmalloc.c
src/release.c
src/ae.c
src/crc64.c
src/crc16.c
src/dict.c
src/siphash.c
)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# better not to work with jemalloc
endif()
set(EXECUTABLE_OUTPUT_PATH src)
add_executable(redis-server ${SRC_SERVER})
add_executable(redis-cli ${SRC_CLI})
set_property(TARGET redis-server PROPERTY C_STANDARD 99)
set_property(TARGET redis-server PROPERTY CXX_STANDARD 11)
set_property(TARGET redis-server PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(TARGET redis-cli PROPERTY C_STANDARD 99)
set_property(TARGET redis-cli PROPERTY CXX_STANDARD 11)
set_property(TARGET redis-cli PROPERTY CXX_STANDARD_REQUIRED ON)
target_include_directories(redis-server
PRIVATE ${REDIS_ROOT}/deps/hiredis
PRIVATE ${REDIS_ROOT}/deps/linenoise
PRIVATE ${REDIS_ROOT}/deps/lua/src
)
target_include_directories(redis-cli
PRIVATE ${REDIS_ROOT}/deps/hiredis
PRIVATE ${REDIS_ROOT}/deps/linenoise
PRIVATE ${REDIS_ROOT}/deps/lua/src
)
target_link_libraries(redis-server
PRIVATE pthread
PRIVATE m
PRIVATE lua
PRIVATE linenoise
PRIVATE hiredis
)
target_link_libraries(redis-cli
PRIVATE pthread
PRIVATE m
PRIVATE linenoise
PRIVATE hiredis
)
link_directories(deps/hiredis/ deps/linenoise/ diredeps/lua/src)
install(TARGETS redis-server
RUNTIME DESTINATION bin
)
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -c")
src/modules
目錄下配置CMakeLists.txt
,文件內容如下:
cmake_minimum_required(VERSION 3.9)
set(CMAKE_BUILD_TYPE "Debug")
add_library(helloworld SHARED helloworld.c)
set_target_properties(helloworld PROPERTIES PREFIX "" SUFFIX ".so")
add_library(hellotype SHARED hellotype.c)
set_target_properties(hellotype PROPERTIES PREFIX "" SUFFIX ".so")
add_library(helloblock SHARED helloblock.c)
set_target_properties(helloblock PROPERTIES PREFIX "" SUFFIX ".so")
#add_library(testmodule SHARED testmodule.c)
#set_target_properties(testmodule PROPERTIES PREFIX "" SUFFIX ".so")
deps
目錄下配置CMakeLists.txt
,文件內容如下:
add_subdirectory(hiredis)
add_subdirectory(linenoise)
add_subdirectory(lua)
deps/hiredis
目錄下配置CMakeLists.txt
,文件內容如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)
INCLUDE(GNUInstallDirs)
PROJECT(hiredis)
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF)
OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF)
MACRO(getVersionBit name)
SET(VERSION_REGEX "^#define ${name} (.+)$")
FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h"
VERSION_BIT REGEX ${VERSION_REGEX})
STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}")
ENDMACRO(getVersionBit)
getVersionBit(HIREDIS_MAJOR)
getVersionBit(HIREDIS_MINOR)
getVersionBit(HIREDIS_PATCH)
getVersionBit(HIREDIS_SONAME)
SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}")
MESSAGE("Detected version: ${VERSION}")
PROJECT(hiredis VERSION "${VERSION}")
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
SET(hiredis_sources
alloc.c
async.c
dict.c
hiredis.c
net.c
read.c
sds.c
sockcompat.c)
SET(hiredis_sources ${hiredis_sources})
IF(WIN32)
ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN)
ENDIF()
ADD_LIBRARY(hiredis SHARED ${hiredis_sources})
SET_TARGET_PROPERTIES(hiredis
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE
VERSION "${HIREDIS_SONAME}")
IF(WIN32 OR MINGW)
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
ENDIF()
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:.> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
INSTALL(TARGETS hiredis
EXPORT hiredis-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(DIRECTORY adapters
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
export(EXPORT hiredis-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake"
NAMESPACE hiredis::)
SET(CMAKE_CONF_INSTALL_DIR share/hiredis)
SET(INCLUDE_INSTALL_DIR include)
include(CMakePackageConfigHelpers)
configure_package_config_file(hiredis-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
PATH_VARS INCLUDE_INSTALL_DIR)
INSTALL(EXPORT hiredis-targets
FILE hiredis-targets.cmake
NAMESPACE hiredis::
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
IF(ENABLE_SSL)
IF (NOT OPENSSL_ROOT_DIR)
IF (APPLE)
SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
ENDIF()
ENDIF()
FIND_PACKAGE(OpenSSL REQUIRED)
SET(hiredis_ssl_sources
ssl.c)
ADD_LIBRARY(hiredis_ssl SHARED
${hiredis_ssl_sources})
IF (APPLE)
SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup")
ENDIF()
SET_TARGET_PROPERTIES(hiredis_ssl
PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
VERSION "${HIREDIS_SONAME}")
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
IF (WIN32 OR MINGW)
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis)
ENDIF()
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
INSTALL(TARGETS hiredis_ssl
EXPORT hiredis_ssl-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(FILES hiredis_ssl.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
export(EXPORT hiredis_ssl-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake"
NAMESPACE hiredis::)
SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl)
configure_package_config_file(hiredis_ssl-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
PATH_VARS INCLUDE_INSTALL_DIR)
INSTALL(EXPORT hiredis_ssl-targets
FILE hiredis_ssl-targets.cmake
NAMESPACE hiredis::
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
ENDIF()
IF(NOT DISABLE_TESTS)
ENABLE_TESTING()
ADD_EXECUTABLE(hiredis-test test.c)
IF(ENABLE_SSL_TESTS)
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1)
TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl)
ELSE()
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
ENDIF()
ADD_TEST(NAME hiredis-test
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
ENDIF()
# Add examples
IF(ENABLE_EXAMPLES)
ADD_SUBDIRECTORY(examples)
ENDIF(ENABLE_EXAMPLES)
deps/linenoise
目錄下配置CMakeLists.txt
,文件內容如下:
add_library(linenoise linenoise.c)
deps/lua
目錄下配置CMakeLists.txt
,文件內容如下:
set(LUA_SRC
src/lauxlib.c
src/liolib.c
src/lopcodes.c
src/lstate.c
src/lobject.c
src/print.c
src/lmathlib.c
src/loadlib.c
src/lvm.c
src/lfunc.c
src/lstrlib.c
src/lua.c
src/linit.c
src/lstring.c
src/lundump.c
src/luac.c
src/ltable.c
src/ldump.c
src/loslib.c
src/lgc.c
src/lzio.c
src/ldblib.c
src/strbuf.c
src/lmem.c
src/lcode.c
src/ltablib.c
src/lua_struct.c
src/lapi.c
src/lbaselib.c
src/lua_cmsgpack.c
src/ldebug.c
src/lparser.c
src/lua_cjson.c
src/fpconv.c
src/lua_bit.c
src/llex.c
src/ltm.c
src/ldo.c
)
add_library(lua STATIC ${LUA_SRC})
配置完之后,右鍵點擊根目錄下的CMakeLists.txt
,選擇Reload CMake Project
。此時右上角會出現redis-server|Debug
的運行選項,點擊調試可以運行redis-server
。
運行后可以通過一些redis客戶端工具連接進行測試。
報錯問題匯總
如果使用的是其他版本的redis,則需要根據編譯運行信息調整上面的CMakeLists.txt
文件,相應的調整配置,一般是缺失編譯文件或者多余代碼。
編譯報錯error: unknown type name 'Dl_info'
首次編譯的時候會遇到編譯錯誤:
FAILED: CMakeFiles/redis-server.dir/src/debug.c.o
/cygdrive/g/Projects/Git_Projects/redis-6.2.6/src/debug.c: In function 'dumpX86Calls':
/cygdrive/g/Projects/Git_Projects/redis-6.2.6/src/debug.c:1753:5: error: unknown type name 'Dl_info'
沒有類型D1_info
,因為這是一個調試文件,然后其中的函數沒有調用,我的解決辦法是把相關的函數注釋掉。主要是src/debug.c
文件中的dumpX86Calls
函數和dumpCodeAroundEIP
兩個函數注釋掉就可以了。
啟動報錯Address already in use
啟動的時候報錯端口被占用,redis-server默認使用6379端口,要去報該端口沒有被占用才行,獲取可以添加運行參數或者配置文件使用其他端口。