webassembly003 whisper.cpp的项目结构CMakeLists.txt
作者:mmseoamin日期:2024-01-19

注:带星号的为非重要部分

基础配置

cmake_minimum_required (VERSION 3.5)
project(whisper.cpp VERSION 1.5.0)
# Add path to modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
# 在\cmake文件夹下还有BuildTypes.cmake,DefaultTargetOptions.cmake,GitVars.cmake几个文件
# CMAKE_MODULE_PATH是供搜索第三方库用的路径变量,因此可在项目中引用自定义模块:
#include(BuildTypes) 在第13行
#include(DefaultTargetOptions) 在第425和478行,为指定的目标设置一些默认的编译特性和属性,以确保项目在编译和安装时的一致性和可靠性。
#include(GitVars) 在第12行
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # 设置CMAKE_CURRENT_SOURCE_DIR变量,应该是可执行文件的输出目录
# 如果当前的源目录等于根源目录
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
    set(WHISPER_STANDALONE ON)  # 标志 WHISPER_STANDALONE 设置为 ON
    include(GitVars)  # 引用 GitVars 模块
    include(BuildTypes)  # 引用 BuildTypes 模块
    # 配置封装到其他语言的项目版本相关,详见Note1
    if (EXISTS "${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl")
        configure_file(${CMAKE_SOURCE_DIR}/bindings/ios/Makefile-tmpl ${CMAKE_SOURCE_DIR}/bindings/ios/Makefile @ONLY)
    endif()
    configure_file(${CMAKE_SOURCE_DIR}/bindings/javascript/package-tmpl.json ${CMAKE_SOURCE_DIR}/bindings/javascript/package.json @ONLY)
    
else() # 如果当前源目录不是根源目录
    set(WHISPER_STANDALONE OFF) # 则将 WHISPER_STANDALONE 标志设置为 OFF
endif()

DefaultTargetOptions

# Set the default compile features and properties for a target.
if (NOT TARGET)
    message(FATAL_ERROR "TARGET not set before including DefaultTargetOptions")
endif()
target_compile_features(${TARGET}
    PRIVATE
        cxx_std_11
    )
set_target_properties(${TARGET}
    PROPERTIES
        EXPORT_COMPILE_COMMANDS ON
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
        INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib"
)

GIT*

