Skip to content

Commit

Permalink
Introduce HalideFeatures system for optional components (#8384)
Browse files Browse the repository at this point in the history
Previously, our `option()` declarations were scattered and not well
documented. They certainly weren't self-documenting. Some of them
depended on other options and used various ways to handle conflicts.
Sometimes inconsistencies were handled with fatal errors, other times
by silently overriding an option.

With this PR, we introduce a new `Halide_feature` function that is
designed to handle interdependent options and default initialization
in a much more regular way.

It behaves very much like option in its first three parameters:

    Halide_feature(CMAKE_FLAG "documentation string" DEFAULT_VALUE)

Only now `DEFAULT_VALUE` can be more intelligent than simply `ON`
or `OFF`. It can also be `TOP_LEVEL`, which is `ON`
iff `CMAKE_PROJECT_TOP_LEVEL` is true. It can also be `AUTO` which
is `ON` iff the `DEPENDS` clause is defined and true. For example,

    Halide_feature(WITH_TEST_RUNTIME "Build runtime tests" AUTO
                DEPENDS NOT MSVC)

If a feature is set to `ON` but its `DEPENDS` clause is false, a warning
will be issued and the feature will be forced `OFF` in the cache.

Furthermore, these features register their documentation strings with
the built-in `FeatureSummary` system so now instead of a stream of
easy-to-miss messages, the configuration ends with a summary of what
is enabled and disabled:

    -- The following features have been enabled:

    * Halide_ENABLE_EXCEPTIONS, Enable exceptions in Halide
    * Halide_ENABLE_RTTI, Enable RTTI in Halide
    * WITH_AUTOSCHEDULERS, Build the Halide autoschedulers
    * WITH_PACKAGING, Halide's CMake package install rules
    * WITH_PYTHON_BINDINGS, Halide's native Python module (not the whole pip package)
    * WITH_SERIALIZATION, Include experimental Serialization/Deserialization code
    * WITH_TESTS, Halide's unit test suite
    * WITH_TUTORIALS, Halide's tutorial code
    * WITH_UTILS, Optional utility programs for Halide, including HalideTraceViz
    * WITH_TEST_AUTO_SCHEDULE, Build autoscheduler tests
    * WITH_TEST_CORRECTNESS, Build correctness tests
    * WITH_TEST_ERROR, Build error tests
    * WITH_TEST_WARNING, Build warning tests
    * WITH_TEST_PERFORMANCE, Build performance tests
    * WITH_TEST_GENERATOR, Build generator tests
    * WITH_TEST_RUNTIME, Build runtime tests

    -- The following features have been disabled:

    * WITH_DOCS, Halide's Doxygen documentation
    * WITH_TEST_FUZZ, Build fuzz tests

A feature may be marked as `ADVANCED`, which excludes it from the
feature summary unless the log level is set to verbose. It also marks
it as advanced in the cache, which hides it from the default view in
the CMake GUI and the curses-TUI.

Finally, features are computed early in the build so that subdirectories
see a consistent view. Some generator tests that were broken under
static Halide (meaning no autoschedulers) are now properly skipped by
directly checking `WITH_AUTOSCHEDULERS`.
  • Loading branch information
alexreinking authored Aug 9, 2024
1 parent ff538b1 commit 7b53a88
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 121 deletions.
76 changes: 29 additions & 47 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ file(CONFIGURE OUTPUT "${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/HalideHelpersConfig.c
##

# Import useful standard modules
include(CMakeDependentOption)
include(CheckCXXSymbolExists)

# Make our custom helpers available throughout the project via include().
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
include(HalideGeneratorHelpers)
include(HalideFeatures)

# Build Halide as a shared lib by default, but still honor command-line settings.
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
Expand Down Expand Up @@ -66,10 +66,8 @@ if (CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR "Halide requires C++17 or newer but CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}")
endif ()

# Build Halide with ccache if the package is present
option(Halide_CCACHE_BUILD "Set to ON for a ccache enabled build" OFF)
mark_as_advanced(Halide_CCACHE_BUILD)

# Build Halide with ccache if the package is present and the user requested it
Halide_feature(Halide_CCACHE_BUILD "Build with CCache as best configured for Halide" OFF ADVANCED)
if (Halide_CCACHE_BUILD)
find_program(CCACHE_PROGRAM ccache REQUIRED)

Expand All @@ -91,8 +89,6 @@ if (Halide_CCACHE_BUILD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(APPEND CMAKE_CXX_FLAGS " -Xclang -fno-pch-timestamp")
endif ()

message(STATUS "Enabling ccache usage for building.")
endif ()

# Detect whether or not ASAN is enabled. Don't cache the result to ensure this
Expand Down Expand Up @@ -206,70 +202,56 @@ find_package(JPEG)
find_package(PNG)

##
# Declare options
##

# Declare these options after we include dependencies (since it declares Halide_ENABLE_RTTI etc)
# but before we add any subdirectories, since any option you test before it is defined is
# implicitly false the *first* time that the build file is processed, and there are some
# out-of-order dependencies here (e.g, code in src/ eventually checks WITH_UTILS).
# This is especially subtle since it means that some options can end up with different
# values if you build a target as part of the initial CMake run, so (e.g.) a `make install`
# from as totally clean build might neglect to install some pieces.

option(WITH_TESTS "Build tests" "${PROJECT_IS_TOP_LEVEL}")
option(WITH_TUTORIALS "Build tutorials" "${PROJECT_IS_TOP_LEVEL}")
option(WITH_DOCS "Build documentation" OFF)
option(WITH_UTILS "Build utils" "${PROJECT_IS_TOP_LEVEL}")
cmake_dependent_option(
WITH_PYTHON_BINDINGS "Build Python bindings" "${PROJECT_IS_TOP_LEVEL}"
"Halide_ENABLE_RTTI AND Halide_ENABLE_EXCEPTIONS" OFF
)
# Optional features. These settings are defined early so that subdirectories see a consistent view

Halide_feature(Halide_BUNDLE_STATIC "Bundle Halide's static dependencies" OFF ADVANCED
DEPENDS NOT BUILD_SHARED_LIBS)

Halide_feature(Halide_ENABLE_EXCEPTIONS "Enable exceptions in Halide" ON)
Halide_feature(Halide_ENABLE_RTTI "Enable RTTI in Halide" ON
DEPENDS LLVM_ENABLE_RTTI)

Halide_feature(WITH_AUTOSCHEDULERS "Build the Halide autoschedulers" ON
DEPENDS BUILD_SHARED_LIBS)
Halide_feature(WITH_DOCS "Halide's Doxygen documentation" OFF)
Halide_feature(WITH_PACKAGING "Halide's CMake package install rules" TOP_LEVEL)
Halide_feature(WITH_PYTHON_BINDINGS "Halide's native Python module (not the whole pip package)" ON
DEPENDS Halide_ENABLE_EXCEPTIONS AND Halide_ENABLE_RTTI)
Halide_feature(WITH_SERIALIZATION "Include experimental Serialization/Deserialization code" ON)
Halide_feature(WITH_SERIALIZATION_JIT_ROUNDTRIP_TESTING
"Intercepting JIT compilation with a serialization roundtrip, for test only"
OFF ADVANCED
DEPENDS WITH_SERIALIZATION)
Halide_feature(WITH_TESTS "Halide's unit test suite" TOP_LEVEL)
Halide_feature(WITH_TUTORIALS "Halide's tutorial code" TOP_LEVEL)
Halide_feature(WITH_UTILS "Optional utility programs for Halide, including HalideTraceViz" TOP_LEVEL)

##
# Add source directories
##

add_subdirectory(src)
add_subdirectory(tools)

##
# Add tests, tutorials, etc. if we're not being imported into another CMake project.
##

if (WITH_TESTS)
message(STATUS "Building tests enabled")
add_subdirectory(test)
else ()
message(STATUS "Building tests disabled")
endif ()

if (WITH_PYTHON_BINDINGS)
message(STATUS "Building Python bindings enabled")
add_subdirectory(python_bindings)
else ()
message(STATUS "Building Python bindings disabled")
endif ()

if (WITH_TUTORIALS)
message(STATUS "Building tutorials enabled")
add_subdirectory(tutorial)
else ()
message(STATUS "Building tutorials disabled")
endif ()

if (WITH_DOCS)
message(STATUS "Building docs enabled")
add_subdirectory(doc)
else ()
message(STATUS "Building docs disabled")
endif ()

if (WITH_UTILS)
message(STATUS "Building utils enabled")
add_subdirectory(util)
else ()
message(STATUS "Building utils disabled")
endif ()

add_subdirectory(packaging)
if (WITH_PACKAGING)
add_subdirectory(packaging)
endif ()
14 changes: 7 additions & 7 deletions README_cmake.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,13 +412,13 @@ through the [`add_subdirectory`][add_subdirectory] or
[`FetchContent`][fetchcontent] mechanisms. They control whether non-essential
targets (like tests and documentation) are built.

| Option | Default | Description |
|------------------------|----------------------|------------------------------------------------------------------|
| `WITH_TESTS` | `ON` | Enable building unit and integration tests |
| `WITH_PYTHON_BINDINGS` | `ON` if Python found | Enable building Python 3.x bindings |
| `WITH_DOCS` | `OFF` | Enable building the documentation via Doxygen |
| `WITH_UTILS` | `ON` | Enable building various utilities including the trace visualizer |
| `WITH_TUTORIALS` | `ON` | Enable building the tutorials |
| Option | Default | Description |
|------------------------|---------|------------------------------------------------------------------|
| `WITH_TESTS` | `ON` | Enable building unit and integration tests |
| `WITH_PYTHON_BINDINGS` | `ON` | Enable building Python 3.x bindings |
| `WITH_DOCS` | `OFF` | Enable building the documentation via Doxygen |
| `WITH_UTILS` | `ON` | Enable building various utilities including the trace visualizer |
| `WITH_TUTORIALS` | `ON` | Enable building the tutorials |

The following options control whether to build certain test subsets. They only
apply when `WITH_TESTS=ON`:
Expand Down
59 changes: 59 additions & 0 deletions cmake/HalideFeatures.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
if (PROJECT_IS_TOP_LEVEL)
include(FeatureSummary)
cmake_language(
DEFER DIRECTORY "${Halide_SOURCE_DIR}"
CALL feature_summary WHAT ENABLED_FEATURES DISABLED_FEATURES
)
endif ()

function(_Halide_feature_info opt doc)
if (NOT PROJECT_IS_TOP_LEVEL)
return()
endif ()

set(notice "")
if (ARG_ADVANCED)
cmake_language(GET_MESSAGE_LOG_LEVEL log_level)
if (log_level MATCHES "^(VERBOSE|DEBUG|TRACE)$")
set(notice " (advanced)")
else ()
return()
endif ()
endif ()

add_feature_info("${opt}${notice}" "${opt}" "${doc}")
endfunction()

function(Halide_feature OPTION DOC DEFAULT)
cmake_parse_arguments(PARSE_ARGV 3 ARG "ADVANCED" "" "DEPENDS")

if (DEFAULT STREQUAL "TOP_LEVEL")
set(default_value "${PROJECT_IS_TOP_LEVEL}")
elseif (DEFAULT STREQUAL "AUTO")
set(default_value ${ARG_DEPENDS})
else ()
set(default_value ${DEFAULT})
endif ()

if (${default_value})
set(default_value ON)
else ()
set(default_value OFF)
endif ()

option("${OPTION}" "${DOC}" "${default_value}")
if (ARG_ADVANCED)
mark_as_advanced("${OPTION}")
endif ()

if (${OPTION} AND DEFINED ARG_DEPENDS AND NOT (${ARG_DEPENDS}))
list(JOIN ARG_DEPENDS " " depends)
message(WARNING "${OPTION} forcibly disabled -- requires ${depends}")
set("${OPTION}" 0)
set("${OPTION}" "${${OPTION}}" CACHE BOOL "${DOC}" FORCE)
endif ()

_Halide_feature_info("${OPTION}" "${DOC}")

set("${OPTION}" "${${OPTION}}" PARENT_SCOPE)
endfunction()
49 changes: 23 additions & 26 deletions packaging/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ install(TARGETS Halide Halide_Generator Halide_GenGen
ARCHIVE COMPONENT Halide_Development
FILE_SET HEADERS COMPONENT Halide_Development)

if (TARGET Halide_Adams2019)
if (WITH_AUTOSCHEDULERS)
install(TARGETS Halide_Adams2019 Halide_Li2018 Halide_Mullapudi2016 Halide_Anderson2021
EXPORT Halide_Interfaces
LIBRARY DESTINATION ${Halide_INSTALL_PLUGINDIR} COMPONENT Halide_Runtime
Expand Down Expand Up @@ -59,36 +59,33 @@ install(TARGETS Halide_Tools Halide_ImageIO Halide_RunGenMain Halide_ThreadPool
FILE_SET HEADERS COMPONENT Halide_Development DESTINATION ${Halide_INSTALL_TOOLSDIR})

##
# Patch RPATH for executable targets
# Install command-line utils
##

file(RELATIVE_PATH lib_dir
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
if (WITH_AUTOSCHEDULERS AND WITH_UTILS)
file(RELATIVE_PATH lib_dir
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})

if (APPLE)
set(rbase @loader_path)
else ()
set(rbase $ORIGIN)
endif ()
if (APPLE)
set(rbase @loader_path)
else ()
set(rbase $ORIGIN)
endif ()

foreach (util IN ITEMS adams2019_retrain_cost_model
adams2019_weightsdir_to_weightsfile
anderson2021_retrain_cost_model
anderson2021_weightsdir_to_weightsfile
featurization_to_sample
get_host_target)
if (TARGET ${util})
if (NOT CMAKE_INSTALL_RPATH)
set_target_properties(${util} PROPERTIES INSTALL_RPATH "${rbase};${rbase}/${lib_dir}")
endif ()
install(
TARGETS ${util}
EXPORT Halide_Interfaces
COMPONENT Halide_Development
)
set(utils
adams2019_retrain_cost_model
adams2019_weightsdir_to_weightsfile
anderson2021_retrain_cost_model
anderson2021_weightsdir_to_weightsfile
featurization_to_sample
get_host_target
)
if (NOT CMAKE_INSTALL_RPATH)
set_target_properties(${utils} PROPERTIES INSTALL_RPATH "${rbase};${rbase}/${lib_dir}")
endif ()
endforeach ()
install(TARGETS ${utils} EXPORT Halide_Interfaces COMPONENT Halide_Development)
endif ()

##
# READMEs and other top-level documentation
Expand Down
5 changes: 5 additions & 0 deletions python_bindings/apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ if (Halide_TARGET MATCHES "vulkan")
return()
endif ()

if (NOT WITH_AUTOSCHEDULERS)
message(WARNING "Python apps are skipped without autoschedulers")
return()
endif ()

set(TEST_TMPDIR "$<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}>")
set(TEST_IMAGES_DIR "$<SHELL_PATH:${CMAKE_CURRENT_SOURCE_DIR}/../../apps/images>")

Expand Down
5 changes: 5 additions & 0 deletions python_bindings/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ if (Halide_TARGET MATCHES "wasm")
return()
endif ()

if (NOT WITH_AUTOSCHEDULERS)
message(WARNING "Python tests are skipped without autoschedulers")
return()
endif ()

add_subdirectory(correctness)
add_subdirectory(generators)
15 changes: 4 additions & 11 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,6 @@ target_compile_definitions(Halide PRIVATE WITH_WEBGPU)
##

# Build serialization, enabled by default
option(WITH_SERIALIZATION "Include experimental Serialization/Deserialization code" ON)
if (WITH_SERIALIZATION)
# Sadly, there seem to be at least three variations of the Flatbuffer
# package in terms of the case of the relevant CMake files. Fortunately,
Expand Down Expand Up @@ -546,10 +545,6 @@ endif ()

# Enable serialization testing by intercepting JIT compilation with a serialization roundtrip;
# This is used only for special builds made specifically for testing, and must be disabled by default.
cmake_dependent_option(
WITH_SERIALIZATION_JIT_ROUNDTRIP_TESTING "Intercepting JIT compilation with a serialization roundtrip, for test only" OFF
"WITH_SERIALIZATION" OFF
)
if (WITH_SERIALIZATION_JIT_ROUNDTRIP_TESTING)
target_compile_definitions(Halide PRIVATE WITH_SERIALIZATION_JIT_ROUNDTRIP_TESTING)
endif ()
Expand Down Expand Up @@ -642,8 +637,9 @@ target_compile_definitions(Halide

##
# RTTI and exceptions settings
##

option(Halide_ENABLE_RTTI "Enable RTTI in Halide" "${LLVM_ENABLE_RTTI}")
# RTTI
set_property(TARGET Halide PROPERTY CXX_RTTI "${Halide_ENABLE_RTTI}")
set_property(TARGET Halide APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL CXX_RTTI)

Expand All @@ -658,7 +654,7 @@ else ()
)
endif ()

option(Halide_ENABLE_EXCEPTIONS "Enable exceptions in Halide" ON)
# Exceptions
if (Halide_ENABLE_EXCEPTIONS)
target_compile_definitions(Halide PUBLIC HALIDE_WITH_EXCEPTIONS)
else ()
Expand All @@ -677,9 +673,6 @@ endif ()
# Add autoschedulers to the build.
##

if (BUILD_SHARED_LIBS)
message(STATUS "Building autoschedulers enabled")
if (WITH_AUTOSCHEDULERS)
add_subdirectory(autoschedulers)
else ()
message(STATUS "Building autoschedulers disabled (static Halide)")
endif ()
14 changes: 10 additions & 4 deletions src/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,10 @@ set(RUNTIME_CXX_FLAGS
-Wno-sync-alignment
)

option(Halide_CLANG_TIDY_BUILD "Generate fake compile jobs for runtime files when running clang-tidy." OFF)
mark_as_advanced(Halide_CLANG_TIDY_BUILD)
Halide_feature(
Halide_CLANG_TIDY_BUILD "Generate fake compile jobs for runtime files when running clang-tidy." OFF
ADVANCED
)

foreach (i IN LISTS RUNTIME_CPP)
foreach (j IN ITEMS 32 64)
Expand Down Expand Up @@ -353,7 +355,11 @@ target_sources(Halide_Runtime
FILE_SET HEADERS
FILES ${RUNTIME_HEADER_FILES})

option(Halide_BUILD_HEXAGON_REMOTE_RUNTIME "Build the hexagon remote runtime for offloading to Hexagon (HVX)" OFF)
if (Halide_BUILD_HEXAGON_REMOTE_RUNTIME AND NOT Halide_CLANG_TIDY_BUILD)
Halide_feature(
Halide_BUILD_HEXAGON_REMOTE_RUNTIME "Build the hexagon remote runtime for offloading to Hexagon (HVX)" OFF
DEPENDS NOT Halide_CLANG_TIDY_BUILD
ADVANCED
)
if (Halide_BUILD_HEXAGON_REMOTE_RUNTIME)
add_subdirectory(hexagon_remote)
endif ()
Loading

0 comments on commit 7b53a88

Please sign in to comment.