diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2d47d57 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,217 @@ +name: build, test and release sqlite-ai +on: + push: + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + runs-on: ${{ matrix.os }} + name: ${{ matrix.name }}${{ matrix.arch && format('-{0}', matrix.arch) || '' }} build${{ matrix.arch != 'arm64-v8a' && matrix.name != 'isim' && matrix.name != 'ios' && ' + test' || ''}} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + # - name: linux-vulkan + # os: ubuntu-latest + # arch: x86_64 + # make: LLAMA="-DGGML_BACKEND_DL=ON -DGGML_NATIVE=OFF -DGGML_CPU_ALL_VARIANTS=ON -DGGML_VULKAN=ON" + # - name: macos + # os: macos-latest + - name: windows + os: windows-latest + arch: x86_64 + - name: windows-cuda + os: windows-latest + arch: x86_64 + make: LLAMA="-DGGML_BACKEND_DL=ON -DGGML_NATIVE=OFF -DGGML_CPU=OFF -DGGML_CUDA=ON" + # - name: android + # os: ubuntu-latest + # arch: arm64-v8a + # make: PLATFORM=android ARCH=arm64-v8a + # - name: android + # os: ubuntu-latest + # arch: x86_64 + # make: PLATFORM=android ARCH=x86_64 + # sqlite-amalgamation-zip: https://sqlite.org/2025/sqlite-amalgamation-3490100.zip + # - name: ios + # os: macos-latest + # make: PLATFORM=ios + # - name: isim + # os: macos-latest + # make: PLATFORM=isim + + defaults: + run: + shell: bash + + steps: + + - uses: actions/checkout@v4.2.2 + with: + submodules: true + + - name: linux install dependencies + if: matrix.name == 'linux-vulkan' + run: | + sudo apt-get update -y + sudo apt-get install -y build-essential wget git cmake sqlite3 libcurl4-openssl-dev + + - name: linux install vulkan + if: matrix.name == 'linux-vulkan' + run: | + wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - + sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list + sudo apt-get update -y + sudo apt-get install -y mesa-vulkan-drivers + # Vulkan is no longer packed for Ubuntu + wget https://sdk.lunarg.com/sdk/download/latest/linux/vulkan-sdk.tar.xz?Human=true -O vulkan-sdk.tar.xz + tar -xf vulkan-sdk.tar.xz + cd $(ls -d 1.* | head -n1) + source setup-env.sh + echo "VULKAN_SDK=$VULKAN_SDK" >> $GITHUB_ENV + echo "PATH=$PATH" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV + echo "VK_ADD_LAYER_PATH=$VK_ADD_LAYER_PATH" >> $GITHUB_ENV + + + - name: Install CUDA Toolkit + if: matrix.name == 'windows-cuda' + run: | + choco install cuda -y --no-progress + shell: powershell + + - name: Setup MSBuild + if: matrix.name == 'windows' || matrix.name == 'windows-cuda' + uses: microsoft/setup-msbuild@v2 + + - name: Windows build sqlite-ai + if: matrix.name == 'windows' || matrix.name == 'windows-cuda' + shell: cmd + run: | + call "%VSINSTALLDIR%\VC\Auxiliary\Build\vcvars64.bat" + nmake /f Makefile PLATFORM=windows ${{ matrix.make && matrix.make || ''}} + + - name: build sqlite-ai + if: matrix.name != 'windows' + run: make extension ${{ matrix.make && matrix.make || ''}} + + - name: macos install sqlite3 without SQLITE_OMIT_LOAD_EXTENSION + if: matrix.name == 'macos' + run: brew link sqlite --force + + - name: android setup test environment + if: matrix.name == 'android' && matrix.arch != 'arm64-v8a' + run: | + + echo "::group::enable kvm group perms" + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + echo "::endgroup::" + + echo "::group::download and build sqlite3 without SQLITE_OMIT_LOAD_EXTENSION" + curl -O ${{ matrix.sqlite-amalgamation-zip }} + unzip sqlite-amalgamation-*.zip + export ${{ matrix.make }} + $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.arch }}-linux-android26-clang sqlite-amalgamation-*/shell.c sqlite-amalgamation-*/sqlite3.c -o sqlite3 -ldl + # remove unused folders to save up space + rm -rf sqlite-amalgamation-*.zip sqlite-amalgamation-* + echo "::endgroup::" + + echo "::group::prepare the test script" + make test PLATFORM=$PLATFORM ARCH=$ARCH || echo "It should fail. Running remaining commands in the emulator" + cat > commands.sh << EOF + mv -f /data/local/tmp/sqlite3 /system/xbin + cd /data/local/tmp + $(make test PLATFORM=$PLATFORM ARCH=$ARCH -n) + EOF + # remove big unused folders to avoid emulator errors + rm -rf build modules + echo "::endgroup::" + + - name: android test sqlite-ai + if: matrix.name == 'android' && matrix.arch != 'arm64-v8a' + uses: reactivecircus/android-emulator-runner@v2.34.0 + with: + api-level: 26 + arch: ${{ matrix.arch }} + script: | + adb root + adb remount + adb push ${{ github.workspace }}/. /data/local/tmp/ + adb shell "sh /data/local/tmp/commands.sh" + + + - name: windows test sqlite-ai + if: matrix.os == 'windows-latest' + run: | + call "%ProgramFiles(x86)%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + nmake /f Makefile test PLATFORM=windows + shell: cmd + + - name: test sqlite-ai + if: matrix.name == 'linux' || matrix.name == 'macos' + run: make test + + - uses: actions/upload-artifact@v4.6.2 + if: always() + with: + name: ai-${{ matrix.name }}${{ matrix.arch && format('-{0}', matrix.arch) || '' }} + path: dist/ai.* + if-no-files-found: error + + release: + runs-on: ubuntu-latest + name: release + needs: build + if: github.ref == 'refs/heads/main' + + steps: + + - uses: actions/checkout@v4.2.2 + + - uses: actions/download-artifact@v4.2.1 + with: + path: artifacts + + - name: release tag version from sqlite-ai.h + id: tag + run: | + VERSION=$(make version) + if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + LATEST=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.name') + if [[ "$VERSION" != "$LATEST" || "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then + echo "version=$VERSION" >> $GITHUB_OUTPUT + else + echo "::warning file=src/sqlite-ai.h::To release a new version, please update the SQLITE_AI_VERSION in src/sqlite-ai.h to be different than the latest $LATEST" + fi + exit 0 + fi + echo "❌ SQLITE_AI_VERSION not found in sqlite-ai.h" + exit 1 + + - name: zip artifacts + run: | + for folder in "artifacts"/*; do + if [ -d "$folder" ]; then + name=$(basename "$folder") + zip -jq "${name}-${{ steps.tag.outputs.version }}.zip" "$folder"/* + tar -cJf "${name}-${{ steps.tag.outputs.version }}.tar.xz" -C "$folder" . + tar -czf "${name}-${{ steps.tag.outputs.version }}.tar.gz" -C "$folder" . + fi + done + + - uses: softprops/action-gh-release@v2.2.1 + if: steps.tag.outputs.version != '' + with: + generate_release_notes: true + tag_name: ${{ steps.tag.outputs.version }} + files: | + ai-*-${{ steps.tag.outputs.version }}.zip + ai-*-${{ steps.tag.outputs.version }}.tar.xz + ai-*-${{ steps.tag.outputs.version }}.tar.gz + make_latest: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4dfeb11..ad851e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,11 @@ .DS_Store /modules/libs +*.xcworkspacedata +*.xcuserstate +*.xcbkptlist +*.plist +/build +/dist +*.sqlite +*.a +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4b2d5d0 --- /dev/null +++ b/Makefile @@ -0,0 +1,232 @@ +# Makefile for SQLite AI Extension +# Supports compilation for Linux, macOS, Windows, Android and iOS + +# customize sqlite3 executable with +# make test SQLITE3=/opt/homebrew/Cellar/sqlite/3.49.1/bin/sqlite3 +SQLITE3 ?= sqlite3 + +# Set default platform if not specified +ifeq ($(OS),Windows_NT) + PLATFORM := windows + HOST := windows + CPUS := $(shell powershell -Command "[Environment]::ProcessorCount") +else + HOST = $(shell uname -s | tr '[:upper:]' '[:lower:]') + ifeq ($(HOST),darwin) + PLATFORM := macos + CPUS := $(shell sysctl -n hw.ncpu) + else + PLATFORM := $(HOST) + CPUS := $(shell nproc) + endif +endif + +# Speed up builds by using all available CPU cores +MAKEFLAGS += -j$(CPUS) + +# Directories +SRC_DIR = src +DIST_DIR = dist +VPATH = $(SRC_DIR) +BUILD_DIR = build +LLAMA_DIR = modules/llama.cpp +WHISPER_DIR = modules/whisper.cpp +MINIAUDIO_DIR = modules/miniaudio +BUILD_LLAMA = $(BUILD_DIR)/llama.cpp +BUILD_WHISPER = $(BUILD_DIR)/whisper.cpp +BUILD_MINIAUDIO = $(BUILD_DIR)/miniaudio + +OBJ_EXT = o +# Compiler and flags +CC = gcc +CXX = g++ +CFLAGS = -Wall -Wextra -Wno-unused-parameter -I$(SRC_DIR) -I$(LLAMA_DIR)/ggml/include -I$(LLAMA_DIR)/include -I$(WHISPER_DIR)/include -I$(MINIAUDIO_DIR) +LLAMA_OPTIONS = -DLLAMA_CURL=OFF -DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_TOOLS=OFF -DLLAMA_BUILD_SERVER=OFF +WHISPER_OPTIONS = -DWHISPER_BUILD_EXAMPLES=OFF -DWHISPER_BUILD_TESTS=OFF -DWHISPER_BUILD_SERVER=OFF +MINIAUDIO_OPTIONS = -DMINIAUDIO_BUILD_EXAMPLES=OFF -DMINIAUDIO_BUILD_TESTS=OFF + +# Module-specific linking flags +LLAMA_LDFLAGS = -L./$(BUILD_LLAMA)/common -L./$(BUILD_LLAMA)/ggml/src -L./$(BUILD_LLAMA)/src -lcommon -lggml -lggml-cpu -lggml-base -lllama +WHISPER_LDFLAGS = -L./$(BUILD_WHISPER)/src -lwhisper +MINIAUDIO_LDFLAGS = -L./$(BUILD_MINIAUDIO) -lminiaudio + +LDFLAGS = $(LLAMA_LDFLAGS) $(WHISPER_LDFLAGS) $(MINIAUDIO_LDFLAGS) + +# Files +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +OBJ_FILES = $(patsubst %.c, $(BUILD_DIR)/%.$(OBJ_EXT), $(notdir $(SRC_FILES))) +LLAMA_LIBS = $(BUILD_LLAMA)/common/libcommon.a $(BUILD_LLAMA)/ggml/src/libggml.a $(BUILD_LLAMA)/ggml/src/libggml-base.a $(BUILD_LLAMA)/ggml/src/libggml-cpu.a $(BUILD_LLAMA)/src/libllama.a +WHISPER_LIBS = $(BUILD_WHISPER)/src/libwhisper.a +MINIAUDIO_LIBS = $(BUILD_MINIAUDIO)/libminiaudio.a + +# Platform-specific settings +ifeq ($(PLATFORM),windows) + OBJ_EXT = obj + CC = cl + CXX = cl + TARGET := $(DIST_DIR)/ai.dll + CFLAGS = /nologo /W3 /EHsc /I$(SRC_DIR) /I$(LLAMA_DIR)/ggml/include /I$(LLAMA_DIR)/include /I$(WHISPER_DIR)/include /I$(MINIAUDIO_DIR) /MD + LDFLAGS = /DLL /OUT:$(TARGET) /LIBPATH:$(BUILD_LLAMA)/common /LIBPATH:$(BUILD_LLAMA)/ggml/src /LIBPATH:$(BUILD_LLAMA)/src /LIBPATH:$(BUILD_WHISPER)/src /LIBPATH:$(BUILD_MINIAUDIO) common.lib ggml.lib ggml-cpu.lib ggml-base.lib llama.lib whisper.lib miniaudio.lib bcrypt.lib + DEF_FILE := $(BUILD_DIR)/ai.def + STRIP = echo "No strip needed for MSVC" +else ifeq ($(PLATFORM),macos) + TARGET := $(DIST_DIR)/ai.dylib + LLAMA_LIBS += $(BUILD_LLAMA)/ggml/src/ggml-metal/libggml-metal.a $(BUILD_LLAMA)/ggml/src/ggml-blas/libggml-blas.a + WHISPER_LIBS += $(BUILD_WHISPER)/src/libwhisper.coreml.a + LDFLAGS += -arch x86_64 -arch arm64 -L./$(BUILD_LLAMA)/ggml/src/ggml-metal -lggml-metal -L./$(BUILD_LLAMA)/ggml/src/ggml-blas -lggml-blas -Wl,-force_load,$(BUILD_WHISPER)/src/libwhisper.coreml.a -framework Metal -framework Foundation -framework CoreFoundation -framework QuartzCore -framework Accelerate -framework CoreML -dynamiclib -undefined dynamic_lookup + CFLAGS += -arch x86_64 -arch arm64 + LLAMA_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" + WHISPER_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DWHISPER_COREML=ON + MINIAUDIO_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" + # Strip removes symbols that are needed for the extension to work properly because loaded via `dlopen()`: + # Error: dlopen(./dist/ai.dylib, 0x000A): symbol not found in flat namespace '_whisper_coreml_encode' + # STRIP = strip -x -S $@ + STRIP = true +else ifeq ($(PLATFORM),android) + # Set ARCH to find Android NDK's Clang compiler, the user should set the ARCH + ifeq ($(filter %,$(ARCH)),) + $(error "Android ARCH must be set to ARCH=x86_64 or ARCH=arm64-v8a") + endif + # Set ANDROID_NDK path to find android build tools + # e.g. on MacOS: export ANDROID_NDK=/Users/username/Library/Android/sdk/ndk/25.2.9519653 + ifeq ($(filter %,$(ANDROID_NDK)),) + $(error "Android NDK must be set") + endif + + BIN = $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(HOST)-x86_64/bin + PATH := $(BIN):$(PATH) + + ifneq (,$(filter $(ARCH),arm64 arm64-v8a)) + override ARCH := aarch64 + endif + + CC = $(BIN)/$(ARCH)-linux-android26-clang + CXX = $(CC)++ + TARGET := $(DIST_DIR)/ai.so + LDFLAGS += -static-libstdc++ -shared + LLAMA_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake -DANDROID_ABI=$(if $(filter aarch64,$(ARCH)),arm64-v8a,$(ARCH)) -DANDROID_PLATFORM=android-26 -DCMAKE_C_FLAGS="-march=$(if $(filter aarch64,$(ARCH)),armv8.7a,x86-64)" -DCMAKE_CXX_FLAGS="-march=$(if $(filter aarch64,$(ARCH)),armv8.7a,x86-64)" -DGGML_OPENMP=OFF -DGGML_LLAMAFILE=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON + WHISPER_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake -DANDROID_ABI=$(if $(filter aarch64,$(ARCH)),arm64-v8a,$(ARCH)) -DANDROID_PLATFORM=android-26 -DCMAKE_C_FLAGS="-march=$(if $(filter aarch64,$(ARCH)),armv8.7a,x86-64)" -DCMAKE_CXX_FLAGS="-march=$(if $(filter aarch64,$(ARCH)),armv8.7a,x86-64)" -DGGML_OPENMP=OFF -DGGML_LLAMAFILE=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON + MINIAUDIO_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake -DANDROID_ABI=$(if $(filter aarch64,$(ARCH)),arm64-v8a,$(ARCH)) -DANDROID_PLATFORM=android-26 -DCMAKE_C_FLAGS="-march=$(if $(filter aarch64,$(ARCH)),armv8.7a,x86-64)" -DCMAKE_CXX_FLAGS="-march=$(if $(filter aarch64,$(ARCH)),armv8.7a,x86-64)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON + STRIP = $(BIN)/llvm-strip --strip-unneeded $@ +else ifeq ($(PLATFORM),ios) + CC = clang + CXX = $(CC)++ + TARGET := $(DIST_DIR)/ai.dylib + SDK := -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=14.0 + LLAMA_LIBS += $(BUILD_LLAMA)/ggml/src/ggml-metal/libggml-metal.a $(BUILD_LLAMA)/ggml/src/ggml-blas/libggml-blas.a + # Need to force-load `libwhisper.coreml.a` for CoreML because dinamically loaded on runtime and the compiler strips the symbols + LDFLAGS += -L./$(BUILD_LLAMA)/ggml/src/ggml-metal -lggml-metal -L./$(BUILD_LLAMA)/ggml/src/ggml-blas -lggml-blas -Wl,-force_load,$(BUILD_WHISPER)/src/libwhisper.coreml.a -framework Accelerate -framework Metal -framework Foundation -framework CoreML -framework AVFoundation -framework AudioToolbox -framework Security -dynamiclib $(SDK) + # Miniaudio requires to compile as Objective-C on iOS + # https://github.com/mackron/miniaudio/blob/master/README.md#building + CFLAGS += -arch arm64 -x objective-c $(SDK) + LLAMA_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 + WHISPER_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DWHISPER_COREML=ON + STRIP = strip -x -S $@ +else ifeq ($(PLATFORM),isim) + TARGET := $(DIST_DIR)/ai.dylib + SDK := -isysroot $(shell xcrun --sdk iphonesimulator --show-sdk-path) -miphonesimulator-version-min=14.0 + LLAMA_LIBS += $(BUILD_LLAMA)/ggml/src/ggml-metal/libggml-metal.a $(BUILD_LLAMA)/ggml/src/ggml-blas/libggml-blas.a + LDFLAGS += -arch x86_64 -arch arm64 -L./$(BUILD_LLAMA)/ggml/src/ggml-metal -lggml-metal -L./$(BUILD_LLAMA)/ggml/src/ggml-blas -lggml-blas -Wl,-force_load,$(BUILD_WHISPER)/src/libwhisper.coreml.a -framework Accelerate -framework Metal -framework Foundation -framework CoreML -framework AVFoundation -framework AudioToolbox -framework Security -dynamiclib $(SDK) + CFLAGS += -arch x86_64 -arch arm64 -x objective-c $(SDK) + LLAMA_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" + WHISPER_OPTIONS += -DBUILD_SHARED_LIBS=OFF -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DWHISPER_COREML=ON +else # linux + TARGET := $(DIST_DIR)/ai.so + # Using -DGGML_CPU_ALL_VARIANTS=ON, `-lggml-cpu` is not needed + LDFLAGS := $(filter-out -lggml-cpu,$(LDFLAGS)) + LDFLAGS += -shared -L./$(BUILD_LLAMA)/bin -Wl,-rpath,./$(BUILD_LLAMA)/bin -Wl,-rpath,./$(BUILD_LLAMA)/common -Wl,-rpath,./$(BUILD_LLAMA)/ggml/src -Wl,-rpath,./$(BUILD_LLAMA)/src -Wl,-rpath,./$(BUILD_WHISPER)/src + # Add miniaudio Linux-specific flags (as per miniaudio docs) + MINIAUDIO_LDFLAGS += -lpthread -lm + LLAMA_OPTIONS += -DBUILD_SHARED_LIBS=ON -DGGML_OPENMP=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON + WHISPER_OPTIONS += -DBUILD_SHARED_LIBS=ON -DGGML_OPENMP=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON + STRIP = strip --strip-unneeded $@ +endif + +# Windows .def file generation +$(DEF_FILE): +ifeq ($(PLATFORM),windows) + @echo "LIBRARY ai.dll" > $@ + @echo "EXPORTS" >> $@ + @echo " sqlite3_ai_init" >> $@ +endif + +# Make sure the build and dist directories exist +$(shell mkdir -p $(BUILD_DIR) $(DIST_DIR)) + +# Default target +extension: $(TARGET) +all: $(TARGET) + +# Ensure object files are built first +compile: $(OBJ_FILES) + +# Loadable library +$(TARGET): $(DEF_FILE) $(LLAMA_LIBS) $(WHISPER_LIBS) $(MINIAUDIO_LIBS) +ifeq ($(PLATFORM),windows) + $(CXX) $(OBJ_FILES) /link $(LDFLAGS) +else + $(CXX) $(OBJ_FILES) $(DEF_FILE) -o $@ $(LDFLAGS) +endif + $(STRIP) + +# Object files +ifeq ($(PLATFORM),windows) +$(BUILD_DIR)/%.obj: %.c + $(CC) $(CFLAGS) /c $< /Fo$@ +else +$(BUILD_DIR)/%.o: %.c + $(CC) $(CFLAGS) -O3 -fPIC -c $< -o $@ +endif + +test: $(TARGET) + $(SQLITE3) ":memory:" -cmd ".bail on" ".load ./dist/ai" "SELECT ai_version();" + +# Build submodules (only after successful compilation of the extension) +build/llama.cpp.stamp: | compile + cmake -B $(BUILD_LLAMA) $(LLAMA_OPTIONS) $(LLAMA_DIR) + cmake --build $(BUILD_LLAMA) --config Release -- -j$(CPUS) + touch $@ + +build/whisper.cpp.stamp: | compile + cmake -B $(BUILD_WHISPER) $(WHISPER_OPTIONS) $(WHISPER_DIR) + cmake --build $(BUILD_WHISPER) --config Release -- -j$(CPUS) + touch $@ + +build/miniaudio.stamp: | compile + cmake -B $(BUILD_MINIAUDIO) $(MINIAUDIO_OPTIONS) $(MINIAUDIO_DIR) + cmake --build $(BUILD_MINIAUDIO) --config Release -- -j$(CPUS) + touch $@ + +$(LLAMA_LIBS): build/llama.cpp.stamp +$(WHISPER_LIBS): build/whisper.cpp.stamp +$(MINIAUDIO_LIBS): build/miniaudio.stamp + +# Tools +version: + @echo $(shell sed -n 's/^#define SQLITE_AI_VERSION[[:space:]]*"\([^"]*\)".*/\1/p' src/sqlite-ai.h) + +# Clean up generated files +clean: + rm -rf $(BUILD_DIR)/* $(DIST_DIR)/* *.gcda *.gcno *.gcov *.sqlite + +# Help message +help: + @echo "SQLite AI Extension Makefile" + @echo "Usage:" + @echo " make [PLATFORM=platform] [ARCH=arch] [ANDROID_NDK=\$$ANDROID_HOME/ndk/26.1.10909125] [target]" + @echo "" + @echo "Platforms:" + @echo " linux (default on Linux)" + @echo " macos (default on macOS)" + @echo " windows (default on Windows)" + @echo " android (needs ARCH to be set to x86_64 or arm64-v8a and ANDROID_NDK to be set)" + @echo " ios (only on macOS)" + @echo " isim (only on macOS)" + @echo "" + @echo "Targets:" + @echo " all - Build the extension (default)" + @echo " clean - Remove built files" + @echo " test - Test the extension" + @echo " help - Display this help message" + +.PHONY: all clean test extension help compile diff --git a/src/utils.c b/src/utils.c index cca1f21..b2c3627 100644 --- a/src/utils.c +++ b/src/utils.c @@ -369,15 +369,25 @@ int ai_uuid_v7_generate (uint8_t value[UUID_LEN]) { #endif // get current timestamp in ms + uint64_t timestamp; + #if defined(_WIN32) + // Use GetSystemTimeAsFileTime for Windows/MinGW + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t t = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + // Convert FILETIME (100-ns intervals since Jan 1, 1601) to ms since Unix epoch + timestamp = (t / 10000ULL) - 11644473600000ULL; + #else struct timespec ts; - #ifdef __ANDROID__ + #if defined(__ANDROID__) if (clock_gettime(CLOCK_REALTIME, &ts) != 0) return -1; #else if (timespec_get(&ts, TIME_UTC) == 0) return -1; #endif + timestamp = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + #endif // add timestamp part to UUID - uint64_t timestamp = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; value[0] = (timestamp >> 40) & 0xFF; value[1] = (timestamp >> 32) & 0xFF; value[2] = (timestamp >> 24) & 0xFF;