diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml new file mode 100644 index 000000000000..921ff5a43b61 --- /dev/null +++ b/.github/workflows/packaging.yml @@ -0,0 +1,51 @@ +name: Packaging +on: [ 'pull_request' ] +jobs: + package-ubuntu: + name: Package for Ubuntu + runs-on: ubuntu-20.04 + env: + CMAKE_CXX_COMPILER_LAUNCHER: ccache + CMAKE_C_COMPILER_LAUNCHER: ccache + LLVM_ROOT: /usr/lib/llvm-11 + steps: + - name: Install dependencies + run: | + wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null \ + | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null + sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' + sudo apt update + sudo apt install cmake ninja-build doxygen ccache + sudo apt install llvm-11-dev liblld-11-dev clang-11 libclang-11-dev libjpeg-dev libpng-dev + sudo apt install lintian dpkg-dev + - name: Check out sources + uses: actions/checkout@v2 + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1 + - name: Run Ubuntu packaging script + run: ./packaging/ubuntu/package.sh . ubuntu + - name: Upload packages + uses: actions/upload-artifact@v2 + with: + name: packages + path: ubuntu/*.deb + test-ubuntu: + name: Test Ubuntu package + needs: package-ubuntu + runs-on: ubuntu-20.04 + steps: + # Specifically use the CMake version that comes with Ubuntu. + - name: Install dependencies + run: sudo apt install cmake ninja-build libc6-dev-arm64-cross gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user + - name: Check out sources + uses: actions/checkout@v2 + - name: Download Halide Ubuntu packages + uses: actions/download-artifact@v2 + with: + name: packages + - name: Install Halide Ubuntu packages + run: sudo apt install ./*.deb + - name: Test integration + run: | + cmake -S test/integration -B build + cd build && ctest -j$(nproc) --output-on-failure diff --git a/CMakePresets.json b/CMakePresets.json index 2028978dfda9..44d8733311c9 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -98,7 +98,7 @@ "BUILD_SHARED_LIBS": "YES", "CMAKE_INSTALL_BINDIR": "bin/$", "CMAKE_INSTALL_LIBDIR": "lib/$", - "HALIDE_INSTALL_CMAKEDIR": "lib/cmake/Halide" + "Halide_INSTALL_CMAKEDIR": "lib/cmake/Halide" } }, { @@ -127,6 +127,35 @@ "BUILD_SHARED_LIBS": "NO", "Halide_BUNDLE_LLVM": "YES" } + }, + { + "name": "package-ubuntu-shared", + "inherits": "package-unix-shared", + "displayName": "Package shared Halide for Ubuntu", + "description": "Package shared Halide for Ubuntu, using system packages.", + "binaryDir": "shared-release", + "cacheVariables": { + "Halide_SHARED_LLVM": "YES", + "LLVM_DIR": "$env{LLVM_ROOT}/lib/cmake/llvm", + "Clang_DIR": "$env{LLVM_ROOT}/lib/cmake/clang", + "LLD_DIR": "$env{LLVM_ROOT}/lib/cmake/lld", + "CMAKE_INSTALL_INCLUDEDIR": "include/Halide", + "CMAKE_INSTALL_LIBDIR": "lib/x86_64-linux-gnu", + "Halide_INSTALL_PLUGINDIR": "lib/x86_64-linux-gnu/Halide", + "Halide_INSTALL_HELPERSDIR": "lib/cmake/HalideHelpers", + "CMAKE_STRIP": "${sourceDir}/packaging/ubuntu/extra-strip.sh" + } + }, + { + "name": "package-ubuntu-static", + "inherits": "package-ubuntu-shared", + "displayName": "Package static Halide for Ubuntu", + "description": "Package static Halide for Ubuntu, using system packages.", + "binaryDir": "static-release", + "cacheVariables": { + "BUILD_SHARED_LIBS": "NO", + "WITH_DOCS": "NO" + } } ] } diff --git a/cmake/HalideGeneratorHelpers.cmake b/cmake/HalideGeneratorHelpers.cmake index 97c3edf7d3fd..c3506ea595f4 100644 --- a/cmake/HalideGeneratorHelpers.cmake +++ b/cmake/HalideGeneratorHelpers.cmake @@ -258,14 +258,31 @@ endfunction() ## function(_Halide_place_dll GEN) + if (NOT WIN32) + return() + endif () + + # Short circuit so that Halide::Halide isn't checked when importing a generator from another CMake project get_property(is_imported TARGET ${GEN} PROPERTY IMPORTED) + if (is_imported) + return() + endif () + get_property(has_post_build TARGET ${GEN} PROPERTY Halide_GENERATOR_HAS_POST_BUILD) + if (has_post_build) + return() + endif () + + # Here GEN is not IMPORTED, which means that it must be linked + # to Halide::Halide and therefore Halide::Halide must exist. get_property(halide_type TARGET Halide::Halide PROPERTY TYPE) - if (WIN32 AND NOT is_imported AND NOT has_post_build AND halide_type STREQUAL "SHARED_LIBRARY") - add_custom_command(TARGET ${GEN} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) - set_property(TARGET ${GEN} PROPERTY Halide_GENERATOR_HAS_POST_BUILD 1) + if (NOT halide_type STREQUAL "SHARED_LIBRARY") + return() endif () + + add_custom_command(TARGET ${GEN} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + set_property(TARGET ${GEN} PROPERTY Halide_GENERATOR_HAS_POST_BUILD 1) endfunction() ## diff --git a/cmake/toolchain.linux-aarch64.cmake b/cmake/toolchain.linux-aarch64.cmake index 4962ac1cf934..9b905105d4c1 100644 --- a/cmake/toolchain.linux-aarch64.cmake +++ b/cmake/toolchain.linux-aarch64.cmake @@ -14,3 +14,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +find_program(QEMU_AARCH64 qemu-aarch64) +if (QEMU_AARCH64 AND EXISTS "/usr/aarch64-linux-gnu") + set(CMAKE_CROSSCOMPILING_EMULATOR ${QEMU_AARCH64} -L /usr/aarch64-linux-gnu) +endif () diff --git a/packaging/common/HalideConfig.cmake b/packaging/common/HalideConfig.cmake index fe7afb65e42c..1a380a0f326b 100644 --- a/packaging/common/HalideConfig.cmake +++ b/packaging/common/HalideConfig.cmake @@ -55,8 +55,6 @@ set(Halide_VERSION_MINOR @Halide_VERSION_MINOR@) set(Halide_VERSION_PATCH @Halide_VERSION_PATCH@) set(Halide_VERSION_TWEAK @Halide_VERSION_TWEAK@) -set(Halide_HOST_TARGET @Halide_HOST_TARGET@) - set(Halide_ENABLE_EXCEPTIONS @Halide_ENABLE_EXCEPTIONS@) set(Halide_ENABLE_RTTI @Halide_ENABLE_RTTI@) @@ -67,7 +65,6 @@ set(Halide_ENABLE_RTTI @Halide_ENABLE_RTTI@) include(CMakeFindDependencyMacro) find_dependency(HalideHelpers "${Halide_VERSION}" EXACT) -find_dependency(Threads) if (Halide_comp_PNG) Halide_find_component_dependency(PNG PNG) diff --git a/packaging/common/HalideHelpersConfig.cmake b/packaging/common/HalideHelpersConfig.cmake index a6c999b86e15..b35b491af5b5 100644 --- a/packaging/common/HalideHelpersConfig.cmake +++ b/packaging/common/HalideHelpersConfig.cmake @@ -1,5 +1,12 @@ cmake_minimum_required(VERSION 3.16) +set(Halide_HOST_TARGET @Halide_HOST_TARGET@) + +include(CMakeFindDependencyMacro) + +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_dependency(Threads) + include(${CMAKE_CURRENT_LIST_DIR}/Halide-Interfaces.cmake) include(${CMAKE_CURRENT_LIST_DIR}/HalideTargetHelpers.cmake) include(${CMAKE_CURRENT_LIST_DIR}/HalideGeneratorHelpers.cmake) diff --git a/packaging/ubuntu/changelog b/packaging/ubuntu/changelog new file mode 100644 index 000000000000..f36c015c23e5 --- /dev/null +++ b/packaging/ubuntu/changelog @@ -0,0 +1,5 @@ +@package_name@ (@CPACK_PACKAGE_VERSION@) UNRELEASED; urgency=low + + * Initial package release. + + -- @CPACK_PACKAGE_CONTACT@ @timestamp@ -0000 diff --git a/packaging/ubuntu/config.cmake b/packaging/ubuntu/config.cmake new file mode 100644 index 000000000000..81688c63801f --- /dev/null +++ b/packaging/ubuntu/config.cmake @@ -0,0 +1,137 @@ +cmake_minimum_required(VERSION 3.19) + +include("shared-Release/CPackConfig.cmake") + +## General setup + +set(CPACK_PACKAGE_CONTACT "Alex Reinking ") +set(CPACK_STRIP_FILES TRUE) +set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_CURRENT_LIST_DIR}/pre_build.cmake") + +############################## +## Components configuration ## +############################## + +# This is a mapping from CPack component names to CMake install() components. +# We use the identity mapping here for simplicity; some advanced configurations +# with GUI installers require these to diverge. +set(CPACK_COMPONENTS_HALIDE_RUNTIME Halide_Runtime) +set(CPACK_COMPONENTS_HALIDE_DEVELOPMENT Halide_Development) +set(CPACK_COMPONENTS_HALIDE_DOCUMENTATION Halide_Documentation) + +set(CPACK_COMPONENTS_ALL Halide_Runtime Halide_Development Halide_Documentation) + +set(CPACK_INSTALL_CMAKE_PROJECTS + static-Release Halide ALL / + shared-Release Halide ALL /) + +################################### +## Ubuntu-specific configuration ## +################################### + +# We set every variable documented here: https://cmake.org/cmake/help/latest/cpack_gen/deb.html +# even if it's just to the default. That way there are no surprises. + +set(CPACK_DEB_COMPONENT_INSTALL YES) + +set(CPACK_DEBIAN_HALIDE_RUNTIME_PACKAGE_NAME libHalide${CPACK_PACKAGE_VERSION_MAJOR}) +set(CPACK_DEBIAN_HALIDE_DEVELOPMENT_PACKAGE_NAME libHalide${CPACK_PACKAGE_VERSION_MAJOR}-dev) +set(CPACK_DEBIAN_HALIDE_DOCUMENTATION_PACKAGE_NAME libHalide${CPACK_PACKAGE_VERSION_MAJOR}-doc) + +set(CPACK_DEBIAN_HALIDE_RUNTIME_FILE_NAME DEB-DEFAULT) +set(CPACK_DEBIAN_HALIDE_DEVELOPMENT_FILE_NAME DEB-DEFAULT) +set(CPACK_DEBIAN_HALIDE_DOCUMENTATION_FILE_NAME DEB-DEFAULT) + +# Debian package versions look like: :- +# is a number that increases when changing the whole versioning schema. +# We would ideally _never_ have to set this since we're using semver. +# is the version number of the actual software being packaged. +# is the version number of the _package_. Set/increment this when fixing +# bugs in the package itself. This should also not be incremented too +# frequently. It's always safe to bump the patch version when in doubt. +unset(CPACK_DEBIAN_PACKAGE_EPOCH) +set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") +unset(CPACK_DEBIAN_PACKAGE_RELEASE) + +# The default here is the host system architecture. It will generally be best +# to package for ARM on ARM, for x86 on x86, etc. The documentation gets the +# pseudo-architecture "all" to indicate that it has no binaries (ie. is arch +# independent). +unset(CPACK_DEBIAN_PACKAGE_ARCHITECTURE) +set(CPACK_DEBIAN_HALIDE_DOCUMENTATION_PACKAGE_ARCHITECTURE all) + +# Package dependencies. +# TODO: figure out how to get LLVM major version piped in here. +set(CPACK_DEBIAN_HALIDE_RUNTIME_PACKAGE_DEPENDS "llvm-11 (>= 11.0.0)") +set(CPACK_DEBIAN_HALIDE_DEVELOPMENT_PACKAGE_DEPENDS "llvm-11-dev (>= 11.0.0), liblld-11-dev (>= 11.0.0)") +set(CPACK_DEBIAN_HALIDE_DOCUMENTATION_PACKAGE_DEPENDS "") + +# Sets up package dependencies based on CPack component dependencies +set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON) + +# Uses CPACK_PACKAGE_CONTACT as default +unset(CPACK_DEBIAN_PACKAGE_MAINTAINER) + +# These inherit their values from cpack cpack_add_component +unset(CPACK_DEBIAN_HALIDE_RUNTIME_DESCRIPTION) +unset(CPACK_DEBIAN_HALIDE_DEVELOPMENT_DESCRIPTION) +unset(CPACK_DEBIAN_HALIDE_DOCUMENTATION_DESCRIPTION) + +# The Debian repository package section. +# See: https://packages.debian.org/unstable/ +# libs = Libraries to make other programs work. They provide special features to developers. +# libdevel = Libraries necessary for developers to write programs that use them. +# doc = FAQs, HOWTOs and other documents trying to explain everything related to +# Debian, and software needed to browse documentation (man, info, etc). +set(CPACK_DEBIAN_HALIDE_RUNTIME_PACKAGE_SECTION libs) +set(CPACK_DEBIAN_HALIDE_DEVELOPMENT_PACKAGE_SECTION libdevel) +set(CPACK_DEBIAN_HALIDE_DOCUMENTATION_PACKAGE_SECTION doc) + +# Deprecated: do not use +unset(CPACK_DEBIAN_ARCHIVE_TYPE) + +# Could also choose from lzma, xz, or bzip2 if one gave a better ratio. +set(CPACK_DEBIAN_COMPRESSION_TYPE "gzip") + +# Optional just means that it is optional for the safe running of +# a Debian system to have our package installed. The other categories +# do not apply to us: required (won't boot without), important (core +# system utils), and standard (basic niceties for a character-mode +# system). +set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + +# Uses CMAKE_PROJECT_HOMEPAGE_URL as default. +unset(CPACK_DEBIAN_PACKAGE_HOMEPAGE) + +# Call dpkg-shlibdeps to get dependencies on system libraries. +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +unset(CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS) # CMake 3.20+ only + +# Disable debug messaging +unset(CPACK_DEBIAN_PACKAGE_DEBUG) + +# Special variables for package constraints. We don't have any yet. +unset(CPACK_DEBIAN_PACKAGE_PREDEPENDS) +unset(CPACK_DEBIAN_PACKAGE_ENHANCES) +unset(CPACK_DEBIAN_PACKAGE_BREAKS) +unset(CPACK_DEBIAN_PACKAGE_CONFLICTS) +unset(CPACK_DEBIAN_PACKAGE_PROVIDES) +unset(CPACK_DEBIAN_PACKAGE_REPLACES) +unset(CPACK_DEBIAN_PACKAGE_RECOMMENDS) +unset(CPACK_DEBIAN_PACKAGE_SUGGESTS) + +# Generate debian/shlibs control file; require exact versions. +set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS YES) +set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY "=") + +# Add custom scripts to package. Used to ensure ldconfig runs. +unset(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA) +set(CPACK_DEBIAN_HALIDE_RUNTIME_PACKAGE_CONTROL_EXTRA + "${CMAKE_CURRENT_LIST_DIR}/triggers") +set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE) + +# Name the source package for this one. TODO? +unset(CPACK_DEBIAN_PACKAGE_SOURCE) + +# Name the package containing debug symbols for this one. TODO? +unset(CPACK_DEBIAN_DEBUGINFO_PACKAGE) diff --git a/packaging/ubuntu/copyright b/packaging/ubuntu/copyright new file mode 100644 index 000000000000..f5529a29b208 --- /dev/null +++ b/packaging/ubuntu/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: @CPACK_PACKAGE_NAME@ +Upstream-Contact: @CPACK_PACKAGE_CONTACT@ +Source: @CPACK_PACKAGE_HOMEPAGE_URL@ + +Files: * +Copyright: @copyright_line@ +License: MIT + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject + to the following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packaging/ubuntu/extra-strip.sh b/packaging/ubuntu/extra-strip.sh new file mode 100755 index 000000000000..993dbfcf70cc --- /dev/null +++ b/packaging/ubuntu/extra-strip.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# See https://github.com/Debian/debhelper/blob/5d1bb29841043d8e47ebbdd043e6cd086cad508e/dh_strip#L362-L384 +# for what dh_strip removes. + +strip --remove-section=.comment --remove-section=.note "$@" diff --git a/packaging/ubuntu/package.sh b/packaging/ubuntu/package.sh new file mode 100755 index 000000000000..b8dcd3bbe921 --- /dev/null +++ b/packaging/ubuntu/package.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e -o pipefail + +halide_source=$(readlink -f "$1") +halide_build_root=$(readlink -f "$2") + +[ -z "$halide_source" ] && echo "Usage: $0 " && exit +[ -z "$halide_build_root" ] && echo "Usage: $0 " && exit +[ -z "$LLVM_ROOT" ] && echo "Must set LLVM_ROOT to /usr/lib/llvm-VERSION" && exit + +function group() { + [[ -n "${GITHUB_ACTIONS}" && -n "${SEEN_GROUP}" ]] && echo "::endgroup::" + [[ -n "${GITHUB_ACTIONS}" ]] && echo "::group::$*" + export SEEN_GROUP=1 +} + +group "Configure shared Halide build" +cmake --preset=package-ubuntu-shared -S "$halide_source" -B "$halide_build_root/shared-Release" + +group "Configure static Halide build" +cmake --preset=package-ubuntu-static -S "$halide_source" -B "$halide_build_root/static-Release" + +group "Build shared Halide" +cmake --build "$halide_build_root/shared-Release" -- -v + +group "Build static Halide" +cmake --build "$halide_build_root/static-Release" -- -v + +group "Create Ubuntu packages" +cd "$halide_build_root" +rm -rf ./_CPack_Packages ./*.deb lintian.log +umask 0022 +export LD_LIBRARY_PATH="$halide_build_root/shared-Release/src" + +cpack -G DEB -C Release --config "$halide_source/packaging/ubuntu/config.cmake" + +# Lintian: https://lintian.debian.org/tags + +group "Run strict Lintian checks" +lintian --no-tag-display-limit -i ./*.deb + +group "Run extra Lintian checks" +lintian --no-tag-display-limit -L "=info" -i ./*.deb + +echo "Success!" diff --git a/packaging/ubuntu/pre_build.cmake b/packaging/ubuntu/pre_build.cmake new file mode 100644 index 000000000000..a74370d1fd70 --- /dev/null +++ b/packaging/ubuntu/pre_build.cmake @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.19) + +file(STRINGS "${CPACK_RESOURCE_FILE_LICENSE}" copyright_line LIMIT_COUNT 1) +string(TIMESTAMP timestamp "%a, %d %b %Y %H:%M:%S" UTC) + +find_program(GZIP gzip) +if (NOT GZIP) + message(FATAL_ERROR "Could not find gzip") +endif () + +foreach (comp IN LISTS CPACK_COMPONENTS_ALL) + string(TOUPPER "CPACK_DEBIAN_${comp}_PACKAGE_NAME" package_name_var) + string(TOLOWER "${${package_name_var}}" package_name) + + # Write copyright information to the package. + configure_file("${CMAKE_CURRENT_LIST_DIR}/copyright" + "${CPACK_TEMPORARY_DIRECTORY}/${comp}/usr/share/doc/${package_name}/copyright" + @ONLY NO_SOURCE_PERMISSIONS) + + # Write changelog to the package. + set(changelog "${CPACK_TEMPORARY_DIRECTORY}/${comp}/usr/share/doc/${package_name}/changelog") + configure_file("${CMAKE_CURRENT_LIST_DIR}/changelog" "${changelog}" + @ONLY NO_SOURCE_PERMISSIONS) + execute_process(COMMAND "${GZIP}" -n9 "${changelog}" COMMAND_ERROR_IS_FATAL ANY) +endforeach () diff --git a/packaging/ubuntu/triggers b/packaging/ubuntu/triggers new file mode 100644 index 000000000000..dd8660367847 --- /dev/null +++ b/packaging/ubuntu/triggers @@ -0,0 +1 @@ +activate-noawait ldconfig diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt new file mode 100644 index 000000000000..b07a74bcabdc --- /dev/null +++ b/test/integration/CMakeLists.txt @@ -0,0 +1,133 @@ +cmake_minimum_required(VERSION 3.16) +project(integration_tests NONE) + +enable_testing() + +## +# Single-linkage JIT integration tests. +## + +foreach (bsl IN ITEMS "" "-DBUILD_SHARED_LIBS=NO" "-DBUILD_SHARED_LIBS=YES") + foreach (hsl IN ITEMS "" "-DHalide_SHARED_LIBS=NO" "-DHalide_SHARED_LIBS=YES") + foreach (comp IN ITEMS "" "-Djit_HALIDE_COMPONENTS=static" "-Djit_HALIDE_COMPONENTS=shared") + # Compute whether we expect to link to static or shared Halide given the relevant variables. + # Explicitly listing a component always wins. Then Halide_SHARED_LIBS takes over. If that's + # not available, it consults BUILD_SHARED_LIBS. If that's not defined, it defaults to shared, + # rather than static, because that's less likely to lead to pathologies with generators. + if (comp MATCHES "shared") + set(expect_shared TRUE) + elseif (comp MATCHES "static") + set(expect_shared FALSE) + elseif (hsl MATCHES "YES") + set(expect_shared TRUE) + elseif (hsl MATCHES "NO") + set(expect_shared FALSE) + elseif (bsl MATCHES "YES") + set(expect_shared TRUE) + elseif (bsl MATCHES "NO") + set(expect_shared FALSE) + else () + set(expect_shared TRUE) + endif () + + set(test_name "${bsl} ${hsl} ${comp}") + string(REPLACE "-D" "" test_name "${test_name}") + string(STRIP "${test_name}" test_name) + string(MAKE_C_IDENTIFIER "jit_${test_name}" test_name) + + set(build_step "check_builds_${test_name}") + set(check_link_step "check_linkage_${test_name}") + + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/jit/${test_name}") + + # This builds and runs the tiny example app. + add_test(NAME "${build_step}" + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/jit" "${build_dir}" + --build-generator Ninja + --build-options ${bsl} ${hsl} ${comp} -DCMAKE_BUILD_TYPE=Release + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) + + # Run ldd on the output binary. The pass/fail regexes are set later. + add_test(NAME "${check_link_step}" + COMMAND ldd "${build_dir}/main") + + # Make sure we don't run ldd before building... + set_tests_properties("${build_step}" PROPERTIES FIXTURES_SETUP "${test_name}") + set_tests_properties("${check_link_step}" PROPERTIES FIXTURES_REQUIRED "${test_name}") + + if (expect_shared) + set_tests_properties("${check_link_step}" PROPERTIES PASS_REGULAR_EXPRESSION "libHalide") + else () + set_tests_properties("${check_link_step}" PROPERTIES FAIL_REGULAR_EXPRESSION "libHalide") + endif () + endforeach () + endforeach () +endforeach () + +## +# AOT integration tests +## + +add_test(NAME aot_shared_generator + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/aot" "${CMAKE_CURRENT_BINARY_DIR}/aot-shared" + --build-generator Ninja + --build-options -DCMAKE_BUILD_TYPE=Release + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) + +add_test(NAME aot_static_generator + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/aot" "${CMAKE_CURRENT_BINARY_DIR}/aot-static" + --build-generator Ninja + --build-options -DHalide_SHARED_LIBS=NO -DCMAKE_BUILD_TYPE=Release + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) + +add_test(NAME aot_shared_generator_adams2019 + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/aot" "${CMAKE_CURRENT_BINARY_DIR}/aot-shared-auto" + --build-generator Ninja + --build-options -DCMAKE_BUILD_TYPE=Release -Daot_USE_AUTOSCHEDULER=YES + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) + +add_test(NAME aot_static_generator_adams2019 + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/aot" "${CMAKE_CURRENT_BINARY_DIR}/aot-static-auto" + --build-generator Ninja + --build-options -DHalide_SHARED_LIBS=NO -DCMAKE_BUILD_TYPE=Release -Daot_USE_AUTOSCHEDULER=YES + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) + +# Cannot use autoscheduler with generators linked to STATIC Halide +set_tests_properties(aot_static_generator_adams2019 + PROPERTIES + WILL_FAIL TRUE + FAIL_REGULAR_EXPRESSION "Autoscheduler Halide::[A-Za-z0-9_]+ does not exist") + +## +# Cross compiling test +## + +if (CMAKE_HOST_SYSTEM_NAME MATCHES "Linux") + add_test(NAME cross_compile_host + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/xc" "${CMAKE_CURRENT_BINARY_DIR}/xc-host" + --build-generator Ninja + --build-options -DCMAKE_BUILD_TYPE=Release + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) + + add_test(NAME cross_compile_aarch64 + COMMAND + ${CMAKE_CTEST_COMMAND} + --build-and-test "${CMAKE_CURRENT_LIST_DIR}/xc" "${CMAKE_CURRENT_BINARY_DIR}/xc-aarch64" + --build-generator Ninja + --build-options + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_CURRENT_LIST_DIR}/../../cmake/toolchain.linux-aarch64.cmake + -DCMAKE_BUILD_TYPE=Release + --test-command ${CMAKE_CTEST_COMMAND} --output-on-failure) +endif () diff --git a/test/integration/README.md b/test/integration/README.md new file mode 100644 index 000000000000..b458f76220d0 --- /dev/null +++ b/test/integration/README.md @@ -0,0 +1,19 @@ +# Integration tests + +These tests validate our CMake-built packages and make sure reasonable +interactions with the Halide-generated libraries and targets work. They run on +GitHub Actions, rather than the buildbots, to test building, installing, and +using Halide in **simple** cases from a clean build environment. In particular, +this folder **should not** be added to the main Halide build +via `add_subdirectory`. + +The assumption is that we are building Halide with the latest CMake version, but +that our users might be on our oldest supported version. GitHub Actions makes it +easy to use two different versions on two different VMs. + +There are scenarios here for JIT compilation, AOT compilation, and AOT _cross_ +compilation from x86 to aarch64 (tested via Qemu). This test in particular cannot +be easily run on the buildbots because it requires two VMs: an Ubuntu build machine +for Halide, and an Ubuntu developer machine which installs the DEB packages. + +Consult the `packaging.yml` workflow file for precise steps to run these locally. diff --git a/test/integration/aot/CMakeLists.txt b/test/integration/aot/CMakeLists.txt new file mode 100644 index 000000000000..4e6ae6d2b3d0 --- /dev/null +++ b/test/integration/aot/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.16) +project(aot) + +enable_testing() + +find_package(Halide REQUIRED) + +option(aot_USE_AUTOSCHEDULER "Use the autoscheduler" OFF) +if (aot_USE_AUTOSCHEDULER) + set(extra_options AUTOSCHEDULER Halide::Adams2019) +endif () + +add_executable(add_gen add.cpp) +target_link_libraries(add_gen PRIVATE Halide::Generator) + +add_halide_library(add FROM add_gen + ${extra_options} + REGISTRATION add_registration) + +add_executable(run_add ${add_registration}) +target_link_libraries(run_add PRIVATE Halide::RunGenMain add) + +add_test(NAME benchmark + COMMAND run_add --benchmarks=all --output_extents=[64,64]) + +set_tests_properties(benchmark PROPERTIES PASS_REGULAR_EXPRESSION "Best output throughput") diff --git a/test/integration/aot/add.cpp b/test/integration/aot/add.cpp new file mode 100644 index 000000000000..e6025f668aad --- /dev/null +++ b/test/integration/aot/add.cpp @@ -0,0 +1,15 @@ +#include +using namespace Halide; + +struct Add : Generator { + Output> output{"output", 2}; + + void generate() { + Var x, y; + output(x, y) = x + y; + + output.set_estimates({{0, 64}, {0, 64}}); + } +}; + +HALIDE_REGISTER_GENERATOR(Add, add); diff --git a/test/integration/jit/CMakeLists.txt b/test/integration/jit/CMakeLists.txt new file mode 100644 index 000000000000..c8d5a6546a96 --- /dev/null +++ b/test/integration/jit/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.16) +project(jit) + +enable_testing() + +set(jit_HALIDE_VERSION "" + CACHE STRING "Optional version argument to find_package(Halide)") + +set(jit_HALIDE_COMPONENTS "" + CACHE STRING "Optional required-components argument to find_package(Halide)") + +find_package(Halide ${jit_HALIDE_VERSION} REQUIRED ${jit_HALIDE_COMPONENTS}) + +add_executable(main main.cpp) +target_link_libraries(main PRIVATE Halide::Halide) + +add_test(NAME validate COMMAND main) + +set_tests_properties(validate PROPERTIES PASS_REGULAR_EXPRESSION "Success!") diff --git a/test/integration/jit/main.cpp b/test/integration/jit/main.cpp new file mode 100644 index 000000000000..3b74e04d88ed --- /dev/null +++ b/test/integration/jit/main.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +using namespace Halide; + +int main() { + Var x{"x"}, y{"y"}; + Func test{"test"}; + + test(x, y) = x + y; + Buffer output = test.realize({4, 4}); + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + if (output(i, j) != (i + j)) { + fprintf(stderr, "output(%d, %d) = %d, expected %d", i, j, output(i, j), i + j); + return EXIT_FAILURE; + } + } + } + + printf("Success!\n"); + return EXIT_SUCCESS; +} diff --git a/test/integration/xc/CMakeLists.txt b/test/integration/xc/CMakeLists.txt new file mode 100644 index 000000000000..5ac15e6b16d8 --- /dev/null +++ b/test/integration/xc/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.16) +project(xc LANGUAGES NONE) + +enable_testing() + +if (NOT CMAKE_CROSSCOMPILING) + # There is a bug with CTest's build-and-test that forwards environment + # variables, such as those set by the toolchain, to child processes. By + # not enabling CXX in the cross-compiling scenario, we can work around + # this bug. The alternative would be to not use build-and-test in the + # testing code, but it's used in every other location. This approach + # also has the benefit of being marginally faster because it doesn't + # waste a few seconds detecting a compiler that won't be used. + # https://gitlab.kitware.com/cmake/cmake/-/issues/22043 + enable_language(CXX) + + # Do things the easy way when not cross compiling + add_subdirectory(generators) + add_subdirectory(add) +else () + # When cross compiling, use ExternalProject to stage building the + # generators with a host toolchain before passing the resulting + # package to the library build, which will use the target toolchain. + include(ExternalProject) + + set(xc_HOST_TOOLCHAIN_FILE "" + CACHE FILEPATH "Toolchain file to use when compiling generators") + + ExternalProject_Add( + generators + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generators" + INSTALL_COMMAND "" + BUILD_ALWAYS YES + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE=${xc_HOST_TOOLCHAIN_FILE} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + ) + + ExternalProject_Get_Property(generators BINARY_DIR) + + ExternalProject_Add( + add + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/add" + INSTALL_COMMAND "" + BUILD_ALWAYS YES + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -Dxc-generators_ROOT=${BINARY_DIR} + ) + + ExternalProject_Add_StepDependencies(add configure generators) + + ExternalProject_Get_Property(add BINARY_DIR) + + add_test(NAME run-tests + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + WORKING_DIRECTORY ${BINARY_DIR}) +endif () diff --git a/test/integration/xc/add/CMakeLists.txt b/test/integration/xc/add/CMakeLists.txt new file mode 100644 index 000000000000..c3b64f88c0bd --- /dev/null +++ b/test/integration/xc/add/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.16) +project(xc-add) + +enable_testing() + +## +## Dependencies +## + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + # Need to load generators from stage when cross compiling + find_package(xc-generators REQUIRED) +endif () + +find_package(HalideHelpers REQUIRED) + +## +## Generate library +## + +add_halide_library(add FROM xc::add_gen + TARGETS cmake + REGISTRATION add_reg_cpp) +add_library(xc::add ALIAS add) + +## +## Helper runner +## + +add_executable(run_add ${add_reg_cpp}) +target_link_libraries(run_add PRIVATE add Halide::RunGenMain) + +## +## Test helper +## + +add_test(NAME run_add + COMMAND run_add --output_extents=[10,10] --benchmarks=all) + +## +## Installation +## + +include(GNUInstallDirs) + +set(xc-add_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/cmake/xc-add" + CACHE PATH "Path to CMake files") + +install(TARGETS add add.runtime + EXPORT xc-add-config) + +install(EXPORT xc-add-config + DESTINATION "${xc-add_INSTALL_CMAKEDIR}" + NAMESPACE xc::) diff --git a/test/integration/xc/generators/CMakeLists.txt b/test/integration/xc/generators/CMakeLists.txt new file mode 100644 index 000000000000..d2dc5ab22815 --- /dev/null +++ b/test/integration/xc/generators/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.16) +project(xc-generators) + +## +## Dependencies +## + +find_package(Halide REQUIRED) + +## +## Create generator target +## + +add_executable(add_gen add.cpp) +add_executable(xc::add_gen ALIAS add_gen) +target_link_libraries(add_gen PRIVATE Halide::Generator) + +## +## Export (staging) +## + +export(TARGETS add_gen + NAMESPACE xc:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/xc-generators/xc-generators-config.cmake") diff --git a/test/integration/xc/generators/add.cpp b/test/integration/xc/generators/add.cpp new file mode 100644 index 000000000000..e6025f668aad --- /dev/null +++ b/test/integration/xc/generators/add.cpp @@ -0,0 +1,15 @@ +#include +using namespace Halide; + +struct Add : Generator { + Output> output{"output", 2}; + + void generate() { + Var x, y; + output(x, y) = x + y; + + output.set_estimates({{0, 64}, {0, 64}}); + } +}; + +HALIDE_REGISTER_GENERATOR(Add, add); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7b8bbde2b01e..221227bed4d0 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -32,7 +32,7 @@ target_include_directories(Halide_ImageIO INTERFACE $) -target_link_libraries(Halide_RunGenMain INTERFACE Halide::Runtime Halide::ImageIO Threads::Threads ${CMAKE_DL_LIBS}) +target_link_libraries(Halide_RunGenMain INTERFACE Halide::Runtime Halide::ImageIO Threads::Threads) set_target_properties(Halide_RunGenMain PROPERTIES EXPORT_NAME RunGenMain) add_library(Halide_Generator INTERFACE)