Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/01-ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,8 @@ jobs:
name: Build & Test (Windows)
needs: lint
uses: ./.github/workflows/05-windows-build.yml

build-ios:
name: Build & Test (iOS)
needs: lint
uses: ./.github/workflows/06-ios-build.yml
151 changes: 151 additions & 0 deletions .github/workflows/06-ios-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: iOS Cross Build

on:
workflow_call:
workflow_dispatch:

permissions:
contents: read

jobs:
build-ios:
runs-on: macos-15
strategy:
fail-fast: false
matrix:
include:
- platform: SIMULATORARM64
arch: arm64
sdk: iphonesimulator
test_on_simulator: true
- platform: OS
arch: arm64
sdk: iphoneos
test_on_simulator: false

name: iOS (${{ matrix.platform }})

steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive

- name: Cache host protoc build
uses: actions/cache@v5
with:
path: build_host
key: macos-host-protoc-${{ hashFiles('src/**', 'CMakeLists.txt') }}
restore-keys: |
macos-host-protoc-

- name: Build host protoc
run: |
if [ ! -f "build_host/bin/protoc" ]; then
cmake -S . -B build_host -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5
cmake --build build_host --target protoc --parallel $(sysctl -n hw.ncpu)
else
echo "Using cached host protoc"
fi

- name: Cache iOS build
uses: actions/cache@v5
with:
path: build_ios_${{ matrix.platform }}
key: ios-build-${{ matrix.platform }}-${{ hashFiles('src/**', 'CMakeLists.txt', 'cmake/**', 'thirdparty/**') }}

- name: Configure and Build
run: |
git submodule foreach --recursive 'git stash --include-untracked' || true

SDK_PATH=$(xcrun --sdk ${{ matrix.sdk }} --show-sdk-path)
NPROC=$(sysctl -n hw.ncpu)

cmake -S . -B build_ios_${{ matrix.platform }} \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_OSX_DEPLOYMENT_TARGET="13.0" \
-DCMAKE_OSX_ARCHITECTURES="${{ matrix.arch }}" \
-DCMAKE_OSX_SYSROOT="$SDK_PATH" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_PYTHON_BINDINGS=OFF \
-DBUILD_TOOLS=OFF \
-DCMAKE_INSTALL_PREFIX="./install" \
-DGLOBAL_CC_PROTOBUF_PROTOC="$GITHUB_WORKSPACE/build_host/bin/protoc" \
-DIOS=ON \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5

cmake --build build_ios_${{ matrix.platform }} --parallel $NPROC

- name: Build test targets
if: matrix.test_on_simulator
run: |
NPROC=$(sysctl -n hw.ncpu)
cmake --build build_ios_${{ matrix.platform }} --target unittest --parallel $NPROC