find_package(Git)
# the commit's SHA1
execute_process(COMMAND
    "${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=8
    WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    OUTPUT_VARIABLE GIT_SHA1
    ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# the date of the commit
execute_process(COMMAND
    "${GIT_EXECUTABLE}" log -1 --format=%ad --date=local
    WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    OUTPUT_VARIABLE GIT_DATE
    ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# the subject of the commit
execute_process(COMMAND
    "${GIT_EXECUTABLE}" log -1 --format=%s
    WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    OUTPUT_VARIABLE GIT_COMMIT_SUBJECT
    ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)

BuildTypes*

# Add new build types
# ReleaseGG - Release with enabled asserts
SET(CMAKE_CXX_FLAGS_RELEASEGG
    "-O3"
    CACHE STRING "Flags used by the c++ compiler during release builds with enabled asserts."
    FORCE )
SET(CMAKE_C_FLAGS_RELEASEGG
    "-O3"
    CACHE STRING "Flags used by the compiler during release builds with enabled asserts."
    FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_RELEASEGG
    ""
    CACHE STRING "Flags used for linking binaries during release builds with enabled asserts."
    FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_RELEASEGG
    ""
    CACHE STRING "Flags used by the shared libraries linker during release builds with enabled asserts."
    FORCE )
MARK_AS_ADVANCED(
    CMAKE_CXX_FLAGS_RELEASEGG
    CMAKE_C_FLAGS_RELEASEGG
    CMAKE_EXE_LINKER_FLAGS_RELEASEGG
    CMAKE_SHARED_LINKER_FLAGS_RELEASEGG )
# RelWithDebInfoGG - RelWithDebInfo with enabled asserts
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFOGG
    "-O2 -g"
    CACHE STRING "Flags used by the c++ compiler during release builds with debug symbols and enabled asserts."
    FORCE )
SET(CMAKE_C_FLAGS_RELWITHDEBINFOGG
    "-O2 -g"
    CACHE STRING "Flags used by the compiler during release builds with debug symbols and enabled asserts."
    FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFOGG
    ""
    CACHE STRING "Flags used for linking binaries during release builds with debug symbols and enabled asserts."
    FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFOGG
    ""
    CACHE STRING "Flags used by the shared libraries linker during release builds with debug symbols and enabled asserts."
    FORCE )
MARK_AS_ADVANCED(
    CMAKE_CXX_FLAGS_RELWITHDEBINFOGG
    CMAKE_C_FLAGS_RELWITHDEBINFOGG
    CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFOGG
    CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFOGG )
if (NOT XCODE AND NOT MSVC AND NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo" "ReleaseGG" "RelWithDebInfoGG")
endif()

WASM配置*

# 检查是否使用 Emscripten 工具链进行交叉编译
if (EMSCRIPTEN)
    # 如果是 Emscripten,设置默认值为不构建共享库
    set(BUILD_SHARED_LIBS_DEFAULT OFF)
    # 添加选项,用于指定是否将 WASM 嵌入到生成的 whisper.js 文件中
    option(WHISPER_WASM_SINGLE_FILE "whisper: embed WASM inside the generated whisper.js" ON)
else()
    # 如果不是 Emscripten,进一步检查是否使用 MinGW 工具链
    if (MINGW)
        # 如果是 MinGW,设置默认值为不构建共享库
        set(BUILD_SHARED_LIBS_DEFAULT OFF)
    else()
        # 如果不是 Emscripten 且不是 MinGW,设置默认值为构建共享库
        set(BUILD_SHARED_LIBS_DEFAULT ON)
    endif()
endif()

options编译选项

# 如果目标平台是苹果 (Apple)
if (APPLE)
    set(WHISPER_METAL_DEFAULT ON)  # 设置 WHISPER_METAL_DEFAULT 为 ON,Metal是苹果发布的一套操作GPU的编程API
else()
    set(WHISPER_METAL_DEFAULT OFF)# 否则设置 WHISPER_METAL_DEFAULT 为 OFF
endif()
# 定义构建共享库的选项,并设置默认值为 BUILD_SHARED_LIBS_DEFAULT
option(BUILD_SHARED_LIBS              "whisper: build shared libs" ${BUILD_SHARED_LIBS_DEFAULT})
# 编译器选项
# 编译器警告的选项
option(WHISPER_ALL_WARNINGS           "whisper: enable all compiler warnings"                   ON)# 定义启用所有编译器警告的选项
option(WHISPER_ALL_WARNINGS_3RD_PARTY "whisper: enable all compiler warnings in 3rd party libs" OFF)# 定义启用所有第三方库中编译器警告的选项
# 编译器自带的调试工具sanitizers选项:
option(WHISPER_SANITIZE_THREAD        "whisper: enable thread sanitizer"    OFF)# 定义启用线程 Sanitizer 的选项
option(WHISPER_SANITIZE_ADDRESS       "whisper: enable address sanitizer"   OFF)# 定义启用地址 Sanitizer 的选项 ASan 是一种结合编译器插桩和运行时的一种快速内存检测工具,主要用于检测代码中的部分 内存安全 问题
option(WHISPER_SANITIZE_UNDEFINED     "whisper: enable undefined sanitizer" OFF)# 定义启用未定义行为 Sanitizer 的选项
# 是否构建示例和测试
option(WHISPER_BUILD_TESTS            "whisper: build tests"    ${WHISPER_STANDALONE})# 定义构建测试的选项
option(WHISPER_BUILD_EXAMPLES         "whisper: build examples" ${WHISPER_STANDALONE})# 定义构建示例的选项
# 定义支持 libSDL2 的选项,Simple DirectMedia Layer是一个跨平台开发库,旨在通过OpenGL和Direct3D提供对音频、键盘、鼠标、操纵杆和图形硬件的低级别访问。https://github.com/libsdl-org/SDL
option(WHISPER_SDL2                   "whisper: support for libSDL2" OFF)
# 定义禁用 AVX 指令集的选项
option(WHISPER_NO_AVX                 "whisper: disable AVX"  OFF)
# 定义禁用 AVX2 指令集的选项
option(WHISPER_NO_AVX2                "whisper: disable AVX2" OFF)
# 定义禁用 FMA 指令集的选项, FMA指令集是128位元和256位元的流式單指令流多資料流擴充集( SSE )指令集
option(WHISPER_NO_FMA                 "whisper: disable FMA"  OFF)
# 定义禁用 F16c 指令集的选项,F16C指令就是AMD的CVT16指令,Intel换了一个名称,随后AMD也接收了这一称呼。Intel 首次在 2012 年的 Ivy Bridge 微架构处理器上使用。
option(WHISPER_NO_F16C                "whisper: disable F16c" OFF)
# 定义支持 OpenVINO 的选项
option(WHISPER_OPENVINO               "whisper: support for OpenVINO" OFF)
# 如果目标平台是苹果 (Apple)
if (APPLE)
    # 定义禁用 Accelerate framework 的选项
    option(WHISPER_NO_ACCELERATE         "whisper: disable Accelerate framework" OFF)
    # 定义使用 Metal 的选项,并设置默认值为 WHISPER_METAL_DEFAULT
    option(WHISPER_METAL                 "whisper: use Metal"                    ${WHISPER_METAL_DEFAULT})
    # 定义禁用 Metal 调试的选项
    option(WHISPER_METAL_NDEBUG          "whisper: disable Metal debugging"      OFF)
    # 定义启用 Core ML framework 的选项
    option(WHISPER_COREML                "whisper: enable Core ML framework"     OFF)
    # 定义允许使用非-CoreML的回退的选项
    option(WHISPER_COREML_ALLOW_FALLBACK "whisper: allow non-CoreML fallback"    OFF)
else()
    # 如果不是苹果,则定义使用 BLAS 库的选项
    option(WHISPER_BLAS                  "whisper: use BLAS libraries"  OFF)
    # 定义 BLAS 库的供应商选项
    option(WHISPER_BLAS_VENDOR           "whisper: BLAS library vendor" Generic)
    # 定义优先使用 OpenBLAS 的选项
    option(WHISPER_OPENBLAS              "whisper: prefer OpenBLAS"     OFF)
    # 定义支持 cuBLAS 的选项
    option(WHISPER_CUBLAS                "whisper: support for cuBLAS"  OFF)
    # 定义支持 hipBLAS 的选项
    option(WHISPER_HIPBLAS               "whisper: support for hipBLAS" OFF)
    # 定义使用 CLBlast 的选项
    option(WHISPER_CLBLAST               "whisper: use CLBlast"         OFF)
endif()
# 定义启用性能分析的选项
option(WHISPER_PERF "whisper: enable perf timings" OFF)
# sanitizers
# 如果不是使用 MSVC 编译器
if (NOT MSVC)
    # 如果启用 Thread Sanitizer
    if (WHISPER_SANITIZE_THREAD)
        # 添加线程 Sanitizer 的编译选项
        set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -fsanitize=thread")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
    endif()
    # 如果启用 Address Sanitizer
    if (WHISPER_SANITIZE_ADDRESS)
        # 添加地址 Sanitizer 的编译选项,并保留函数调用栈信息
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fsanitize=address -fno-omit-frame-pointer")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
    endif()
    # 如果启用 Undefined Behavior Sanitizer
    if (WHISPER_SANITIZE_UNDEFINED)
        # 添加未定义行为 Sanitizer 的编译选项
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fsanitize=undefined")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
    endif()
endif()
# 以下两行是注释掉的代码,未启用
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffast-math")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")

dependencies依赖

# 使用 find_package 查找 Threads 库,找到 Threads 库后,可以使用 Threads::Threads 在后续的目标中链接该库
find_package(Threads REQUIRED)

if (APPLE)*

# on APPLE
if (APPLE)
    # include Accelerate framework
    if (NOT WHISPER_NO_ACCELERATE)
        find_library(ACCELERATE_FRAMEWORK Accelerate)
        if (ACCELERATE_FRAMEWORK)
            message(STATUS "Accelerate framework found")
            set(WHISPER_EXTRA_LIBS  ${WHISPER_EXTRA_LIBS}  ${ACCELERATE_FRAMEWORK})
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_ACCELERATE)
        else()
            message(FATAL_ERROR "Accelerate framework not found")
        endif()
    endif()
    if (WHISPER_METAL)
        find_library(FOUNDATION_LIBRARY         Foundation              REQUIRED)
        find_library(METAL_FRAMEWORK            Metal                   REQUIRED)
        find_library(METALKIT_FRAMEWORK         MetalKit                REQUIRED)
        if (METAL_FRAMEWORK)
            message(STATUS "Metal framework found")
            set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS}
                ${FOUNDATION_LIBRARY}
                ${METAL_FRAMEWORK}
                ${METALKIT_FRAMEWORK}
                )
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_METAL)
            if (WHISPER_METAL_NDEBUG)
                set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_METAL_NDEBUG)
            endif()
        else()
            message(FATAL_ERROR "Metal framework not found")
        endif()
        set(GGML_SOURCES_METAL ggml-metal.m ggml-metal.h)
        # copy ggml-metal.metal to bin directory
        configure_file(ggml-metal.metal bin/ggml-metal.metal COPYONLY)
    endif()
    if (WHISPER_COREML)
        find_library(FOUNDATION_FRAMEWORK Foundation)
        find_library(COREML_FRAMEWORK CoreML)
        if (COREML_FRAMEWORK)
            message(STATUS "CoreML framework found")
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DWHISPER_USE_COREML)
        else()
            message(FATAL_ERROR "CoreML framework not found")
        endif()
        if (WHISPER_COREML_ALLOW_FALLBACK)
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DWHISPER_COREML_ALLOW_FALLBACK)
        endif()
    endif()
