425 lines
17 KiB
CMake
425 lines
17 KiB
CMake
# =============================================================================
|
||
# generate_podspec.cmake
|
||
# 从 CMake target 中自动提取源文件列表,生成 MNN.podspec
|
||
#
|
||
# 使用方法:
|
||
# 在 CMakeLists.txt 末尾(所有 target 定义之后):
|
||
# include(cmake/generate_podspec.cmake)
|
||
# generate_mnn_podspec_from_targets()
|
||
# =============================================================================
|
||
|
||
cmake_minimum_required(VERSION 3.6)
|
||
|
||
# =============================================================================
|
||
# Configuration
|
||
# =============================================================================
|
||
# Use MNN_VERSION from CMakeLists.txt as default
|
||
if(NOT DEFINED MNN_PODSPEC_VERSION OR MNN_PODSPEC_VERSION STREQUAL "")
|
||
set(MNN_PODSPEC_VERSION "${MNN_VERSION}" CACHE STRING "MNN podspec version" FORCE)
|
||
endif()
|
||
set(MNN_IOS_DEPLOYMENT_TARGET "11.0" CACHE STRING "iOS deployment target")
|
||
|
||
# =============================================================================
|
||
# 辅助函数:获取 target 的所有源文件
|
||
# =============================================================================
|
||
function(get_target_sources_recursive TARGET OUTPUT_VAR)
|
||
set(RESULT "")
|
||
|
||
# 获取 target 的源文件
|
||
get_target_property(SRCS ${TARGET} SOURCES)
|
||
if(SRCS)
|
||
list(APPEND RESULT ${SRCS})
|
||
endif()
|
||
|
||
# 获取 OBJECT 库的源文件
|
||
get_target_property(INTERFACE_SOURCES ${TARGET} INTERFACE_SOURCES)
|
||
if(INTERFACE_SOURCES)
|
||
list(APPEND RESULT ${INTERFACE_SOURCES})
|
||
endif()
|
||
|
||
set(${OUTPUT_VAR} ${RESULT} PARENT_SCOPE)
|
||
endfunction()
|
||
|
||
# =============================================================================
|
||
# Helper: Convert absolute paths to relative paths (filter out directories)
|
||
# =============================================================================
|
||
function(convert_to_relative_paths SOURCE_LIST ROOT_DIR OUTPUT_VAR)
|
||
set(RESULT "")
|
||
foreach(SRC ${SOURCE_LIST})
|
||
if(IS_ABSOLUTE ${SRC})
|
||
# Skip directories
|
||
if(IS_DIRECTORY ${SRC})
|
||
continue()
|
||
endif()
|
||
file(RELATIVE_PATH REL_PATH "${ROOT_DIR}" "${SRC}")
|
||
list(APPEND RESULT "${REL_PATH}")
|
||
else()
|
||
# Skip if it looks like a directory (no extension)
|
||
get_filename_component(EXT "${SRC}" EXT)
|
||
if(NOT EXT STREQUAL "")
|
||
list(APPEND RESULT "${SRC}")
|
||
endif()
|
||
endif()
|
||
endforeach()
|
||
set(${OUTPUT_VAR} ${RESULT} PARENT_SCOPE)
|
||
endfunction()
|
||
|
||
# =============================================================================
|
||
# 辅助函数:按扩展名分类文件
|
||
# =============================================================================
|
||
function(classify_files FILE_LIST CPP_OUT HEADER_OUT ASM_OUT MM_OUT METAL_OUT OTHER_OUT)
|
||
set(CPP_FILES "")
|
||
set(HEADER_FILES "")
|
||
set(ASM_FILES "")
|
||
set(MM_FILES "")
|
||
set(METAL_FILES "")
|
||
set(OTHER_FILES "")
|
||
|
||
foreach(FILE ${FILE_LIST})
|
||
get_filename_component(EXT "${FILE}" EXT)
|
||
string(TOLOWER "${EXT}" EXT_LOWER)
|
||
|
||
if(EXT_LOWER MATCHES "\\.(cpp|cc|c)$")
|
||
list(APPEND CPP_FILES "${FILE}")
|
||
elseif(EXT_LOWER MATCHES "\\.(h|hpp)$")
|
||
list(APPEND HEADER_FILES "${FILE}")
|
||
elseif(EXT_LOWER MATCHES "\\.(s|S)$")
|
||
list(APPEND ASM_FILES "${FILE}")
|
||
elseif(EXT_LOWER STREQUAL ".mm")
|
||
list(APPEND MM_FILES "${FILE}")
|
||
elseif(EXT_LOWER STREQUAL ".metal")
|
||
list(APPEND METAL_FILES "${FILE}")
|
||
else()
|
||
list(APPEND OTHER_FILES "${FILE}")
|
||
endif()
|
||
endforeach()
|
||
|
||
set(${CPP_OUT} ${CPP_FILES} PARENT_SCOPE)
|
||
set(${HEADER_OUT} ${HEADER_FILES} PARENT_SCOPE)
|
||
set(${ASM_OUT} ${ASM_FILES} PARENT_SCOPE)
|
||
set(${MM_OUT} ${MM_FILES} PARENT_SCOPE)
|
||
set(${METAL_OUT} ${METAL_FILES} PARENT_SCOPE)
|
||
set(${OTHER_OUT} ${OTHER_FILES} PARENT_SCOPE)
|
||
endfunction()
|
||
|
||
# =============================================================================
|
||
# 辅助函数:格式化文件列表为 podspec 格式(每行一个)
|
||
# =============================================================================
|
||
function(format_file_list_for_podspec FILE_LIST OUTPUT_VAR INDENT)
|
||
set(RESULT "")
|
||
list(LENGTH FILE_LIST LEN)
|
||
math(EXPR LAST_INDEX "${LEN} - 1")
|
||
set(INDEX 0)
|
||
|
||
foreach(FILE ${FILE_LIST})
|
||
if(INDEX EQUAL LAST_INDEX)
|
||
string(APPEND RESULT "${INDENT}'${FILE}'")
|
||
else()
|
||
string(APPEND RESULT "${INDENT}'${FILE}',\n")
|
||
endif()
|
||
math(EXPR INDEX "${INDEX} + 1")
|
||
endforeach()
|
||
|
||
set(${OUTPUT_VAR} "${RESULT}" PARENT_SCOPE)
|
||
endfunction()
|
||
|
||
# =============================================================================
|
||
# 验证函数:检查生成 podspec 的前置条件
|
||
# =============================================================================
|
||
function(validate_podspec_generation)
|
||
set(MNN_ROOT "${CMAKE_SOURCE_DIR}")
|
||
|
||
# 1. 检查 MNN_ROOT 目录是否存在
|
||
if(NOT IS_DIRECTORY "${MNN_ROOT}")
|
||
message(FATAL_ERROR "MNN source directory not found: ${MNN_ROOT}")
|
||
endif()
|
||
|
||
# 2. 检查必要目录是否存在
|
||
set(REQUIRED_DIRS "include/MNN" "source" "3rd_party")
|
||
foreach(dir ${REQUIRED_DIRS})
|
||
if(NOT IS_DIRECTORY "${MNN_ROOT}/${dir}")
|
||
message(FATAL_ERROR "Required directory not found: ${dir}\nPlease ensure MNN source structure is complete.")
|
||
endif()
|
||
endforeach()
|
||
|
||
# 3. 检查 LICENSE 文件是否存在
|
||
if(NOT EXISTS "${MNN_ROOT}/LICENSE")
|
||
message(WARNING "LICENSE file not found. Podspec license field may be invalid.")
|
||
endif()
|
||
|
||
# 4. 检查 MNN_TARGETS 是否已定义且非空
|
||
if(NOT DEFINED MNN_TARGETS)
|
||
message(FATAL_ERROR "MNN_TARGETS is not defined. Please include generate_podspec.cmake after all targets are created.")
|
||
endif()
|
||
list(LENGTH MNN_TARGETS TARGETS_COUNT)
|
||
if(TARGETS_COUNT EQUAL 0)
|
||
message(FATAL_ERROR "MNN_TARGETS is empty. No targets to generate podspec from.")
|
||
endif()
|
||
|
||
# 5. 检查版本号是否有效
|
||
if(NOT DEFINED MNN_PODSPEC_VERSION OR MNN_PODSPEC_VERSION STREQUAL "")
|
||
message(FATAL_ERROR "MNN_PODSPEC_VERSION is not set. Please define MNN_VERSION or pass -DMNN_PODSPEC_VERSION=x.x.x")
|
||
endif()
|
||
|
||
message(STATUS "Validation passed: All prerequisites met for podspec generation.")
|
||
endfunction()
|
||
|
||
# =============================================================================
|
||
# 主函数:从 CMake targets 生成 podspec
|
||
# =============================================================================
|
||
function(generate_mnn_podspec_from_targets)
|
||
set(MNN_ROOT "${CMAKE_SOURCE_DIR}")
|
||
|
||
message(STATUS "========================================")
|
||
message(STATUS "Generating MNN.podspec from CMake targets...")
|
||
message(STATUS "========================================")
|
||
|
||
# =============================================================================
|
||
# 0. 验证前置条件
|
||
# =============================================================================
|
||
validate_podspec_generation()
|
||
|
||
# =============================================================================
|
||
# 1. Collect source files from all MNN targets
|
||
# Using MNN_TARGETS variable defined in CMakeLists.txt (fully automatic!)
|
||
# =============================================================================
|
||
set(ALL_SOURCE_FILES "")
|
||
set(ARM82_SOURCE_FILES "")
|
||
|
||
# MNN_TARGETS is already defined in CMakeLists.txt and contains all targets
|
||
# that are linked to the final MNN library. We just iterate over them!
|
||
message(STATUS "Collecting sources from MNN_TARGETS: ${MNN_TARGETS}")
|
||
|
||
foreach(TGT ${MNN_TARGETS})
|
||
if(TARGET ${TGT})
|
||
get_target_property(SRCS ${TGT} SOURCES)
|
||
if(SRCS)
|
||
# ARM82 needs special compiler flags, collect separately
|
||
if("${TGT}" STREQUAL "MNN_Arm82")
|
||
list(APPEND ARM82_SOURCE_FILES ${SRCS})
|
||
message(STATUS " [ARM82] ${TGT}: collecting separately")
|
||
else()
|
||
list(APPEND ALL_SOURCE_FILES ${SRCS})
|
||
list(LENGTH SRCS SRC_COUNT)
|
||
message(STATUS " ${TGT}: ${SRC_COUNT} files")
|
||
endif()
|
||
endif()
|
||
endif()
|
||
endforeach()
|
||
|
||
# Also collect ARM targets (MNNARM64, MNNARM32) which may not be in MNN_TARGETS
|
||
foreach(TGT MNNARM64 MNNARM32)
|
||
if(TARGET ${TGT})
|
||
get_target_property(SRCS ${TGT} SOURCES)
|
||
if(SRCS)
|
||
list(APPEND ALL_SOURCE_FILES ${SRCS})
|
||
list(LENGTH SRCS SRC_COUNT)
|
||
message(STATUS " ${TGT}: ${SRC_COUNT} files")
|
||
endif()
|
||
endif()
|
||
endforeach()
|
||
|
||
# Collect ARM82 if it exists but wasn't in MNN_TARGETS
|
||
if(TARGET MNN_Arm82)
|
||
get_target_property(SRCS MNN_Arm82 SOURCES)
|
||
if(SRCS AND NOT ARM82_SOURCE_FILES)
|
||
list(APPEND ARM82_SOURCE_FILES ${SRCS})
|
||
list(LENGTH SRCS SRC_COUNT)
|
||
message(STATUS " [ARM82] MNN_Arm82: ${SRC_COUNT} files")
|
||
endif()
|
||
endif()
|
||
|
||
# 去重
|
||
list(REMOVE_DUPLICATES ALL_SOURCE_FILES)
|
||
list(REMOVE_DUPLICATES ARM82_SOURCE_FILES)
|
||
|
||
# 转换为相对路径
|
||
convert_to_relative_paths("${ALL_SOURCE_FILES}" "${MNN_ROOT}" ALL_SOURCE_FILES_REL)
|
||
convert_to_relative_paths("${ARM82_SOURCE_FILES}" "${MNN_ROOT}" ARM82_SOURCE_FILES_REL)
|
||
|
||
# 分类文件
|
||
classify_files("${ALL_SOURCE_FILES_REL}"
|
||
CPP_FILES HEADER_FILES ASM_FILES MM_FILES METAL_FILES OTHER_FILES)
|
||
classify_files("${ARM82_SOURCE_FILES_REL}"
|
||
ARM82_CPP ARM82_HEADER ARM82_ASM ARM82_MM ARM82_METAL ARM82_OTHER)
|
||
|
||
# 统计
|
||
list(LENGTH ALL_SOURCE_FILES_REL TOTAL_FILES)
|
||
list(LENGTH ARM82_SOURCE_FILES_REL ARM82_TOTAL_FILES)
|
||
message(STATUS "Collected ${TOTAL_FILES} source files from CMake targets")
|
||
message(STATUS "Collected ${ARM82_TOTAL_FILES} ARM82 files (separate subspec)")
|
||
|
||
|
||
|
||
# =============================================================================
|
||
# 3. 收集公开头文件
|
||
# =============================================================================
|
||
file(GLOB_RECURSE PUBLIC_HEADERS
|
||
"${MNN_ROOT}/include/MNN/*.h"
|
||
"${MNN_ROOT}/include/MNN/*.hpp"
|
||
)
|
||
convert_to_relative_paths("${PUBLIC_HEADERS}" "${MNN_ROOT}" PUBLIC_HEADERS_REL)
|
||
|
||
# =============================================================================
|
||
# 4. 格式化文件列表
|
||
# =============================================================================
|
||
format_file_list_for_podspec("${ALL_SOURCE_FILES_REL}" SOURCE_FILES_FORMATTED " ")
|
||
format_file_list_for_podspec("${PUBLIC_HEADERS_REL}" PUBLIC_HEADERS_FORMATTED " ")
|
||
format_file_list_for_podspec("${ARM82_SOURCE_FILES_REL}" ARM82_FILES_FORMATTED " ")
|
||
|
||
# 收集 MM 文件路径(用于 requires_arc)
|
||
set(ARC_FILES "")
|
||
foreach(FILE ${MM_FILES})
|
||
list(APPEND ARC_FILES "${FILE}")
|
||
endforeach()
|
||
format_file_list_for_podspec("${ARC_FILES}" ARC_FILES_FORMATTED " ")
|
||
|
||
# =============================================================================
|
||
# 5. 生成 podspec 内容
|
||
# =============================================================================
|
||
set(PODSPEC_CONTENT "# =============================================================================
|
||
# MNN.podspec
|
||
# Auto-generated by CMake, DO NOT EDIT!
|
||
# Command: cmake .. -DMNN_GENERATE_PODSPEC=ON
|
||
# =============================================================================
|
||
|
||
Pod::Spec.new do |s|
|
||
s.name = \"MNN\"
|
||
s.version = \"${MNN_PODSPEC_VERSION}\"
|
||
s.summary = \"MNN - A lightweight deep neural network inference framework\"
|
||
|
||
s.description = <<-DESC
|
||
MNN is a blazing fast, lightweight deep neural network inference engine.
|
||
Auto-generated from CMake configuration.
|
||
DESC
|
||
|
||
s.homepage = \"https://github.com/alibaba/MNN\"
|
||
s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' }
|
||
s.author = { \"MNN\" => \"mnn@alibaba-inc.com\" }
|
||
|
||
s.platform = :ios
|
||
s.ios.deployment_target = '${MNN_IOS_DEPLOYMENT_TARGET}'
|
||
|
||
# .mm files require ARC
|
||
s.requires_arc = [
|
||
${ARC_FILES_FORMATTED}
|
||
]
|
||
|
||
s.source = { :git => \"https://github.com/alibaba/MNN.git\", :tag => \"\#{s.version}\" }
|
||
|
||
s.frameworks = 'Metal', 'Accelerate', 'CoreVideo', 'Foundation'
|
||
s.weak_frameworks = 'MetalPerformanceShaders'
|
||
s.libraries = 'c++'
|
||
|
||
# =============================================================================
|
||
# Source files - Auto-extracted from CMake targets
|
||
# Total ${TOTAL_FILES} files
|
||
# =============================================================================
|
||
s.source_files = [
|
||
# Public headers
|
||
'include/MNN/*.{h,hpp}',
|
||
'include/MNN/expr/*.{h,hpp}',
|
||
'schema/current/*.{h}',
|
||
'3rd_party/flatbuffers/include/flatbuffers/*.{h}',
|
||
'3rd_party/half/*.{hpp}',
|
||
|
||
# Source files extracted from CMake targets
|
||
${SOURCE_FILES_FORMATTED}
|
||
]
|
||
|
||
# =============================================================================
|
||
# Public headers
|
||
# =============================================================================
|
||
s.public_header_files = [
|
||
${PUBLIC_HEADERS_FORMATTED}
|
||
]
|
||
|
||
# =============================================================================
|
||
# Build configuration
|
||
# =============================================================================
|
||
s.pod_target_xcconfig = {
|
||
'HEADER_SEARCH_PATHS' => [
|
||
'\"\$(PODS_TARGET_SRCROOT)/include\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/express\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/tools\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/schema/current\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/3rd_party\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/3rd_party/flatbuffers/include\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/3rd_party/half\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source/backend/cpu\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source/backend/cpu/arm\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source/backend/arm82\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source/backend/arm82/compute\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source/backend/arm82/asm\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/source/backend/metal\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/tools/cv/include\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/transformers/llm/engine/include\"',
|
||
'\"\$(PODS_TARGET_SRCROOT)/transformers/llm/engine/src\"'
|
||
].join(' '),
|
||
'GCC_PREPROCESSOR_DEFINITIONS' => [
|
||
'\$(inherited)',
|
||
'MNN_METAL_ENABLED=1',
|
||
'ENABLE_ARMV82=1',
|
||
'MNN_USE_NEON=1',
|
||
'MNN_SUPPORT_TRANSFORMER_FUSE=1',
|
||
'MNN_LOW_MEMORY=1',
|
||
'MNN_IMGPROC_DRAW=1',
|
||
'MNN_REDUCE_SIZE=1'
|
||
].join(' '),
|
||
'OTHER_CFLAGS' => '-fno-rtti -fno-exceptions -Oz',
|
||
'OTHER_CPLUSPLUSFLAGS' => '-std=c++17 -fno-rtti -fno-exceptions -Oz',
|
||
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
|
||
'METAL_LIBRARY_FILE_BASE' => 'mnn',
|
||
'ARCHS' => 'arm64',
|
||
'VALID_ARCHS' => 'arm64',
|
||
# Optimization: remove debug info and strip
|
||
'GCC_OPTIMIZATION_LEVEL' => 'z',
|
||
'DEBUG_INFORMATION_FORMAT' => 'dwarf',
|
||
'STRIP_INSTALLED_PRODUCT' => 'YES',
|
||
'STRIP_STYLE' => 'non-global',
|
||
'DEAD_CODE_STRIPPING' => 'YES',
|
||
'DEPLOYMENT_POSTPROCESSING' => 'YES'
|
||
}
|
||
|
||
# =============================================================================
|
||
# ARM82 FP16 - Requires -march=armv8.2-a+fp16 compiler flag
|
||
# Total ${ARM82_TOTAL_FILES} files
|
||
# =============================================================================
|
||
s.subspec 'ARM82' do |sp|
|
||
sp.source_files = [
|
||
${ARM82_FILES_FORMATTED}
|
||
]
|
||
sp.pod_target_xcconfig = {
|
||
'OTHER_CFLAGS' => '-march=armv8.2-a+fp16 -fno-rtti -fno-exceptions',
|
||
'OTHER_CPLUSPLUSFLAGS' => '-std=c++17 -march=armv8.2-a+fp16 -fno-rtti -fno-exceptions'
|
||
}
|
||
sp.xcconfig = {
|
||
'OTHER_ASFLAGS' => '-march=armv8.2-a+fp16'
|
||
}
|
||
end
|
||
|
||
s.default_subspecs = ['ARM82']
|
||
|
||
end
|
||
")
|
||
|
||
# =============================================================================
|
||
# 6. 写入文件
|
||
# =============================================================================
|
||
set(PODSPEC_FILE "${MNN_ROOT}/MNN.podspec")
|
||
file(WRITE "${PODSPEC_FILE}" "${PODSPEC_CONTENT}")
|
||
|
||
message(STATUS "========================================")
|
||
message(STATUS "Generated: ${PODSPEC_FILE}")
|
||
message(STATUS "Total files: ${TOTAL_FILES}")
|
||
message(STATUS "ARM82 files: ${ARM82_TOTAL_FILES}")
|
||
message(STATUS "========================================")
|
||
endfunction()
|
||
|
||
# =============================================================================
|
||
# 如果直接调用
|
||
# =============================================================================
|
||
if(MNN_GENERATE_PODSPEC)
|
||
generate_mnn_podspec_from_targets()
|
||
endif() |