- name: Boot iOS Simulator
if: matrix.test_on_simulator
run: |
DEVICE_ID=$(xcrun simctl list devices available -j \
| python3 -c "
import json, sys
data = json.load(sys.stdin)
for runtime, devices in data['devices'].items():
if 'iOS' in runtime:
for d in devices:
if 'iPhone' in d['name'] and d['isAvailable']:
print(d['udid'])
sys.exit(0)
sys.exit(1)
")
echo "DEVICE_ID=$DEVICE_ID" >> $GITHUB_ENV
xcrun simctl boot "$DEVICE_ID"
echo "Booted simulator: $DEVICE_ID"

- name: Run all tests on simulator
if: matrix.test_on_simulator
run: |
FAILED_TESTS=""
PASSED=0
TOTAL=0

for APP in build_ios_${{ matrix.platform }}/bin/*_test.app; do
[ -d "$APP" ] || continue
TEST_NAME=$(basename "$APP" .app)
BUNDLE_ID="com.zvec.${TEST_NAME}"
TOTAL=$((TOTAL + 1))

echo "::group::Running ${TEST_NAME}"
xcrun simctl install "$DEVICE_ID" "$APP"
set +eo pipefail
xcrun simctl launch --console "$DEVICE_ID" "$BUNDLE_ID" 2>&1 | tee /tmp/${TEST_NAME}.log
LAUNCH_EXIT=${PIPESTATUS[0]}
set -eo pipefail

if grep -q '\[ FAILED \]' /tmp/${TEST_NAME}.log; then
echo "::error::${TEST_NAME} has failing tests"
FAILED_TESTS="${FAILED_TESTS} ${TEST_NAME}"
elif grep -q '\[ PASSED \]' /tmp/${TEST_NAME}.log; then
PASSED=$((PASSED + 1))
elif grep -qE 'Failed: 0$' /tmp/${TEST_NAME}.log; then
# c_api_test uses a custom test framework (not GTest)
PASSED=$((PASSED + 1))
elif [ "$LAUNCH_EXIT" -eq 0 ]; then
echo "::warning::${TEST_NAME} exited 0 but produced no recognisable test summary"
PASSED=$((PASSED + 1))
else
echo "::error::${TEST_NAME} exited ${LAUNCH_EXIT} with no test summary"
FAILED_TESTS="${FAILED_TESTS} ${TEST_NAME}"
fi
echo "::endgroup::"
done

echo "Test summary: ${PASSED}/${TOTAL} passed"
if [ -n "$FAILED_TESTS" ]; then
echo "::error::Failed tests:${FAILED_TESTS}"
exit 1
fi

- name: Shutdown Simulator
if: matrix.test_on_simulator && always()
run: |
xcrun simctl shutdown "$DEVICE_ID" || true
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ yarn-error.log*

allure-*

!build_android.sh
!build_android.sh
!build_ios.sh

22 changes: 19 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror=return-type")
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT IOS)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-as-needed")
endif()
Expand All @@ -47,7 +47,19 @@ message(STATUS "PROJECT_ROOT_DIR = ${PROJECT_ROOT_DIR}")
include(${PROJECT_ROOT_DIR}/cmake/bazel.cmake)
include(${PROJECT_ROOT_DIR}/cmake/option.cmake)

if (NOT ANDROID AND AUTO_DETECT_ARCH AND HOST_ARCH MATCHES "^(x86|x64)$")
# iOS platform detection
if(NOT ANDROID AND NOT IOS AND CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(IOS TRUE)
endif()

# iOS bundle properties for test executables
if(IOS)
set(MACOSX_BUNDLE_BUNDLE_VERSION "1")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0")
set(CMAKE_MACOSX_BUNDLE_INFO_PLIST "${PROJECT_ROOT_DIR}/cmake/iOSBundleInfo.plist.in")
endif()

if (NOT ANDROID AND NOT IOS AND AUTO_DETECT_ARCH AND HOST_ARCH MATCHES "^(x86|x64)$")
setup_compiler_march_for_x86(MATH_MARCH_FLAG_SSE MATH_MARCH_FLAG_AVX2 MATH_MARCH_FLAG_AVX512 MATH_MARCH_FLAG_AVX512FP16)
message(STATUS "best compiler march, sse: " ${MATH_MARCH_FLAG_SSE} ", avx2: " ${MATH_MARCH_FLAG_AVX2} ", avx512: " ${MATH_MARCH_FLAG_AVX512} ", avx512fp16: " ${MATH_MARCH_FLAG_AVX512FP16})
endif()
Expand All @@ -66,7 +78,7 @@ message(STATUS "BUILD_TOOLS:${BUILD_TOOLS}")

option(RABITQ_ENABLE_AVX512 "Compile RaBitQ with AVX-512 support" OFF)

if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64" AND NOT ANDROID)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64" AND NOT ANDROID AND NOT IOS)
include(CheckCCompilerFlag)

check_c_compiler_flag("-mavx2" COMPILER_SUPPORTS_AVX2)
Expand All @@ -86,6 +98,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64
add_definitions(-DRABITQ_SUPPORTED=0)
message(STATUS "RaBitQ support disabled - compiler does not support AVX2 or AVX-512")
endif()
elseif(IOS)
set(RABITQ_SUPPORTED OFF)
add_definitions(-DRABITQ_SUPPORTED=0)
message(STATUS "RaBitQ support disabled - not supported on iOS")
else()
set(RABITQ_SUPPORTED OFF)
add_definitions(-DRABITQ_SUPPORTED=0)
Expand Down
66 changes: 55 additions & 11 deletions cmake/bazel.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@
## )
##

cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
include(CMakeParseArguments)

# Using AppleClang instead of Clang (Compiler id)
Expand All @@ -314,11 +314,16 @@ enable_testing()

# Add unittest target
if(NOT TARGET unittest)
add_custom_target(
unittest
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
--build-config $<CONFIGURATION>
)
if(IOS)
# iOS: build-only target; tests are run on simulator separately
add_custom_target(unittest)
else()
add_custom_target(
unittest
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
--build-config $<CONFIGURATION>
)
endif()
endif()

# Directories of target output
Expand Down Expand Up @@ -584,9 +589,16 @@ macro(_add_library _NAME _OPTION)
add_library(
${_NAME}_static STATIC ${_OPTION} $<TARGET_OBJECTS:${_NAME}_objects>
)
add_library(
${_NAME} SHARED ${_OPTION} $<TARGET_OBJECTS:${_NAME}_objects>
)
if(IOS)
# iOS: create the main target as static too (no shared libs on iOS)
add_library(
${_NAME} STATIC ${_OPTION} $<TARGET_OBJECTS:${_NAME}_objects>
)
else()
add_library(
${_NAME} SHARED ${_OPTION} $<TARGET_OBJECTS:${_NAME}_objects>
)
endif()
add_dependencies(${_NAME} ${_NAME}_static)
if(NOT MSVC)
set_property(TARGET ${_NAME}_static PROPERTY OUTPUT_NAME ${_NAME})
Expand Down Expand Up @@ -708,7 +720,7 @@ function(_target_link_libraries _NAME)
endif()

if(NOT MSVC)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "iOS")
list(APPEND LINK_LIBS -Wl,--whole-archive ${LIB} -Wl,--no-whole-archive)
else()
list(APPEND LINK_LIBS -Wl,-force_load ${LIB})
Expand Down Expand Up @@ -1011,6 +1023,13 @@ function(cc_binary)
endif()
add_executable(${CC_ARGS_NAME} ${CC_ARGS_SRCS})

# iOS: set bundle properties for simulator/device installation
if(IOS)
set_target_properties(${CC_ARGS_NAME} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_ROOT_DIR}/cmake/iOSBundleInfo.plist.in"
)
endif()

if(CC_ARGS_PACKED)
install(
TARGETS ${CC_ARGS_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
Expand Down Expand Up @@ -1051,8 +1070,33 @@ function(cc_test)
string(REPLACE "-" "_" MACRO_PREFIX "${CC_ARGS_NAME}")
list(APPEND CC_ARGS_DEFS ${MACRO_PREFIX}_VERSION="${CC_ARGS_VERSION}")
endif()
# iOS: add sandbox helper to redirect CWD to writable directory
if(IOS)
list(APPEND CC_ARGS_SRCS "${PROJECT_ROOT_DIR}/tests/ios_test_sandbox.cc")
# Arrow's iOS code references CoreFoundation symbols; link Apple frameworks
list(APPEND CC_ARGS_LDFLAGS
-framework CoreFoundation
-framework CoreGraphics
-framework CoreData
-framework CoreText
-framework Security
-framework Foundation
-Wl,-U,_MallocExtension_ReleaseFreeMemory
-Wl,-U,_ProfilerStart
-Wl,-U,_ProfilerStop
-Wl,-U,_RegisterThriftProtocol
)
endif()

add_executable(${CC_ARGS_NAME} EXCLUDE_FROM_ALL ${CC_ARGS_SRCS})

# iOS: set bundle properties for simulator/device installation
if(IOS)
set_target_properties(${CC_ARGS_NAME} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_ROOT_DIR}/cmake/iOSBundleInfo.plist.in"
)
endif()

_cc_target_properties(
NAME "${CC_ARGS_NAME}"
INCS "${CC_ARGS_INCS}"
Expand Down Expand Up @@ -2133,7 +2177,7 @@ function(_fetch_content)

set(
CMAKELISTS_CONTENT
"cmake_minimum_required(VERSION 3.1)\n"
"cmake_minimum_required(VERSION 3.13)\n"
"project(${DL_ARGS_NAME})\n"
"include(ExternalProject)\n"
"ExternalProject_Add(\n"
Expand Down
22 changes: 22 additions & 0 deletions cmake/iOSBundleInfo.plist.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.zvec.${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
</dict>
</plist>
5 changes: 5 additions & 0 deletions cmake/option.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ function(setup_compiler_march_for_x86 VAR_NAME_SSE VAR_NAME_AVX2 VAR_NAME_AVX512
endforeach()
endfunction()

# iOS: Skip -march flags and OpenMP; architecture is controlled by CMAKE_OSX_ARCHITECTURES
if(IOS OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
return()
endif()

if(NOT AUTO_DETECT_ARCH)
if(ENABLE_NATIVE)
if (NOT MSVC)
Expand Down
Loading
Loading