endif()

if (WHISPER_OPENBLAS)*

if (WHISPER_OPENBLAS)
    set(WHISPER_BLAS_VENDOR "OpenBLAS")
    set(WHISPER_BLAS ON)
endif()
if (WHISPER_BLAS)
    if (WIN32)
        if(DEFINED ENV{OPENBLAS_PATH})
            set(BLAS_LIBRARIES $ENV{OPENBLAS_PATH}/lib/libopenblas.dll.a)
            message(STATUS "Libraries ${BLAS_LIBRARIES}")
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_OPENBLAS)
            include_directories($ENV{OPENBLAS_PATH}/include)
            set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${BLAS_LIBRARIES})
        else ()
            message(FATAL_ERROR "BLAS library was not found. Environment variable OPENBLAS_PATH not defined.")
        endif ()
    else ()
        set(BLA_STATIC 1)
        set(BLA_VENDOR ${WHISPER_BLAS_VENDOR})
        set(BLA_SIZEOF_INTEGER 8)
        set(BLA_PREFER_PKGCONFIG 1)
        find_package(BLAS)
        if(BLAS_FOUND)
            message(STATUS "BLAS compatible library found")
            message(STATUS "Libraries ${BLAS_LIBRARIES}")
            find_path(BLAS_INCLUDE_DIRS cblas.h /usr/include/openblas /usr/local/include/openblas $ENV{BLAS_HOME}/include)
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_OPENBLAS)
            include_directories(${BLAS_INCLUDE_DIRS})
            set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${BLAS_LIBRARIES})
        else()
            message(FATAL_ERROR "BLAS library was not found")
        endif()
    endif ()
