diff --git a/.github/workflows/macos-appleclang-native.yml b/.github/workflows/macos-appleclang-native.yml new file mode 100644 index 0000000000..03a6b1b1c9 --- /dev/null +++ b/.github/workflows/macos-appleclang-native.yml @@ -0,0 +1,65 @@ +name: Native build with AppleClang on MacOS + +on: + push: + branches: [master] + pull_request: + branches: [master] + merge_group: + +concurrency: + group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +jobs: + build: + # The CMake configure and build commands are platform-agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + strategy: + fail-fast: false + matrix: + build-type: [Release] + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies via Homebrew + run: | + brew update + brew install boost icu4c openssl@3 zstd jemalloc pkg-config + echo PATH="$(brew --prefix icu4c)/bin:$(brew --prefix icu4c)/sbin:$PATH" >> $GITHUB_ENV + echo PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$(brew --prefix icu4c)/lib/pkgconfig" >> $GITHUB_ENV + + - name: Install python dependencies for E2E tests + run: | + pip3 install --break-system-packages pyaml --no-binary=:pyicu: pyicu + + - name: Print clang version + run: clang++ --version + + - name: Create build directory + run: mkdir ${{github.workspace}}/build + + - name: Configure CMake + working-directory: ${{ github.workspace }}/build + run: cmake -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DLOGLEVEL=INFO -DUSE_PARALLEL=false -D_NO_TIMING_TESTS=ON -DCOMPILER_SUPPORTS_MARCH_NATIVE=FALSE -DICU_ROOT=$(brew --prefix icu4c) -GNinja .. + + - name: Build + # Build your program with the given configuration + run: | + cmake --build ${{github.workspace}}/build --config ${{matrix.build-type}} -- -j $(sysctl -n hw.ncpu) + id: build + + - name: Test + id: complete_tests + working-directory: ${{github.workspace}}/build/test + run: env CTEST_OUTPUT_ON_FAILURE=1 ctest -C ${{matrix.build-type}} . + + - name: Running and printing the benchmark examples. + working-directory: ${{github.workspace}}/build + run: benchmark/BenchmarkExamples -p + + - name: E2E + run: ${{github.workspace}}/e2e/e2e.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index a374df4141..3193e23769 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,25 @@ if (POLICY CMP0167) endif () project(QLever C CXX) +if (APPLE) + # Dynamically detect Homebrew prefix + execute_process( + COMMAND brew --prefix + OUTPUT_VARIABLE HOMEBREW_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE BREW_RESULT + ERROR_QUIET + ) + if (BREW_RESULT EQUAL 0 AND HOMEBREW_PREFIX) + message(STATUS "Found Homebrew at: ${HOMEBREW_PREFIX}") + list(APPEND CMAKE_PREFIX_PATH "${HOMEBREW_PREFIX}") + link_directories(${HOMEBREW_PREFIX}/lib) + include_directories(${HOMEBREW_PREFIX}/include) + else() + message(WARNING "Homebrew not found. You may need to manually set CMAKE_PREFIX_PATH to locate dependencies (Boost, ICU, OpenSSL, etc.)") + endif() +endif () + # C/C++ Versions set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) @@ -51,7 +70,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-fcoroutines) endif () -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" ) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0.0" AND NOT COMPILER_VERSION_CHECK_DEACTIVATED) MESSAGE(FATAL_ERROR "Clang++ versions older than 16.0 are not supported by QLever") endif () @@ -62,7 +81,7 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(RANGE_V3_REQUIRED_BY_COMPILER ON) endif () else () - MESSAGE(FATAL_ERROR "QLever currently only supports the G++ or LLVM-Clang++ compilers. Found ${CMAKE_CXX_COMPILER_ID}") + MESSAGE(FATAL_ERROR "QLever currently only supports the G++, LLVM-Clang++ or AppleClang compilers. Found ${CMAKE_CXX_COMPILER_ID}") endif () ## Build targets for address sanitizer @@ -80,7 +99,7 @@ set(CMAKE_CXX_FLAGS_ASAN if ("${CMAKE_GENERATOR}" STREQUAL "Ninja") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") add_compile_options(-fdiagnostics-color=always) - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") add_compile_options(-fcolor-diagnostics) endif () endif () @@ -271,10 +290,8 @@ if (${ALLOW_SHUTDOWN}) message(STATUS "Adding -DALLOW_SHUTDOWN") endif () - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") @@ -434,7 +451,6 @@ set(LOG_LEVEL_DEBUG DEBUG) set(LOG_LEVEL_TIMING TIMING) set(LOG_LEVEL_TRACE TRACE) - if (CMAKE_BUILD_TYPE MATCHES DEBUG) set(LOGLEVEL DEBUG CACHE STRING "The loglevel") else () diff --git a/src/ServerMain.cpp b/src/ServerMain.cpp index bbc7ae48d3..f143abcae8 100644 --- a/src/ServerMain.cpp +++ b/src/ServerMain.cpp @@ -201,9 +201,10 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - AD_LOG_INFO << EMPH_ON << "QLever server, compiled on " - << qlever::version::DatetimeOfCompilation << " using git hash " - << qlever::version::GitShortHash << EMPH_OFF << std::endl; + AD_LOG_INFO << EMPH_ON << "QLever server " << qlever::version::ProjectVersion + << ", compiled on " << qlever::version::DatetimeOfCompilation + << " using git hash " << qlever::version::GitShortHash << EMPH_OFF + << std::endl; try { Server server(port, numSimultaneousQueries, memoryMaxSize, diff --git a/src/engine/GroupByImpl.cpp b/src/engine/GroupByImpl.cpp index 0adea5da6b..5c626c3579 100644 --- a/src/engine/GroupByImpl.cpp +++ b/src/engine/GroupByImpl.cpp @@ -123,12 +123,12 @@ class LazyGroupByRange currentGroupBlock_); groupSplitAcrossTables_ = false; } else { - // This processes the whole block in batches if possible - IdTableStatic table = - std::move(resultTable_).toStatic(); - parent_->processBlock(table, aggregates_, evaluationContext, - blockStart, blockEnd, - ¤tLocalVocab_, groupByCols_); + // This processes the whole block in batches if possible. + IdTableStatic table{ + std::move(resultTable_).template toStatic()}; + parent_->template processBlock( + table, aggregates_, evaluationContext, blockStart, blockEnd, + ¤tLocalVocab_, groupByCols_); resultTable_ = std::move(table).toDynamic(); } } diff --git a/src/index/IndexBuilderMain.cpp b/src/index/IndexBuilderMain.cpp index 5b27bf3d72..533620ddc6 100644 --- a/src/index/IndexBuilderMain.cpp +++ b/src/index/IndexBuilderMain.cpp @@ -273,7 +273,8 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - AD_LOG_INFO << EMPH_ON << "QLever index builder, compiled on " + AD_LOG_INFO << EMPH_ON << "QLever index builder " + << qlever::version::ProjectVersion << ", compiled on " << qlever::version::DatetimeOfCompilation << " using git hash " << qlever::version::GitShortHash << EMPH_OFF << std::endl; diff --git a/src/rdfTypes/GeometryInfoHelpersImpl.h b/src/rdfTypes/GeometryInfoHelpersImpl.h index a522df27f9..236813979e 100644 --- a/src/rdfTypes/GeometryInfoHelpersImpl.h +++ b/src/rdfTypes/GeometryInfoHelpersImpl.h @@ -32,6 +32,7 @@ #include "util/GeoConverters.h" #include "util/Log.h" #include "util/TypeTraits.h" +#include "util/Views.h" // This file contains functions used for parsing and processing WKT geometries // using `pb_util`. To avoid unnecessarily compiling expensive modules, this @@ -341,7 +342,9 @@ struct MetricLengthVisitor { static_assert(ad_utility::similarToInstantiation); return ::ranges::accumulate( - ::ranges::transform_view(multiGeom, MetricLengthVisitor{}), 0); + ::ranges::transform_view(ad_utility::allView(multiGeom), + MetricLengthVisitor{}), + 0); } // Compute the length for the custom container type `AnyGeometry` from diff --git a/src/util/JoinAlgorithms/IndexNestedLoopJoin.h b/src/util/JoinAlgorithms/IndexNestedLoopJoin.h index 3e5b68155d..f8b2a55617 100644 --- a/src/util/JoinAlgorithms/IndexNestedLoopJoin.h +++ b/src/util/JoinAlgorithms/IndexNestedLoopJoin.h @@ -238,8 +238,10 @@ class IndexNestedLoopJoin { for (const auto& rightRow : rightTable) { size_t leftOffset = 0; size_t leftSize = leftTable.size(); + // Use `ql::ranges::ref_view` to avoid copying `RowReference` (which has a + // deleted copy constructor) on AppleClang. for (const auto& [rightId, leftCol] : - ::ranges::zip_view(rightRow, leftColumns)) { + ::ranges::views::zip(ql::ranges::ref_view{rightRow}, leftColumns)) { AD_EXPENSIVE_CHECK(!rightId.isUndefined()); auto currentStart = leftCol.begin() + leftOffset; auto subrange = ql::ranges::equal_range( diff --git a/test/CompactStringVectorTest.cpp b/test/CompactStringVectorTest.cpp index fd635ab3ea..dd6afae5e9 100644 --- a/test/CompactStringVectorTest.cpp +++ b/test/CompactStringVectorTest.cpp @@ -310,7 +310,10 @@ TYPED_TEST(CompactVectorOfStringsFixture, cloneAndRemap) { auto copy2 = original.cloneAndRemap(mappingFunction); ASSERT_EQ(original.size(), copy2.size()); - for (auto [reference, element] : ::ranges::zip_view(original, copy2)) { + // Use `ql::ranges::ref_view` to avoid copying `CompactVectorOfStrings` (which + // has a deleted copy constructor) on AppleClang. + for (const auto& [reference, element] : ::ranges::views::zip( + ql::ranges::ref_view{original}, ql::ranges::ref_view{copy2})) { ASSERT_EQ(reference.size(), element.size()); auto modifiedReference = ::ranges::to( reference | ql::views::transform(mappingFunction)); diff --git a/test/util/AsyncTestHelpers.h b/test/util/AsyncTestHelpers.h index b94daef0e9..dea9eaa4a8 100644 --- a/test/util/AsyncTestHelpers.h +++ b/test/util/AsyncTestHelpers.h @@ -29,12 +29,15 @@ template requires(TestableCoroutine || TestableFunction) void runAsyncTest(Func innerRun, size_t numThreads) { auto ioContext = std::make_shared(); + auto future = [&]() { if constexpr (TestableCoroutine) { return net::co_spawn(*ioContext, innerRun(*ioContext), net::use_future); } else { - return net::post(*ioContext, std::packaged_task{ - [&] { innerRun(*ioContext); }}); + // Use a named variable to work around AppleClang compiler crash when + // passing a temporary `packaged_task` directly to `net::post`. + std::packaged_task task{[&] { innerRun(*ioContext); }}; + return net::post(*ioContext, std::move(task)); } }();