diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..a222edfb --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.lib filter=lfs diff=lfs merge=lfs -text +*.dll filter=lfs diff=lfs merge=lfs -text +*.a filter=lfs diff=lfs merge=lfs -text +*.dylib filter=lfs diff=lfs merge=lfs -text +*.so filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/cmake-macos.yml b/.github/workflows/cmake-macos.yml deleted file mode 100644 index d3ef0a29..00000000 --- a/.github/workflows/cmake-macos.yml +++ /dev/null @@ -1,159 +0,0 @@ -name: Builds + Unit Tests - -on: - pull_request: - branches: [ "main" ] - push: - branches: [ "main" ] - workflow_dispatch: - -env: - BUILD_TYPE: Release - TEST_TYPE: CI_TEST - GENERATOR: "Unix Makefiles" - CXX_COMPILER: clang++ - C_COMPILER: clang - CLEAN_BUILD: false - -jobs: - build: - runs-on: macos-14 - timeout-minutes: 120 - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Cache Protobuf Build - if: env.CLEAN_BUILD != 'true' - id: protobuf-cache - uses: actions/cache@v3 - with: - path: build/third_party/protobuf - key: ${{ runner.os }}-protobuf-build-${{ hashFiles('third_party/protobuf/version.json') }} - - - name: Check Protobuf Cache - if: env.CLEAN_BUILD != 'true' - run: | - echo "Protobuf cache hit: ${{ steps.protobuf-cache.outputs.cache-hit }}" - if [ "${{ steps.protobuf-cache.outputs.cache-hit }}" != 'true' ]; then - echo "Protobuf cache missed or expired, setting CLEAN_BUILD to true." - echo "CLEAN_BUILD=true" >> $GITHUB_ENV - fi - - - name: Cache JUCE Build - if: env.CLEAN_BUILD != 'true' - id: juce-cache - uses: actions/cache@v3 - with: - path: build/third_party/JUCE - key: ${{ runner.os }}-juce-build-${{ hashFiles('third_party/JUCE/CMakeLists.txt') }} - - - name: Check JUCE Cache - if: env.CLEAN_BUILD != 'true' - run: | - echo "JUCE cache hit: ${{ steps.juce-cache.outputs.cache-hit }}" - if [ "${{ steps.juce-cache.outputs.cache-hit }}" != 'true' ]; then - echo "JUCE cache missed or expired, setting CLEAN_BUILD to true." - echo "CLEAN_BUILD=true" >> $GITHUB_ENV - fi - - - name: Cache IAMF Build - if: env.CLEAN_BUILD != 'true' - id: iamf-cache - uses: actions/cache@v3 - with: - path: build/third_party/iamf - key: ${{ runner.os }}-iamf-build-${{ hashFiles('third_party/iamf/CMakeLists.txt') }} - - - name: Check IAMF Cache - if: env.CLEAN_BUILD != 'true' - run: | - echo "IAMF cache hit: ${{ steps.iamf-cache.outputs.cache-hit }}" - if [ "${{ steps.iamf-cache.outputs.cache-hit }}" != 'true' ]; then - echo "IAMF cache missed or expired, setting CLEAN_BUILD to true." - echo "CLEAN_BUILD=true" >> $GITHUB_ENV - fi - - - name: Cache Libear Build - if: env.CLEAN_BUILD != 'true' - id: libear-cache - uses: actions/cache@v3 - with: - path: build/third_party/libear - key: ${{ runner.os }}-libear-build-${{ hashFiles('third_party/libear/CMakeLists.txt') }} - - - name: Check Libear Cache - if: env.CLEAN_BUILD != 'true' - run: | - echo "Libear cache hit: ${{ steps.libear-cache.outputs.cache-hit }}" - if [ "${{ steps.libear-cache.outputs.cache-hit }}" != 'true' ]; then - echo "Libear cache missed or expired, setting CLEAN_BUILD to true." - echo "CLEAN_BUILD=true" >> $GITHUB_ENV - fi - - - name: Cache Main Build - if: env.CLEAN_BUILD != 'true' - id: main-build-cache - uses: actions/cache@v3 - with: - path: build - key: ${{ runner.os }}-main-build-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'components/**/*.cpp', 'components/**/*.h') }} - - - name: Check Main Build Cache - if: env.CLEAN_BUILD != 'true' - run: | - echo "Main build cache hit: ${{ steps.main-build-cache.outputs.cache-hit }}" - if [ "${{ steps.main-build-cache.outputs.cache-hit }}" != 'true' ]; then - echo "Main build cache missed or expired, setting CLEAN_BUILD to true." - echo "CLEAN_BUILD=true" >> $GITHUB_ENV - fi - - - name: Check Clean Build - if: env.CLEAN_BUILD == 'true' - run: | - echo "CLEAN_BUILD is true, removing entire build directory." - if [ -d "build" ]; then rm -rf build; fi - - - name: Check Formatting - run: | - brew install clang-format - find common -iname '*.h' -o -iname '*.cpp' | xargs clang-format --style=file:.clang-format --dry-run -Werror -i - find rendererplugin -iname '*.h' -o -iname '*.cpp' | xargs clang-format --style=file:.clang-format --dry-run -Werror -i - find audioelementplugin -iname '*.h' -o -iname '*.cpp' | xargs clang-format --style=file:.clang-format --dry-run -Werror -i - - - name: Setup CMake 3.30.x - uses: jwlawson/actions-setup-cmake@v1 - with: - cmake-version: "3.30.x" - - name: Verify CMake - run: | - cmake --version - which cmake - - - name: Configure CMake - run: | - cmake -S . -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -G "${{ env.GENERATOR }}" \ - -D${{ env.TEST_TYPE }}=ON \ - -DCMAKE_CXX_COMPILER=${{ env.CXX_COMPILER }} \ - -DCMAKE_C_COMPILER=${{ env.C_COMPILER }} \ - -DBUILD_TESTING=ON \ - -DZMQ_BUILD_TESTS=OFF - - - name: Build - run: cmake --build build --config ${{ env.BUILD_TYPE }} -- -j $(sysctl -n hw.logicalcpu) - - - name: Test - working-directory: ${{ github.workspace }}/build - run: ctest -C ${{ env.BUILD_TYPE }} - - # Dump CTest and Ecplisa logs if the previous step failed. - - name: Dump Test Logs - if: ${{ failure() }} - run: | - echo "//// CTest Logs ////" && - cat /Users/runner/work/eclipsa/eclipsa/build/Testing/Temporary/LastTest.log && - echo "//// Eclipsa Logs ////" && - cat /tmp/Eclipsa_Audio_Plugin/log.txt \ No newline at end of file diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 00000000..dbb8fe2d --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,300 @@ +name: Cross-Platform Build + +on: + pull_request: + branches: [ "main" ] + push: + branches: [ "main" ] + workflow_dispatch: + +env: + BUILD_TYPE: Release + TEST_TYPE: CI_TEST + CLEAN_BUILD: false + # Enable vcpkg binary caching for GitHub Actions + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + +jobs: + build: + name: ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + os: [macos-14, windows-latest] + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + lfs: true + + - name: Fetch LFS objects + run: git lfs pull + + - name: Debug LFS (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + ls third_party/obr/lib/Windows/Release/obr.lib + Get-Item third_party/obr/lib/Windows/Release/obr.lib | Select-Object Length + + + # --- Caching Steps (Common) --- + - name: Cache Protobuf Build + if: env.CLEAN_BUILD != 'true' + id: protobuf-cache + uses: actions/cache@v3 + with: + path: build/_deps/protobuf-build + key: ${{ runner.os }}-protobuf-build-${{ hashFiles('third_party/protobuf/version.json') }} + + - name: Check Protobuf Cache + if: env.CLEAN_BUILD != 'true' + shell: bash + run: | + echo "Protobuf cache hit: ${{ steps.protobuf-cache.outputs.cache-hit }}" + if [ "${{ steps.protobuf-cache.outputs.cache-hit }}" != 'true' ]; then + echo "Protobuf cache missed or expired, setting CLEAN_BUILD to true." + echo "CLEAN_BUILD=true" >> $GITHUB_ENV + fi + + - name: Cache JUCE Build + if: env.CLEAN_BUILD != 'true' + id: juce-cache + uses: actions/cache@v3 + with: + path: build/third_party/JUCE + key: ${{ runner.os }}-juce-build-${{ hashFiles('third_party/JUCE/CMakeLists.txt') }} + + - name: Check JUCE Cache + if: env.CLEAN_BUILD != 'true' + shell: bash + run: | + echo "JUCE cache hit: ${{ steps.juce-cache.outputs.cache-hit }}" + if [ "${{ steps.juce-cache.outputs.cache-hit }}" != 'true' ]; then + echo "JUCE cache missed or expired, setting CLEAN_BUILD to true." + echo "CLEAN_BUILD=true" >> $GITHUB_ENV + fi + + - name: Cache IAMF Build + if: env.CLEAN_BUILD != 'true' + id: iamf-cache + uses: actions/cache@v3 + with: + path: build/_deps/libiamf-build + key: ${{ runner.os }}-iamf-build-${{ hashFiles('third_party/libiamf/CMakeLists.txt') }} + + - name: Check IAMF Cache + if: env.CLEAN_BUILD != 'true' + shell: bash + run: | + echo "IAMF cache hit: ${{ steps.iamf-cache.outputs.cache-hit }}" + if [ "${{ steps.iamf-cache.outputs.cache-hit }}" != 'true' ]; then + echo "IAMF cache missed or expired, setting CLEAN_BUILD to true." + echo "CLEAN_BUILD=true" >> $GITHUB_ENV + fi + + - name: Cache Libear Build + if: env.CLEAN_BUILD != 'true' + id: libear-cache + uses: actions/cache@v3 + with: + path: build/third_party/libear + key: ${{ runner.os }}-libear-build-${{ hashFiles('third_party/libear/CMakeLists.txt') }} + + - name: Check Libear Cache + if: env.CLEAN_BUILD != 'true' + shell: bash + run: | + echo "Libear cache hit: ${{ steps.libear-cache.outputs.cache-hit }}" + if [ "${{ steps.libear-cache.outputs.cache-hit }}" != 'true' ]; then + echo "Libear cache missed or expired, setting CLEAN_BUILD to true." + echo "CLEAN_BUILD=true" >> $GITHUB_ENV + fi + + - name: Cache Main Build + if: env.CLEAN_BUILD != 'true' + id: main-build-cache + uses: actions/cache@v3 + with: + path: build + key: ${{ runner.os }}-main-build-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'components/**/*.cpp', 'components/**/*.h') }} + + - name: Check Main Build Cache + if: env.CLEAN_BUILD != 'true' + shell: bash + run: | + echo "Main build cache hit: ${{ steps.main-build-cache.outputs.cache-hit }}" + if [ "${{ steps.main-build-cache.outputs.cache-hit }}" != 'true' ]; then + echo "Main build cache missed or expired, setting CLEAN_BUILD to true." + echo "CLEAN_BUILD=true" >> $GITHUB_ENV + fi + + - name: Check Clean Build + if: env.CLEAN_BUILD == 'true' + shell: bash + run: | + echo "CLEAN_BUILD is true, removing entire build directory." + if [ -d "build" ]; then rm -rf build; fi + + # --- Formatting (macOS only for now) --- + - name: Check Formatting + if: runner.os == 'macOS' + run: | + brew install clang-format + find common -iname '*.h' -o -iname '*.cpp' | xargs clang-format --style=file:.clang-format --dry-run -Werror -i + find rendererplugin -iname '*.h' -o -iname '*.cpp' | xargs clang-format --style=file:.clang-format --dry-run -Werror -i + find audioelementplugin -iname '*.h' -o -iname '*.cpp' | xargs clang-format --style=file:.clang-format --dry-run -Werror -i + + # --- CMake Setup --- + - name: Setup CMake + uses: jwlawson/actions-setup-cmake@v1 + with: + cmake-version: "3.30.x" + + - name: Verify CMake + shell: bash + run: | + cmake --version + which cmake + + # --- Install Dependencies (Windows) --- + - name: Cache Intel OneAPI + id: cache-oneapi + if: runner.os == 'Windows' + uses: actions/cache@v3 + with: + path: oneapi + key: ${{ runner.os }}-oneapi-mkl-2025.3.0 + + - name: Install Intel OneAPI MKL (Windows) + if: runner.os == 'Windows' && steps.cache-oneapi.outputs.cache-hit != 'true' + shell: powershell + run: | + # Download Intel OneAPI Base Toolkit (Offline Installer) + # We use the offline installer because the online installer URL is not stable/predictable. + # This is a large download (~2.5GB), but we cache the result so this only happens once. + $installerUrl = "https://registrationcenter-download.intel.com/akdlm/IRC_NAS/1f18901e-877d-469d-a41a-a10f11b39336/intel-oneapi-base-toolkit-2025.3.0.372_offline.exe" + $installerPath = "$env:TEMP\intel-oneapi-base-toolkit-offline.exe" + + Write-Host "Downloading Intel OneAPI Base Toolkit from $installerUrl..." + + # Use curl.exe for faster download (Invoke-WebRequest can be slow due to progress bar) + # -L: Follow redirects + # -o: Output file + # --retry: Retry on transient errors + & curl.exe -L -o "$installerPath" "$installerUrl" --retry 5 --retry-delay 5 + + if (-not (Test-Path $installerPath)) { + Write-Error "Download failed: File not found at $installerPath" + exit 1 + } + + Write-Host "Installing Intel OneAPI Base Toolkit (MKL only)..." + + # Install to a local directory within the workspace so it can be cached + $installDir = "$env:GITHUB_WORKSPACE\oneapi" + + # Run installer silently with custom install directory + $proc = Start-Process -FilePath $installerPath -ArgumentList "-s -a --silent --eula accept --components intel.oneapi.win.mkl.devel -p=NEED_VS2022_INTEGRATION=0 --install-dir `"$installDir`"" -Wait -NoNewWindow -PassThru + + if ($proc.ExitCode -ne 0) { + Write-Error "Installation failed with exit code $($proc.ExitCode)" + exit 1 + } + + Write-Host "Installation complete." + + - name: Set MKLROOT Environment Variable + if: runner.os == 'Windows' + shell: powershell + run: | + $mklRoot = "$env:GITHUB_WORKSPACE\oneapi\mkl\latest" + if (Test-Path $mklRoot) { + Write-Host "MKL found at: $mklRoot" + echo "MKLROOT=$mklRoot" | Out-File -FilePath $env:GITHUB_ENV -Append + } else { + Write-Error "MKL installation failed or not found at expected path: $mklRoot" + exit 1 + } + + - name: Verify MKL install (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + Write-Host "MKLROOT is: $env:MKLROOT" + + $libPath = "$env:MKLROOT\lib" + $lib64Path = "$env:MKLROOT\lib\intel64" + + if (Test-Path $lib64Path) { + Write-Host "Found lib/intel64" + Get-ChildItem $lib64Path | Select-Object Name | Select-Object -First 5 + } elseif (Test-Path $libPath) { + Write-Host "Found lib (root)" + Get-ChildItem $libPath | Select-Object Name | Select-Object -First 5 + } else { + Write-Warning "Could not find lib or lib/intel64 under MKLROOT" + Get-ChildItem "$env:MKLROOT" -Recurse -Depth 2 + } + + # --- Configure (Windows) --- + - name: Configure CMake (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} ` + -D${{ env.TEST_TYPE }}=ON ` + -DBUILD_TESTING=ON ` + -DZMQ_BUILD_TESTS=OFF ` + -DMKL_ROOT="$env:MKLROOT" ` + -DVCPKG_TARGET_TRIPLET=x64-windows ` + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" + + # --- Configure (macOS) --- + - name: Configure CMake (macOS) + if: runner.os == 'macOS' + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -G "Unix Makefiles" \ + -D${{ env.TEST_TYPE }}=ON \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER=clang \ + -DBUILD_TESTING=ON \ + -DZMQ_BUILD_TESTS=OFF + + # --- Build --- + - name: Build + shell: bash + run: | + if [ "${{ runner.os }}" == "macOS" ]; then + # Limit parallelism on macOS to avoid "Resource temporarily unavailable" errors + cmake --build build --config ${{ env.BUILD_TYPE }} -j 4 + else + cmake --build build --config ${{ env.BUILD_TYPE }} --parallel + fi + + # --- Test --- + - name: Test + working-directory: ${{ github.workspace }}/build + shell: bash + run: ctest -C ${{ env.BUILD_TYPE }} --output-on-failure + + # --- Logs --- + - name: Dump Test Logs + if: ${{ failure() }} + shell: bash + run: | + echo "//// CTest Logs ////" + if [ -f "Testing/Temporary/LastTest.log" ]; then cat Testing/Temporary/LastTest.log; fi + echo "//// Eclipsa Logs ////" + # Try to find logs in common locations + if [ -f "/tmp/Eclipsa_Audio_Plugin/log.txt" ]; then + cat /tmp/Eclipsa_Audio_Plugin/log.txt + elif [ -f "$TEMP/Eclipsa_Audio_Plugin/log.txt" ]; then + cat "$TEMP/Eclipsa_Audio_Plugin/log.txt" + else + echo "Log file not found." + fi diff --git a/cmake/eclipsa_build_tests.cmake b/cmake/eclipsa_build_tests.cmake index 72cfca81..42095fb0 100644 --- a/cmake/eclipsa_build_tests.cmake +++ b/cmake/eclipsa_build_tests.cmake @@ -38,16 +38,12 @@ function(eclipsa_build_tests) # Move the dlls required for the unit tests as well # This can be done in the post build, since we'll run the tests after the build is complete set(ZMQ_DLL "${CMAKE_BINARY_DIR}/_deps/zeromq-build/bin/${BUILD_LIB_DIR}/libzmq-v143-mt$<$:-gd>-4_3_6.dll") - set(GTEST_DLL "${CMAKE_SOURCE_DIR}/build/bin/${BUILD_LIB_DIR}/gtest.dll") - set(GTEST_MAIN_DLL "${CMAKE_SOURCE_DIR}/build/bin/${BUILD_LIB_DIR}/gtest_main.dll") set(CRYPTOMD_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/${CMAKE_BUILD_TYPE}/libcryptoMD.dll") set(LIBSSLMD_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/${CMAKE_BUILD_TYPE}/libsslMD.dll") - set(TEST_DIR "${CMAKE_SOURCE_DIR}/build/${BUILD_LIB_DIR}") + set(TEST_DIR "${CMAKE_BINARY_DIR}/${BUILD_LIB_DIR}") add_custom_command(TARGET eclipsa_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${TEST_DIR}" - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GTEST_DLL} "${TEST_DIR}/" - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GTEST_MAIN_DLL} "${TEST_DIR}/" COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ZMQ_DLL} "${TEST_DIR}/" COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRYPTOMD_DLL} "${TEST_DIR}/" COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LIBSSLMD_DLL} "${TEST_DIR}/" diff --git a/third_party/libiamf/CMakeLists.txt b/third_party/libiamf/CMakeLists.txt index e2399641..ae2df931 100644 --- a/third_party/libiamf/CMakeLists.txt +++ b/third_party/libiamf/CMakeLists.txt @@ -18,8 +18,7 @@ include(FetchContent) FetchContent_Declare( libiamf - GIT_REPOSITORY https://github.com/AOMediaCodec/libiamf.git - GIT_TAG 48b8b5dc06971e14826d114fd032dab285f4c944 + URL https://github.com/AOMediaCodec/libiamf/archive/48b8b5dc06971e14826d114fd032dab285f4c944.zip ) set(CODEC_CAP ON CACHE BOOL "Codec capability check" FORCE) @@ -39,6 +38,7 @@ if(NOT libiamf_POPULATED) string(REGEX REPLACE "target_link_libraries\\([^)]*[ \t\n]+m[ \t\n)]+[^)]*\\)" "" LIBIAMF_CMAKE_CONTENT "${LIBIAMF_CMAKE_CONTENT}") string(REPLACE " m)" ")" LIBIAMF_CMAKE_CONTENT "${LIBIAMF_CMAKE_CONTENT}") string(REPLACE " m " " " LIBIAMF_CMAKE_CONTENT "${LIBIAMF_CMAKE_CONTENT}") + string(REPLACE "option(BUILD_SHARED_LIBS \"Build shared library\" ON)" "option(BUILD_SHARED_LIBS \"Build shared library\" OFF)" LIBIAMF_CMAKE_CONTENT "${LIBIAMF_CMAKE_CONTENT}") file(WRITE "${libiamf_SOURCE_DIR}/code/CMakeLists.txt" "${LIBIAMF_CMAKE_CONTENT}") message(STATUS "Patched libiamf CMakeLists.txt to remove GCC-only flags and m library") endif()