endif ()
if (WHISPER_CUBLAS)
    cmake_minimum_required(VERSION 3.17)
    find_package(CUDAToolkit)
    if (CUDAToolkit_FOUND)
        message(STATUS "cuBLAS found")
        enable_language(CUDA)
        set(GGML_SOURCES_CUDA ggml-cuda.cu ggml-cuda.h)
        add_compile_definitions(GGML_USE_CUBLAS)
        if (WHISPER_STATIC)
            set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart_static CUDA::cublas_static CUDA::cublasLt_static)
        else()
            set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart CUDA::cublas CUDA::cublasLt)
        endif()
    else()
        message(FATAL_ERROR "cuBLAS not found")
    endif()
endif()
if (WHISPER_HIPBLAS)
    list(APPEND CMAKE_PREFIX_PATH /opt/rocm)
    if (NOT ${CMAKE_C_COMPILER_ID} MATCHES "Clang")
        message(WARNING "Only LLVM is supported for HIP, hint: CC=/opt/rocm/llvm/bin/clang")
    endif()
    if (NOT ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
        message(WARNING "Only LLVM is supported for HIP, hint: CXX=/opt/rocm/llvm/bin/clang++")
    endif()
    find_package(hip)
    find_package(hipblas)
    find_package(rocblas)
    if (${hipblas_FOUND} AND ${hip_FOUND})
        message(STATUS "HIP and hipBLAS found")
        add_compile_definitions(GGML_USE_HIPBLAS GGML_USE_CUBLAS)
        add_library(ggml-rocm OBJECT ggml-cuda.cu ggml-cuda.h)
        set_property(TARGET ggml-rocm PROPERTY POSITION_INDEPENDENT_CODE ON)
        set_source_files_properties(ggml-cuda.cu PROPERTIES LANGUAGE CXX)
        target_link_libraries(ggml-rocm PRIVATE hip::device PUBLIC hip::host roc::rocblas roc::hipblas)
        if (WHISPER_STATIC)
            message(FATAL_ERROR "Static linking not supported for HIP/ROCm")
        endif()
        set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ggml-rocm)
    else()
        message(FATAL_ERROR "hipBLAS or HIP not found. Try setting CMAKE_PREFIX_PATH=/opt/rocm")
    endif()
endif()
if (WHISPER_CLBLAST)
    find_package(CLBlast)
    if (CLBlast_FOUND)
        message(STATUS "CLBlast found")
        set(GGML_SOURCES_OPENCL ggml-opencl.cpp ggml-opencl.h)
        add_compile_definitions(GGML_USE_CLBLAST)
        set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} clblast)
    else()
        message(FATAL_ERROR "CLBlast not found")
    endif()
endif()
if( WHISPER_OPENVINO )
    find_package(OpenVINO REQUIRED COMPONENTS Runtime)
endif()

编译器选项

构建类型,如 Debug、Release*

# compiler flags
# 如果没有设置 CMAKE_BUILD_TYPE(用于指定构建类型,如 Debug、Release)且没有配置 CMAKE_CONFIGURATION_TYPES(多配置构建的情况下),则进行以下设置
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    # 设置构建类型为 Release,并将其缓存
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
    # 设置 CMAKE_BUILD_TYPE 的可选值为 "Debug"、"Release"、"RelWithDebInfo"
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo")
endif ()

warning*

if (WHISPER_ALL_WARNINGS)
    if (NOT MSVC)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \
            -Wall                           \
            -Wextra                         \
            -Wpedantic                      \
            -Wshadow                        \
            -Wcast-qual                     \
            -Wstrict-prototypes             \
            -Wpointer-arith                 \
            -Wno-unused-function            \
        ")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
            -Wall                           \
            -Wextra                         \
            -Wpedantic                      \
            -Wcast-qual                     \
        ")
    else()
        # todo : msvc
    endif()
endif()

MSVC*

if (NOT MSVC)
    # 如果不是使用 MSVC 编译器,则添加编译选项 -Werror=vla,将变长数组 (VLA) 的警告视为错误
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=vla")
    # 下面两行是注释掉的代码,可以用于进一步设置编译选项(未启用)
    #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-math-errno -ffinite-math-only -funsafe-math-optimizations")
endif()
# 输出 CMAKE_SYSTEM_PROCESSOR 的信息到状态消息
message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")

硬件设置(AVX之类的)

if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
    message(STATUS "ARM detected")
elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ppc64le")
    message(STATUS "PowerPC detected")
else()
    message(STATUS "x86 detected")
    if (MSVC)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
        set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /utf-8")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8")
        if(NOT WHISPER_NO_AVX2)
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
            set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX2")
            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /arch:AVX2")
        else()
            if(NOT WHISPER_NO_AVX)
                set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX")
                set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX")
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /arch:AVX")
            endif()
        endif()
    else()
        if (EMSCRIPTEN)
            set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -pthread")
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
        else()
            if(NOT WHISPER_NO_AVX)
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx")
            endif()
            if(NOT WHISPER_NO_AVX2)
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx2")
            endif()
            if(NOT WHISPER_NO_FMA)
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma")
            endif()
            if(NOT WHISPER_NO_F16C)
                set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mf16c")
            endif()
        endif()
    endif()
endif()

配置 可移植操作系统接口POSIX 兼容性

  • POSIX:可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX )
    #
    # POSIX conformance
    #
    # clock_gettime came in POSIX.1b (1993)
    # CLOCK_MONOTONIC came in POSIX.1-2001 / SUSv3 as optional
    # posix_memalign came in POSIX.1-2001 / SUSv3
    # M_PI is an XSI extension since POSIX.1-2001 / SUSv3, came in XPG1 (1985)
    add_compile_definitions(_XOPEN_SOURCE=600) # 通过 add_compile_definitions 命令(Note2),将预处理宏 _XOPEN_SOURCE 的定义设置为 600。这个宏用于启用或禁用一些特定于 UNIX 系统的功能。在这里,设置为 600 表示支持 POSIX.1-2001 / SUSv3 标准及更早的版本。
    # Somehow in OpenBSD whenever POSIX conformance is specified
    # some string functions rely on locale_t availability,
    # which was introduced in POSIX.1-2008, forcing us to go higher
    if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") # OpenBSD 系统,Linux 和 BSD 都是免费的,开源的,类Unix系统。OpenBSD的目标系统类型是服务器、NAS、工作站和嵌入式系统。
        remove_definitions(-D_XOPEN_SOURCE=600)# 移除先前的 -D_XOPEN_SOURCE=600 定义
        add_compile_definitions(_XOPEN_SOURCE=700)# 设置 _XOPEN_SOURCE 的定义为 700
    endif()
    # Data types, macros and functions related to controlling CPU affinity
    # are available on Linux through GNU extensions in libc
    if (CMAKE_SYSTEM_NAME MATCHES "Linux")# Linux 系统
        add_compile_definitions(_GNU_SOURCE)# 添加 _GNU_SOURCE 的编译定义,这个宏是为了启用 GNU C 库(glibc)中的一些特定功能和扩展,包括与 CPU 亲和性相关的数据类型、宏和函数。
    endif()
    # RLIMIT_MEMLOCK came in BSD, is not specified in POSIX.1,
    # and on macOS its availability depends on enabling Darwin extensions
    # similarly on DragonFly, enabling BSD extensions is necessary
    if (CMAKE_SYSTEM_NAME MATCHES "Darwin") # 如果系统是 Darwin(Apple现在的主要操作系统,无论是macOS、iOS还是iPadOS,甚至是HomePod和Apple TV(TvOS)都是建立在Darwin的基础上),则添加 _DARWIN_C_SOURCE 的编译定义
        add_compile_definitions(_DARWIN_C_SOURCE)
    endif()
    if (CMAKE_SYSTEM_NAME MATCHES "DragonFly")# 如果系统是 DragonFly,则添加 _DARWIN_C_SOURCE 的编译定义
        add_compile_definitions(_DARWIN_C_SOURCE)
    endif()
    # alloca is a non-standard interface that is not visible on BSDs when
    # POSIX conformance is specified, but not all of them provide a clean way
    # to enable it in such cases
    if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")# 如果系统是 FreeBSD,则添加 __BSD_VISIBLE 的编译定义
        add_compile_definitions(__BSD_VISIBLE)
    endif()
    if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")# 如果系统是 NetBSD,则添加 _NETBSD_SOURCE 的编译定义
        add_compile_definitions(_NETBSD_SOURCE)
    endif()
    if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")# 如果系统是 OpenBSD,则添加 _BSD_SOURCE 的编译定义
        add_compile_definitions(_BSD_SOURCE)
    endif()
    
    # perf性能测试相关,暂时不是很重要
    if (WHISPER_PERF)
        set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_PERF)
    endif()
    

    Core ML framework和OPENVINO*

    • Core ML 是一个 Apple 框架,允许开发人员轻松集成 机器学习 (ML) 模型到应用中。Core ML 可在 iOS、iPadOS、 watchOS、macOS 和 Apple tvOS。Core ML 引入了公共文件格式 (.mlmodel) 适用于广泛的 ML 方法,包括深度神经网络(卷积 和循环)、树集成(提升树、随机森林、决策树)、 和广义线性模型。Core ML 模型可直接集成到 Xcode 中的 App。 Core ML 通过利用 Apple 硬件并最大限度地减少内存占用和功耗,针对各种型号类型的设备端性能进行了优化。 Core ML 旨在通过利用其 CPU、GPU 和神经网络引擎来优化设备性能,同时将内存和功耗降至最低。应用程序使用 CoreML 应用程序编程接口 (API) 在每个用户拥有的所有设备上进行预测和微调模型。可以转换为Core ML的模型格式有Caffe、Keras、XGBoost、 Scikit-learn 、MXNet 、 LibSVM 和 Torch7。
    • 英特尔的OpenVINO™ 工具套件:一款可轻松实现“一次写入,处处部署”的开源工具套件
      #
      # whisper.coreml - Core ML support
      #
      if (WHISPER_COREML)
          set(TARGET whisper.coreml)
          add_library(${TARGET}
              coreml/whisper-encoder.h
              coreml/whisper-encoder.mm
              coreml/whisper-encoder-impl.h
              coreml/whisper-encoder-impl.m
              )
          include(DefaultTargetOptions)
          target_include_directories(${TARGET} PUBLIC
              .
              )
          target_link_libraries(${TARGET} PRIVATE ${FOUNDATION_FRAMEWORK} ${COREML_FRAMEWORK})
          set_target_properties(${TARGET} PROPERTIES
              COMPILE_FLAGS "-fobjc-arc"
              )
      endif()
      if (WHISPER_OPENVINO)
          set(TARGET whisper.openvino)
          add_library(${TARGET} OBJECT
              openvino/whisper-openvino-encoder.h
              openvino/whisper-openvino-encoder.cpp
              )
          target_include_directories(${TARGET} PUBLIC
              .
              )
          set_property(TARGET ${TARGET} PROPERTY POSITION_INDEPENDENT_CODE ON)
          set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DWHISPER_USE_OPENVINO)
          target_link_libraries(${TARGET} PRIVATE openvino::runtime)
      endif()
      

      whisper库

      #
      # whisper - this is the main library of the project
      #
      set(TARGET whisper)
      # 生成库文件
      add_library(${TARGET}
          ggml.h
          ggml.c
          ggml-alloc.h
          ggml-alloc.c
          ggml-backend.h
          ggml-backend.c
          ggml-quants.h
          ggml-quants.c
          ${GGML_SOURCES_METAL}
          ${GGML_SOURCES_CUDA}
          ${GGML_SOURCES_OPENCL}
          whisper.h
          whisper.cpp
          )
      include(DefaultTargetOptions)
      target_include_directories(${TARGET} PUBLIC
          .
          )# 为TARGET添加头文件搜索路径
      
      • 非重要代码
        if (WHISPER_COREML)
            target_link_libraries(${TARGET} PRIVATE whisper.coreml)
        endif()
        if (WHISPER_OPENVINO)
            target_link_libraries(${TARGET} PRIVATE whisper.openvino)
        endif()
        if (MSVC)
            target_link_libraries(${TARGET} PRIVATE ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT})
            set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -D_CRT_SECURE_NO_WARNINGS)
        else()
            target_link_libraries(${TARGET} PRIVATE m ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT})
        endif()
        
        • 构建共享库
          # 根据构建类型设置库目标的链接和编译定义
          if (BUILD_SHARED_LIBS) # 默认为ON
              # 设置目标链接的库
              target_link_libraries(${TARGET} PUBLIC
                  ${CMAKE_DL_LIBS} # ${CMAKE_DL_LIBS} is the Name of library containing dlopen and dlclose,在 Unix/Linux 系统中用于动态加载共享库的函数.
                  )
              # 为库目标添加编译定义(宏定义,见Note3)
              target_compile_definitions(${TARGET} PUBLIC
                  WHISPER_SHARED
                  GGML_SHARED
                  )
              # 设置库目标的私有编译定义
              target_compile_definitions(${TARGET} PRIVATE
                  WHISPER_BUILD
                  GGML_BUILD
                  )
              if (WHISPER_METAL)# 如果使用 苹果的Metal,则将 ggml-metal.m.metal 文件添加到资源
                  # TODO: 我认为这应该使 ggml-metal.m "看到" "bin" 目录下的 ggml-metal.metal 文件
                  #       但由于某种原因,在这里的工作方式与 llama.cpp 中的不同
                  set_target_properties(${TARGET} PROPERTIES RESOURCE "${CMAKE_CURRENT_SOURCE_DIR}/ggml-metal.metal")
              endif()
          endif()
          
          • 非重要代码
            if (GGML_SOURCES_CUDA)
                message(STATUS "GGML CUDA sources found, configuring CUDA architecture")
                set_property(TARGET whisper PROPERTY CUDA_ARCHITECTURES OFF)
                set_property(TARGET whisper PROPERTY CUDA_SELECT_NVCC_ARCH_FLAGS "Auto")
            endif()
            if (EMSCRIPTEN)
                set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS "-msimd128")
            endif()
            # 使用了 target_compile_definitions 命令,将 WHISPER_EXTRA_FLAGS 中的内容添加到目标(${TARGET})的公共编译定义中。公共编译定义是指在编译目标时可见于整个项目的定义,通常是宏定义。
            target_compile_definitions(${TARGET} PUBLIC
                ${WHISPER_EXTRA_FLAGS}
                )
            

            install

            # 设置目标属性,指定库的公共头文件
            set_target_properties(${TARGET} PROPERTIES PUBLIC_HEADER "whisper.h")
            # 包含GNUInstallDirs模块,该模块定义了一些标准的安装目录变量
            include(GNUInstallDirs)
            # 安装目标文件到指定目录
            install(
                TARGETS ${TARGET}           # 目标名称
                LIBRARY DESTINATION lib     # 库文件的安装目录
                ARCHIVE DESTINATION lib/static  # 静态库文件的安装目录
                RUNTIME DESTINATION bin     # 可执行文件的安装目录
                RESOURCE DESTINATION bin    # 资源文件的安装目录
                PUBLIC_HEADER DESTINATION include  # 公共头文件的安装目录
            )
            

            绑定和测试*

            #
            # bindings
            #
            add_subdirectory(bindings)
            #
            # programs, examples and tests
            #
            if (WHISPER_BUILD_TESTS AND NOT CMAKE_JS_VERSION)
                enable_testing()
                add_subdirectory(tests)
            endif ()
            if (WHISPER_BUILD_EXAMPLES)
                add_subdirectory(examples)
            endif()
            

            构建的过程

            /bin/cmake/linux/bin/cmake --build /home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug --target bench -- -j 8
            gmake[1]: 进入目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            gmake[2]: 进入目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            gmake[3]: 进入目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            Scanning dependencies of target whisper # 构建 `whisper` 库
            gmake[3]: 离开目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            gmake[3]: 进入目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            [ 12%] Building C object CMakeFiles/whisper.dir/ggml.c.o
            [ 25%] Building C object CMakeFiles/whisper.dir/ggml-alloc.c.o
            [ 37%] Building C object CMakeFiles/whisper.dir/ggml-backend.c.o
            [ 50%] Building C object CMakeFiles/whisper.dir/ggml-quants.c.o
            [ 62%] Building CXX object CMakeFiles/whisper.dir/whisper.cpp.o
            /home/pdd/le/whisper.cpp-1.5.0/whisper.cpp: In function ‘ggml_backend* whisper_backend_init(const whisper_context_params&)’:
            /home/pdd/le/whisper.cpp-1.5.0/whisper.cpp:1059:75: warning: unused parameter ‘params’ [-Wunused-parameter]
             1059 | static ggml_backend_t whisper_backend_init(const whisper_context_params & params) {
                  |                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
            /home/pdd/le/whisper.cpp-1.5.0/whisper.cpp: At global scope:
            /home/pdd/le/whisper.cpp-1.5.0/whisper.cpp:205:29: warning: ‘ggml_tensor* ggml_mul_mat_pad(ggml_context*, ggml_tensor*, ggml_tensor*, int)’ defined but not used [-Wunused-function]
              205 | static struct ggml_tensor * ggml_mul_mat_pad(struct ggml_context * ctx, struct ggml_tensor * x, struct ggml_tensor * y, int pad = 32) {
                  |                             ^~~~~~~~~~~~~~~~
            [ 75%] Linking CXX shared library libwhisper.so # 链接 `whisper` 库(`libwhisper.so`)
            gmake[3]: 离开目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            [ 75%] Built target whisper
            gmake[3]: 进入目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            Scanning dependencies of target bench
            gmake[3]: 离开目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            gmake[3]: 进入目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            [ 87%] Building CXX object examples/bench/CMakeFiles/bench.dir/bench.cpp.o
            [100%] Linking CXX executable ../../bin/bench 
            gmake[3]: 离开目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            [100%] Built target bench # 创建 `bench` 可执行文件
            gmake[2]: 离开目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            gmake[1]: 离开目录“/home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug”
            Build finished
            

            CG

            • 有关cmake和CMakeLists.txt

              Note1:configure_file

              configure_file 是 CMake 中用于在构建过程中生成文件的命令。它的主要作用是将源文件的内容复制到目标文件中,并在复制的过程中进行一些文本替换,替换操作依赖于 CMake 变量和配置。

              语法如下:

              configure_file(  [@ONLY] [ESCAPE_QUOTES] [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF|CR]])
              

              其中:

              • 是源文件的路径。
              • 是目标文件的路径。
              • @ONLY 可选参数表示仅在 @ 符号引导的变量上执行替换。不提供 @ONLY 时,所有变量都将替换。
              • ESCAPE_QUOTES 可选参数表示在替换时转义引号。
              • NEWLINE_STYLE 可选参数指定换行符的风格,可以是 UNIX、DOS、WIN32、LF、CRLF 或 CR。

                例子:

                # CMakeLists.txt
                set(VERSION_MAJOR 1)
                set(VERSION_MINOR 0)
                configure_file(config.h.in config.h @ONLY)
                
                // config.h.in
                #define VERSION_MAJOR @VERSION_MAJOR@
                #define VERSION_MINOR @VERSION_MINOR@
                

                在这个例子中,config.h.in 是一个模板文件,其中使用了 @VERSION_MAJOR@ 和 @VERSION_MINOR@ 这样的占位符。通过 configure_file 命令,CMake 将替换这些占位符为相应的变量的值,并生成 config.h 文件。在这个过程中,@ONLY 表示仅替换由 @ 符号引导的变量。这个生成的 config.h 文件可以被项目的源代码使用。

                此外,configure_file 还允许在配置时执行一些 CMake 脚本,进一步扩展了其功能。通过这种方式,可以在构建时生成一些特定于构建配置的文件,例如根据不同平台生成不同的配置文件。

                // https://github1s.com/ggerganov/whisper.cpp/blob/v1.5.0/bindings/javascript/package-tmpl.json
                {
                  "name": "whisper.cpp",
                  "version": "@PROJECT_VERSION@",
                  "description": "Whisper speech recognition",
                  "main": "whisper.js",
                  "scripts": {
                    "test": "echo \"todo: add tests\" && exit 0"
                  },
                  "repository": {
                    "type": "git",
                    "url": "git+https://github.com/ggerganov/whisper.cpp"
                  },
                  "keywords": [
                    "openai",
                    "whisper",
                    "speech-to-text",
                    "speech-recognition",
                    "transformer"
                  ],
                  "author": "Georgi Gerganov",
                  "license": "MIT",
                  "bugs": {
                    "url": "https://github.com/ggerganov/whisper.cpp/issues"
                  },
                  "homepage": "https://github.com/ggerganov/whisper.cpp#readme"
                }
                // https://github1s.com/ggerganov/whisper.cpp/blob/v1.5.0/bindings/javascript/package.json
                {
                  "name": "whisper.cpp",
                  "version": "1.5.0",
                  "description": "Whisper speech recognition",
                  "main": "whisper.js",
                  "scripts": {
                    "test": "echo \"todo: add tests\" && exit 0"
                  },
                  "repository": {
                    "type": "git",
                    "url": "git+https://github.com/ggerganov/whisper.cpp"
                  },
                  "keywords": [
                    "openai",
                    "whisper",
                    "speech-to-text",
                    "speech-recognition",
                    "transformer"
                  ],
                  "author": "Georgi Gerganov",
                  "license": "MIT",
                  "bugs": {
                    "url": "https://github.com/ggerganov/whisper.cpp/issues"
                  },
                  "homepage": "https://github.com/ggerganov/whisper.cpp#readme"
                }
                

                Note2:add_compile_definitions

                add_compile_definitions 是 CMake 中用于向目标添加编译定义(宏定义)的命令。它用于在编译时向源代码中注入预定义的宏,从而影响代码的编译行为。在上下文中,这些宏通常用于控制特定平台或编译器的特定行为。

                语法如下:

                add_compile_definitions(definition1 [definition2 ...])
                
                • definition1, definition2, …: 要添加的编译定义,可以是一个或多个。

                  例如,如果要向编译过程中添加一个名为 ENABLE_FEATURE_X 的宏定义,可以使用如下命令:

                  add_compile_definitions(ENABLE_FEATURE_X)
                  

                  在你提供的代码片段中,add_compile_definitions 用于添加一些特定平台的编译定义,以启用或禁用某些非标准的接口或特性。这样,可以根据操作系统的类型为代码选择性地启用或禁用特定的功能或扩展。

                  Note3:target_compile_definitions