diff --git a/README.md b/README.md index e99a4fa22490..9a51a7f26d12 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ currently targets: - CPU architectures: X86, ARM, Hexagon, PowerPC, RISC-V - Operating systems: Linux, Windows, macOS, Android, iOS, Qualcomm QuRT -- GPU Compute APIs: CUDA, OpenCL, Apple Metal, Microsoft - Direct X 12, Vulkan +- GPU Compute APIs: CUDA, OpenCL, Apple Metal, Microsoft Direct X 12, Vulkan Rather than being a standalone programming language, Halide is embedded in C++. This means you write C++ code that builds an in-memory representation of a @@ -18,26 +17,71 @@ in Python without C++. Halide requires C++17 (or later) to use. -For more detail about what Halide is, see http://halide-lang.org. +For more detail about what Halide is, see https://halide-lang.org. -For API documentation see http://halide-lang.org/docs +For API documentation see https://halide-lang.org/docs. -To see some example code, look in the tutorials directory. +For some example code, read through the tutorials online +at https://halide-lang.org/tutorials. The corresponding code is in the +`tutorials/` directory. Larger examples are in the `apps/` directory. If you've acquired a full source distribution and want to build Halide, see the -[notes below](#building-halide-with-cmake). +[notes below](#building-halide). # Getting Halide +## Pip + +As of Halide 19.0.0, we provide binary wheels on PyPI. Halide provides bindings +for C++ and Python. Even if you only intend to use Halide from C++, pip may be +the easiest way to get a binary build of Halide. + +Full releases may be installed with `pip` like so: + +```shell +$ pip install halide +``` + +Every commit to `main` is published to Test PyPI as a development version and +these may be installed with a few extra flags: + +```shell +$ pip install halide --pre --extra-index-url https://test.pypi.org/simple +``` + +Currently, we provide wheels for: Windows x86-64, macOS x86-64, macOS arm64, and +Linux x86-64. The Linux wheels are built for manylinux_2_28, which makes them +broadly compatible (Debian 10, Ubuntu 18.10, Fedora 29). + +## Homebrew + +Alternatively, if you use macOS, you can install Halide via +[Homebrew](https://brew.sh/) like so: + +``` +$ brew install halide +``` + ## Binary tarballs The latest version of Halide can always be found on GitHub at https://github.com/halide/Halide/releases -We provide binary releases for many popular platforms and architectures, -including 32/64-bit x86 Windows, 64-bit macOS, and 32/64-bit x86/ARM +We provide binary releases for many popular platforms and architectures, +including 32/64-bit x86 Windows, 64-bit x86/ARM macOS, and 32/64-bit x86/ARM Ubuntu Linux. +The macOS releases are built using XCode's command-line tools with Apple Clang +500.2.76. This means that we link against libc++ instead of libstdc++. You may +need to adjust compiler options accordingly if you're using an older XCode which +does not default to libc++. + +We use a recent Ubuntu LTS to build the Linux releases; if your distribution is +too old, it might not have the requisite glibc. + +Nightly builds of Halide and the LLVM versions we use in CI are also available +at https://buildbot.halide-lang.org/ + ## Vcpkg If you use [vcpkg](https://github.com/microsoft/vcpkg) to manage dependencies, @@ -52,27 +96,16 @@ code for the active platform. If you want to include all the backends, you should install `halide[target-all]:x64-windows` instead. Note that since this will build LLVM, it will take a _lot_ of disk space (up to 100GB). -## Homebrew - -Alternatively, if you use macOS, you can install Halide via -[Homebrew](https://brew.sh/) like so: - -``` -$ brew install halide -``` - ## Other package managers -We are interested in bringing Halide to other popular package managers and -Linux distribution repositories including, but not limited to, Conan, -Debian, [Ubuntu (or PPA)](https://github.com/halide/Halide/issues/5285), -CentOS/Fedora, and Arch. If you have experience publishing packages we would be -happy to work with you! +We are interested in bringing Halide to other popular package managers and Linux +distribution repositories! We track the status of various distributions of +Halide [in this GitHub issue](https://github.com/halide/Halide/issues/4660). If +you have experience publishing packages we would be happy to work with you! -If you are a maintainer of any other package distribution platform, we would be -excited to work with you, too. +# Building Halide -# Platform Support +## Platform Support There are two sets of platform requirements relevant to Halide: those required to run the compiler library in either JIT or AOT mode, and those required to run @@ -81,13 +114,13 @@ the _binary outputs_ of the AOT compiler. These are the **tested** host toolchain and platform combinations for building and running the Halide compiler library. -| Compiler | Version | OS | Architectures | -|------------|--------------|------------------------|-----------------| -| GCC | 9.4 | Ubuntu Linux 20.04 LTS | x86, x64, ARM32 | -| GCC | 9.4 | Ubuntu Linux 18.04 LTS | ARM32, ARM64 | -| MSVC | 2019 (19.28) | Windows 10 (20H2) | x86, x64 | -| AppleClang | 14.0.3 | macOS 13.4 | x86_64 | -| AppleClang | 14.0.3 | macOS 13.4 | ARM64 | +| Compiler | Version | OS | Architectures | +|------------|--------------|------------------------|---------------| +| GCC | 9.5 | Ubuntu Linux 20.04 LTS | x86, x64 | +| GCC | 11.4 | Ubuntu Linux 22.04 LTS | ARM32, ARM64 | +| MSVC | 2022 (19.37) | Windows 11 (22631) | x86, x64 | +| AppleClang | 15.0.0 | macOS 14.4.1 | x64 | +| AppleClang | 14.0.0 | macOS 14.6 | ARM64 | Some users have successfully built Halide for Linux using Clang 9.0.0+, for Windows using ClangCL 11.0.0+, and for Windows ARM64 by cross-compiling with @@ -105,106 +138,93 @@ the generated headers correctly. The C++ bindings currently require C++17. If you discover a compatibility problem with a generated pipeline, please open an issue. -# Building Halide with Make - -### TL;DR - -Have llvm-16.0 (or greater) installed and run `make` in the root directory of -the repository (where this README is). - -### Acquiring LLVM +## Acquiring LLVM At any point in time, building Halide requires either the latest stable version -of LLVM, the previous stable version of LLVM, and trunk. At the time of writing, -this means versions 18, 17, and 16 are supported, but 15 is not. The commands -`llvm-config` and `clang` must be somewhere in the path. +of LLVM, the previous stable version of LLVM, or trunk. At the time of writing, +this means versions 19, 18, and 17 are supported, but 16 is not. -If your OS does not have packages for LLVM, you can find binaries for it at -http://llvm.org/releases/download.html. Download an appropriate package and then -either install it, or at least put the `bin` subdirectory in your path. (This -works well on OS X and Ubuntu.) +It is simplest to get a binary release of LLVM on macOS by using +[Homebrew](https://brew.sh). Just run `brew install llvm`. On Debian flavors of +Linux, the [LLVM APT repo](https://apt.llvm.org) is best; use the provided +installation script. We know of no suitable official binary releases for +Windows, however the ones we use in CI can usually be found at +https://buildbot.halide-lang.org, along with tarballs for our other tested +platforms. See [the section on Windows](#windows) below for further advice. -If you want to build it yourself, first check it out from GitHub: +If your OS does not have packages for LLVM, or you want more control over the +configuration, you can build it yourself. First check it out from GitHub: -``` -% git clone --depth 1 --branch llvmorg-18.1.8 https://github.com/llvm/llvm-project.git +```shell +$ git clone --depth 1 --branch llvmorg-18.1.8 https://github.com/llvm/llvm-project.git ``` -(LLVM 18.1.8 is the most recent released LLVM at the time of writing. For current trunk, -use `main` instead) +(LLVM 18.1.8 is the most recent released LLVM at the time of writing. For +current trunk, use `main` instead) Then build it like so: -``` -% cmake -DCMAKE_BUILD_TYPE=Release \ +```shell +$ cmake -G Ninja -S llvm-project/llvm -B build \ + -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lld;clang-tools-extra" \ - -DLLVM_TARGETS_TO_BUILD="X86;ARM;NVPTX;AArch64;Hexagon;WebAssembly;RISCV" \ - -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ASSERTIONS=ON \ - -DLLVM_ENABLE_EH=ON -DLLVM_ENABLE_RTTI=ON -DLLVM_BUILD_32_BITS=OFF \ - -DLLVM_ENABLE_RUNTIMES="compiler-rt" \ - -S llvm-project/llvm -B llvm-build -% cmake --build llvm-build -% cmake --install llvm-build --prefix llvm-install -``` - -Running a serial build will be slow. To improve speed, try running a parallel -build. That's done by default in Ninja; for make, use the option -j NNN, -where NNN is the number of parallel jobs, e.g. the number of CPUs you have. -Then, point Halide to it: - -``` -% export LLVM_ROOT=$PWD/llvm-install -% export LLVM_CONFIG=$LLVM_ROOT/bin/llvm-config -``` - -Note that you _must_ add `clang` to `LLVM_ENABLE_PROJECTS`; adding `lld` to -`LLVM_ENABLE_PROJECTS` is only required when using WebAssembly, -`LLVM_ENABLE_RUNTIMES="compiler-rt"` is only required if building the fuzz -tests, and adding `clang-tools-extra` is only necessary if you plan to -contribute code to Halide (so that you can run `clang-tidy` on your pull -requests). We recommend enabling both in all cases to simplify builds. You can -disable exception handling (EH) and RTTI if you don't want the Python bindings. - -### Building Halide with make - -With `LLVM_CONFIG` set (or `llvm-config` in your path), you should be able to -just run `make` in the root directory of the Halide source tree. -`make run_tests` will run the JIT test suite, and `make test_apps` will make -sure all the apps compile and run (but won't check their output). - -There is no `make install`. If you want to make an install package, use CMake. - -### Building Halide out-of-tree with make - -If you wish to build Halide in a separate directory, you can do that like so: - - % cd .. - % mkdir halide_build - % cd halide_build - % make -f ../Halide/Makefile - -# Building Halide with CMake + -DLLVM_ENABLE_RUNTIMES=compiler-rt \ + -DLLVM_TARGETS_TO_BUILD="WebAssembly;X86;AArch64;ARM;Hexagon;NVPTX;PowerPC;RISCV" \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DLLVM_ENABLE_EH=ON \ + -DLLVM_ENABLE_RTTI=ON \ + -DLLVM_ENABLE_HTTPLIB=OFF \ + -DLLVM_ENABLE_LIBEDIT=OFF \ + -DLLVM_ENABLE_LIBXML2=OFF \ + -DLLVM_ENABLE_TERMINFO=OFF \ + -DLLVM_ENABLE_ZLIB=OFF \ + -DLLVM_ENABLE_ZSTD=OFF \ + -DLLVM_BUILD_32_BITS=OFF +$ cmake --build build +$ cmake --install build --prefix llvm-install +``` + +This will produce a working LLVM installation in `$PWD/llvm-install`. We refer +to this path as `LLVM_ROOT` later. **Do not confuse this installation tree with +the build tree!** + +LLVM takes a long time to build, so the above command uses Ninja to maximize +parallelism. If you choose to omit `-G Ninja`, Makefiles will be generated +instead. In this case, enable parallelism with `cmake --build build -j NNN` +where `NNN` is the number of parallel jobs, i.e. the number of CPUs you have. + +Note that you _must_ add `clang` and `lld` to `LLVM_ENABLE_PROJECTS` and +`WebAssembly` and `X86` _must_ be included in `LLVM_TARGETS_TO_BUILD`. +`LLVM_ENABLE_RUNTIMES=compiler-rt` is only required to build the fuzz tests, and +`clang-tools-extra` is only necessary if you plan to contribute code to Halide +(so that you can run `clang-tidy` on your pull requests). You can disable +exception handling (EH) and RTTI if you don't want the Python bindings. We +recommend enabling the full set to simplify builds during development. + +## Building Halide with CMake + +This is discussed in greater detail in [BuildingHalideWithCMake.md]. CMake +version 3.28+ is required to build Halide. + +[BuildingHalideWithCMake.md]: doc/BuildingHalideWithCMake.md ### MacOS and Linux Follow the above instructions to build LLVM or acquire a suitable binary release. Then change directory to the Halide repository and run: -``` -% cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR=$LLVM_ROOT/lib/cmake/llvm -S . -B build -% cmake --build build +```shell +$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release -DHalide_LLVM_ROOT=$LLVM_ROOT +$ cmake --build build ``` -`LLVM_DIR` is the folder in the LLVM installation tree **(do not use the build -tree by mistake)** that contains `LLVMConfig.cmake`. It is not required to set -this variable if you have a suitable system-wide version installed. If you have -multiple system-wide versions installed, you can specify the version with -`Halide_REQUIRE_LLVM_VERSION`. Remove `-G Ninja` if you prefer to build with a -different generator. +Setting `-DHalide_LLVM_ROOT` is not required if you have a suitable system-wide +version installed. However, if you have multiple LLVMs installed, it can pick +between them. ### Windows -We suggest building with Visual Studio 2019. Your mileage may vary with earlier +We suggest building with Visual Studio 2022. Your mileage may vary with earlier versions. Be sure to install the "C++ CMake tools for Windows" in the Visual Studio installer. For older versions of Visual Studio, do not install the CMake tools, but instead acquire CMake and Ninja from their respective project @@ -215,13 +235,13 @@ to `D:\Halide`. We also assume that your shell environment is set up correctly. For a 64-bit build, run: ``` -D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 +D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 ``` For a 32-bit build, run: ``` -D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64_x86 +D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64_x86 ``` #### Managing dependencies with vcpkg @@ -232,23 +252,19 @@ The best way to get compatible dependencies on Windows is to use ``` D:\> git clone https://github.com/Microsoft/vcpkg.git D:\> cd vcpkg -D:\> .\bootstrap-vcpkg.bat -D:\vcpkg> .\vcpkg integrate install +D:\vcpkg> .\bootstrap-vcpkg.bat -disableMetrics ... CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake" ``` -Then install the libraries. For a 64-bit build, run: +When using the toolchain file, vcpkg will automatically build all the necessary +dependencies. However, as stated above, be aware that acquiring LLVM this way +may use over 100 GB of disk space for its build trees and take a very long time +to build. You can manually delete the build trees afterward, but vcpkg will not +do this automatically. -``` -D:\vcpkg> .\vcpkg install libpng:x64-windows libjpeg-turbo:x64-windows llvm[target-all,clang-tools-extra]:x64-windows -``` - -To support 32-bit builds, also run: - -``` -D:\vcpkg> .\vcpkg install libpng:x86-windows libjpeg-turbo:x86-windows llvm[target-all,clang-tools-extra]:x86-windows -``` +See [BuildingHalideWithCMake.md](./doc/BuildingHalideWithCMake.md#vcpkg-presets) +for directions to use Vcpkg for everything _except_ LLVM. #### Building Halide @@ -257,189 +273,157 @@ build in either 32-bit or 64-bit depending on the environment script (`vcvars`) that was run earlier. ``` -D:\Halide> cmake -G Ninja ^ - -DCMAKE_BUILD_TYPE=Release ^ - -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake ^ - -S . -B build +D:\Halide> cmake -G Ninja -S . -B build ^ + --toolchain D:/vcpkg/scripts/buildsystems/vcpkg.cmake ^ + -DCMAKE_BUILD_TYPE=Release ``` -**Note:** If building with Python bindings on 32-bit (enabled by default), be -sure to point CMake to the installation path of a 32-bit Python 3. You can do -this by specifying, for example: -`"-DPython3_ROOT_DIR=C:\Program Files (x86)\Python38-32"`. - Then run the build with: ``` -D:\Halide> cmake --build build --config Release +D:\Halide> cmake --build build ``` To run all the tests: ``` -D:\Halide> cd build -D:\Halide\build> ctest -C Release +D:\Halide> ctest --test-dir build --output-on-failure ``` Subsets of the tests can be selected with `-L` and include `correctness`, -`python`, `error`, and the other directory names under `/tests`. +`generator`, `error`, and the other directory names under `tests/`. #### Building LLVM (optional) Follow these steps if you want to build LLVM yourself. First, download LLVM's -sources (these instructions use the latest 17.0 release) +sources (these instructions use the 18.1.8 release). ``` -D:\> git clone --depth 1 --branch release/17.x https://github.com/llvm/llvm-project.git +D:\> git clone --depth 1 --branch llvm-org-18.1.8 https://github.com/llvm/llvm-project.git ``` -For a 64-bit build, run: +As above, run `vcvarsall.bat` to pick between x86 and x64. Then configure LLVM +with the following command (for 32-bit, set `-DLLVM_BUILD_32_BITS=ON` instead): ``` -D:\> cmake -G Ninja ^ +D:\> cmake -G Ninja -S llvm-project\llvm -B build ^ -DCMAKE_BUILD_TYPE=Release ^ -DLLVM_ENABLE_PROJECTS=clang;lld;clang-tools-extra ^ - -DLLVM_ENABLE_TERMINFO=OFF ^ - -DLLVM_TARGETS_TO_BUILD=X86;ARM;NVPTX;AArch64;Hexagon;RISCV ^ + -DLLVM_ENABLE_RUNTIMES=compiler-rt ^ + -DLLVM_TARGETS_TO_BUILD=WebAssembly;X86;AArch64;ARM;Hexagon;NVPTX;PowerPC;RISCV ^ -DLLVM_ENABLE_ASSERTIONS=ON ^ -DLLVM_ENABLE_EH=ON ^ -DLLVM_ENABLE_RTTI=ON ^ - -DLLVM_BUILD_32_BITS=OFF ^ - -S llvm-project\llvm -B llvm-build -``` - -For a 32-bit build, run: - -``` -D:\> cmake -G Ninja ^ - -DCMAKE_BUILD_TYPE=Release ^ - -DLLVM_ENABLE_PROJECTS=clang;lld;clang-tools-extra ^ + -DLLVM_ENABLE_HTTPLIB=OFF ^ + -DLLVM_ENABLE_LIBEDIT=OFF ^ + -DLLVM_ENABLE_LIBXML2=OFF ^ -DLLVM_ENABLE_TERMINFO=OFF ^ - -DLLVM_TARGETS_TO_BUILD=X86;ARM;NVPTX;AArch64;Hexagon;RISCV ^ - -DLLVM_ENABLE_ASSERTIONS=ON ^ - -DLLVM_ENABLE_EH=ON ^ - -DLLVM_ENABLE_RTTI=ON ^ - -DLLVM_BUILD_32_BITS=ON ^ - -S llvm-project\llvm -B llvm32-build + -DLLVM_ENABLE_ZLIB=OFF ^ + -DLLVM_ENABLE_ZSTD=OFF ^ + -DLLVM_BUILD_32_BITS=OFF ``` -Finally, run: +**MSBuild:** If you want to build LLVM with MSBuild instead of Ninja, use +`-G "Visual Studio 17 2022" -Thost=x64 -A x64` or +`-G "Visual Studio 17 2022" -Thost=x64 -A Win32` in place of `-G Ninja`. + +Finally, run the build and install to a local directory: ``` -D:\> cmake --build llvm-build --config Release -D:\> cmake --install llvm-build --prefix llvm-install +D:\> cmake --build build --config Release +D:\> cmake --install build --prefix llvm-install ``` You can substitute `Debug` for `Release` in the above `cmake` commands if you -want a debug build. Make sure to add `-DLLVM_DIR=D:/llvm-install/lib/cmake/llvm` -to the Halide CMake command to override `vcpkg`'s LLVM. +want a debug build. -**MSBuild:** If you want to build LLVM with MSBuild instead of Ninja, use -`-G "Visual Studio 16 2019" -Thost=x64 -A x64` or -`-G "Visual Studio 16 2019" -Thost=x64 -A Win32` in place of `-G Ninja`. +To use this with Halide, but still allow vcpkg to manage other dependencies, you +must add two flags to Halide's CMake configure command line. First, disable LLVM +with `-DVCPKG_OVERLAY_PORTS=cmake/vcpkg`. Second, point CMake to our newly built +Halide with `-DHalide_LLVM_ROOT=D:/llvm-install`. #### If all else fails... -Do what the build-bots do: https://buildbot.halide-lang.org/master/#/builders +Do what the buildbots do: https://buildbot.halide-lang.org/master/#/builders -If the column that best matches your system is red, then maybe things aren't -just broken for you. If it's green, then you can click the "stdio" links in the -latest build to see what commands the build bots run, and what the output was. +If the row that best matches your system is red, then maybe things aren't just +broken for you. If it's green, then you can click through to the latest build +and see the commands that the build bots run. Open a step ("Configure Halide" is +useful) and look at the "stdio" logs in the viewer. These logs contain the full +commands that were run, as well as the environment variables they were run with. -# Some useful environment variables - -`HL_TARGET=...` will set Halide's AOT compilation target. - -`HL_JIT_TARGET=...` will set Halide's JIT compilation target. +## Building Halide with make -`HL_DEBUG_CODEGEN=1` will print out pseudocode for what Halide is compiling. -Higher numbers will print more detail. +> [!WARNING] +> We do not provide support for the Makefile. Feel free to use it, but if +> anything goes wrong, switch to the CMake build. Note also that the Makefile +> cannot build the Python bindings or produce install packages. -`HL_NUM_THREADS=...` specifies the number of threads to create for the thread -pool. When the async scheduling directive is used, more threads than this number -may be required and thus allocated. A maximum of 256 threads is allowed. (By -default, the number of cores on the host is used.) +*TL;DR*: Have LLVM 17 (or greater) installed and run `make` in the root +directory of the repository (where this README is). -`HL_TRACE_FILE=...` specifies a binary target file to dump tracing data into -(ignored unless at least one `trace_` feature is enabled in `HL_TARGET` or -`HL_JIT_TARGET`). The output can be parsed programmatically by starting from the -code in `utils/HalideTraceViz.cpp`. +By default, `make` will use the `llvm-config` tool found in the `PATH`. If you +want to use a different LLVM, such as a custom-built one following the +instructions above, set the following environment variable: -# Using Halide on OSX - -Precompiled Halide distributions are built using XCode's command-line tools with -Apple clang 500.2.76. This means that we link against libc++ instead of -libstdc++. You may need to adjust compiler options accordingly if you're using -an older XCode which does not default to libc++. - -# Halide for Hexagon HVX - -Halide supports offloading work to Qualcomm Hexagon DSP on Qualcomm Snapdragon -845/710 devices or newer. The Hexagon DSP provides a set of 128 byte vector -instruction extensions - the Hexagon Vector eXtensions (HVX). HVX is well suited -for image processing, and Halide for Hexagon HVX will generate the appropriate -HVX vector instructions from a program authored in Halide. - -Halide can be used to compile Hexagon object files directly, by using a target -such as `hexagon-32-qurt-hvx`. - -Halide can also be used to offload parts of a pipeline to Hexagon using the -`hexagon` scheduling directive. To enable the `hexagon` scheduling directive, -include the `hvx` target feature in your target. The currently supported -combination of targets is to use the HVX target features with an x86 linux -host (to use the simulator) or with an ARM android target (to use Hexagon DSP -hardware). For examples of using the `hexagon` scheduling directive on both the -simulator and a Hexagon DSP, see the blur example app. - -To build and run an example app using the Hexagon target, - -1. Obtain and build trunk LLVM and Clang. (Earlier versions of LLVM may work but - are not actively tested and thus not recommended.) -2. Download and install the Hexagon SDK and Hexagon Tools. Hexagon SDK 4.3.0 or - later is needed. Hexagon Tools 8.4 or later is needed. -3. Build and run an example for Hexagon HVX - -### 1. Obtain and build trunk LLVM and Clang - -(Follow the instructions given previously, just be sure to check out the `main` -branch.) - -### 2. Download and install the Hexagon SDK and Hexagon Tools - -Go to https://qpm.qualcomm.com/#/main/home +```shell +$ export LLVM_CONFIG="$LLVM_ROOT/bin/llvm-config" +``` -1. Go to Tools, and download Qualcomm Package Manager 3. Install the package manager on your machine. -2. Run the installed Qualcomm Package Manager and install the Qualcomm Hexagon SDK 5.x (or 4.x). - The SDK can be selected from the Qualcomm Hexagon SDK Products. -3. Set an environment variable to point to the SDK installation location - ``` - export SDK_LOC=/location/of/SDK - ``` +Now you should be able to just run `make` in the root directory of the Halide +source tree. `make run_tests` will run the JIT test suite, and `make test_apps` +will make sure all the apps compile and run (but won't check their output). -### 3. Build and run an example for Hexagon HVX +When building the tests, you can set the AOT compilation target with the +`HL_TARGET` environment variable. -In addition to running Hexagon code on device, Halide also supports running -Hexagon code on the simulator from the Hexagon tools. +### Building Halide out-of-tree with make -To build and run the blur example in Halide/apps/blur on the simulator: +If you wish to build Halide in a separate directory, you can do that like so: -``` -cd apps/blur -export HL_HEXAGON_SIM_REMOTE=../../src/runtime/hexagon_remote/bin/v65/hexagon_sim_remote -export HL_HEXAGON_TOOLS=$SDK_LOC/Hexagon_Tools/8.x/Tools/ -LD_LIBRARY_PATH=../../src/runtime/hexagon_remote/bin/host/:$HL_HEXAGON_TOOLS/lib/iss/:. HL_TARGET=host-hvx make test +```shell +$ cd .. +$ mkdir halide_build +$ cd halide_build +$ make -f ../Halide/Makefile ``` -### To build and run the blur example in Halide/apps/blur on Android: +# Some useful environment variables -To build the example for Android, first ensure that you have Android NDK r19b or -later installed, and the ANDROID_NDK_ROOT environment variable points to it. -(Note that Qualcomm Hexagon SDK v4.3.0 includes Android NDK r19c, which is -fine.) +`HL_JIT_TARGET=...` will set Halide's JIT compilation target. -Now build and run the blur example using the script to run it on device: +`HL_DEBUG_CODEGEN=1` will print out pseudocode for what Halide is compiling. +Higher numbers will print more detail. -``` -export HL_HEXAGON_TOOLS=$SDK_LOC/HEXAGON_Tools/8.4.11/Tools/ -HL_TARGET=arm-64-android-hvx ./adb_run_on_device.sh -``` +`HL_NUM_THREADS=...` specifies the number of threads to create for the thread +pool. When the async scheduling directive is used, more threads than this number +may be required and thus allocated. A maximum of 256 threads is allowed. (By +default, the number of cores on the host is used.) + +`HL_TRACE_FILE=...` specifies a binary target file to dump tracing data into +(ignored unless at least one `trace_` feature is enabled in the target). The +output can be parsed programmatically by starting from the code in +`utils/HalideTraceViz.cpp`. + +# Further references + +We have more documentation in `doc/`, the following links might be helpful: + +| Document | Description | +|-----------------------------------------------|---------------------------------------------------------------------------| +| [CMake build](doc/BuildingHalideWithCMake.md) | How to configure and build Halide using CMake. | +| [CMake package](doc/HalideCMakePackage.md) | How to use the Halide CMake package to build your code. | +| [Hexagon](doc/Hexagon.md) | How to use the Hexagon backend. | +| [Python](doc/Python.md) | Documentation for the Python bindings. | +| [RunGen](doc/RunGen.md) | How to use the RunGen interface to run and benchmark arbitrary pipelines. | +| [Vulkan](doc/Vulkan.md) | How to use the Halide Vulkan backend (BETA) | +| [WebAssembly](doc/WebAssembly.md) | How to use the WebAssembly backend and how to use V8 in place of wabt. | +| [WebGPU](doc/WebGPU.md) | How to run WebGPU pipelines (BETA) | + +The following links are of greater interest to developers wishing to contribute +code to Halide: + +| Document | Description | +|------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| [CMake developer](doc/CodeStyleCMake.md) | Guidelines for authoring new CMake code. | +| [FuzzTesting](doc/FuzzTesting.md) | Information about fuzz testing the Halide compiler (rather than pipelines). Intended for internal developers. | diff --git a/README_cmake.md b/README_cmake.md deleted file mode 100644 index d58fb18b767a..000000000000 --- a/README_cmake.md +++ /dev/null @@ -1,1411 +0,0 @@ -# Halide and CMake - -This is a comprehensive guide to the three main usage stories of the Halide -CMake build. - -1. Compiling or packaging Halide from source. -2. Building Halide programs using the official CMake package. -3. Contributing to Halide and updating the build files. - -The following sections cover each in detail. - -## Table of Contents - -- [Halide and CMake](#halide-and-cmake) - - [Table of Contents](#table-of-contents) -- [Getting started](#getting-started) - - [Installing CMake](#installing-cmake) - - [Cross-platform](#cross-platform) - - [Windows](#windows) - - [macOS](#macos) - - [Ubuntu Linux](#ubuntu-linux) - - [Installing dependencies](#installing-dependencies) - - [Windows](#windows-1) - - [macOS](#macos-1) - - [Ubuntu](#ubuntu) -- [Building Halide with CMake](#building-halide-with-cmake) - - [Basic build](#basic-build) - - [Windows](#windows-2) - - [macOS and Linux](#macos-and-linux) - - [CMake Presets](#cmake-presets) - - [Installing](#installing) - - [Build options](#build-options) - - [Find module options](#find-module-options) -- [Using Halide from your CMake build](#using-halide-from-your-cmake-build) - - [A basic CMake project](#a-basic-cmake-project) - - [JIT mode](#jit-mode) - - [AOT mode](#aot-mode) - - [Autoschedulers](#autoschedulers) - - [RunGenMain](#rungenmain) - - [Halide package documentation](#halide-package-documentation) - - [Components](#components) - - [Variables](#variables) - - [Imported targets](#imported-targets) - - [Functions](#functions) - - [`add_halide_library`](#add_halide_library) - - [`add_halide_generator`](#add_halide_generator) - - [`add_halide_python_extension_library`](#add_halide_python_extension_library) - - [`add_halide_runtime`](#add_halide_runtime) - - [Cross compiling](#cross-compiling) - - [Use `add_halide_generator`](#use-add_halide_generator) - - [Use a super-build](#use-a-super-build) - - [Use `ExternalProject` directly](#use-externalproject-directly) - - [Use an emulator or run on device](#use-an-emulator-or-run-on-device) - - [Bypass CMake](#bypass-cmake) -- [Contributing CMake code to Halide](#contributing-cmake-code-to-halide) - - [General guidelines and best practices](#general-guidelines-and-best-practices) - - [Prohibited commands list](#prohibited-commands-list) - - [Prohibited variables list](#prohibited-variables-list) - - [Adding tests](#adding-tests) - - [Adding apps](#adding-apps) - -# Getting started - -This section covers installing a recent version of CMake and the correct -dependencies for building and using Halide. If you have not used CMake before, -we strongly suggest reading through the [CMake documentation][cmake-docs] first. - -## Installing CMake - -Halide requires at least version 3.22, which was released in November 2021. -Fortunately, getting a recent version of CMake couldn't be easier, and there are -multiple good options on any system to do so. Generally, one should always have -the most recent version of CMake installed system-wide. CMake is committed to -backwards compatibility and even the most recent release can build projects over -a decade old. - -### Cross-platform - -The Python package manager `pip3` has the newest version of CMake at all times. -This might be the most convenient method since Python 3 is an optional -dependency for Halide, anyway. - -``` -$ pip3 install --upgrade cmake -``` - -See the [PyPI website][pypi-cmake] for more details. - -### Windows - -On Windows, there are three primary methods for installing an up-to-date CMake: - -1. If you have Visual Studio 2019 installed, you can get CMake 3.17 through the - Visual Studio installer. This is the recommended way of getting CMake if you - are able to use Visual Studio 2019. See Microsoft's - [documentation][vs2019-cmake-docs] for more details. -2. If you use [Chocolatey][chocolatey], its [CMake package][choco-cmake] is kept - up to date. It should be as simple as `choco install cmake`. -3. Otherwise, you should install CMake from [Kitware's website][cmake-download]. - -### macOS - -On macOS, the [Homebrew][homebrew] [CMake package][brew-cmake] is kept up to -date. Simply run: - -``` -$ brew update -$ brew install cmake -``` - -to install the newest version of CMake. If your environment prevents you from -installing Homebrew, the binary release on [Kitware's website][cmake-download] -is also a viable option. - -### Ubuntu Linux - -There are a few good ways to install a modern CMake on Ubuntu: - -1. If you're on Ubuntu Linux 22.04 (Jammy Jellyfish), then simply running - `sudo apt install cmake` will get you CMake 3.22. -2. If you are on an older Ubuntu release or would like to use the newest CMake, - try installing via the snap store: `snap install cmake`. Be sure you do not - already have `cmake` installed via APT. The snap package automatically stays - up to date. -3. For older versions of Debian, Ubuntu, Mint, and derivatives, Kitware provides - an [APT repository][cmake-apt] with up-to-date releases. Note that this is - still useful for Ubuntu 20.04 because it will remain up to date. -4. If all else fails, you might need to build CMake from source (eg. on old - Ubuntu versions running on ARM). In that case, follow the directions posted - on [Kitware's website][cmake-from-source]. - -For other Linux distributions, check with your distribution's package manager or -use pip as detailed above. Snap packages might also be available. - -**Note:** On WSL 1, the snap service is not available; in this case, prefer to -use the APT repository. On WSL 2, all methods are available. - -## Installing dependencies - -We generally recommend using a package manager to fetch Halide's dependencies. -Except where noted, we recommend using [vcpkg][vcpkg] on Windows, -[Homebrew][homebrew] on macOS, and APT on Ubuntu 20.04 LTS. - -Only LLVM and Clang are _absolutely_ required to build Halide. Halide always -supports three LLVM versions: the current major version, the previous major -version, and trunk. The LLVM and Clang versions must match exactly. For most -users, we recommend using a binary release of LLVM rather than building it -yourself. - -However, to run all of the tests and apps, an extended set is needed. This -includes [lld][lld], [Python 3][python], [libpng][libpng], [libjpeg][libjpeg], -[Doxygen][doxygen], [OpenBLAS][openblas], [ATLAS][atlas], and [Eigen3][eigen]. -While not required to build any part of Halide, we find that [Ninja][ninja] is -the best backend build tool across all platforms. - -Note that CMake has many special variables for overriding the locations of -packages and executables. A partial list can be found in the -["find module options"](#find-module-options) section below, and more can be -found in the documentation for the CMake [find_package][find_package] command. -Normally, you should prefer to make sure your environment is set up so that -CMake can find dependencies automatically. For instance, if you want CMake to -use a particular version of Python, create a [virtual environment][venv] and -activate it _before_ configuring Halide. - -### Windows - -We assume you have vcpkg installed at `D:\vcpkg`. Follow the instructions in the -[vcpkg README][vcpkg] to install. Start by installing LLVM. - -``` -D:\vcpkg> .\vcpkg install llvm[target-all,enable-assertions,clang-tools-extra]:x64-windows -D:\vcpkg> .\vcpkg install llvm[target-all,enable-assertions,clang-tools-extra]:x86-windows -``` - -This will also install Clang and LLD. The `enable-assertions` option is not -strictly necessary but will make debugging during development much smoother. -These builds will take a long time and a lot of disk space. After they are -built, it is safe to delete the intermediate build files and caches in -`D:\vcpkg\buildtrees` and `%APPDATA%\local\vcpkg`. - -Then install the other libraries: - -``` -D:\vcpkg> .\vcpkg install libpng:x64-windows libjpeg-turbo:x64-windows openblas:x64-windows eigen3:x64-windows -D:\vcpkg> .\vcpkg install libpng:x86-windows libjpeg-turbo:x86-windows openblas:x86-windows eigen3:x86-windows -``` - -To build the documentation, you will need to install [Doxygen][doxygen]. This -can be done either through [Chocolatey][choco-doxygen] or from the [Doxygen -website][doxygen-download]. - -``` -> choco install doxygen -``` - -To build the Python bindings, you will need to install Python 3. This should be -done by running the official installer from the [Python website][python]. Be -sure to download the debugging symbols through the installer. This will require -using the "Advanced Installation" workflow. Although it is not strictly -necessary, it is convenient to install Python system-wide on Windows (ie. -`C:\Program Files`). This makes it easy for CMake to find without needing to -manually set the `PATH`. - -Once Python is installed, you can install the Python module dependencies either -globally or in a [virtual environment][venv] by running - -``` -> pip3 install -r requirements.txt -``` - -from the root of the repository. - -If you would like to use [Ninja][ninja], note that it is installed alongside -CMake when using the Visual Studio 2019 installer. Alternatively, you can -install via [Chocolatey][choco-ninja] or place the [pre-built -binary][ninja-download] from their website in the PATH. - -``` -> choco install ninja -``` - -### macOS - -On macOS, it is possible to install all dependencies via [Homebrew][homebrew]: - -``` -$ brew install llvm libpng libjpeg python@3.8 openblas doxygen ninja -``` - -The `llvm` package includes `clang`, `clang-format`, and `lld`, too. Don't -forget to install the Python module dependencies: - -``` -$ pip3 install -r python_bindings/requirements.txt -``` - -### Ubuntu - -Finally, on Ubuntu 20.04 LTS, you should install the following packages (this -includes the Python module dependencies): - -``` -dev@ubuntu:~$ sudo apt install \ - clang-tools lld llvm-dev libclang-dev liblld-10-dev \ - libpng-dev libjpeg-dev libgl-dev \ - python3-dev python3-numpy python3-scipy python3-imageio python3-pybind11 \ - libopenblas-dev libeigen3-dev libatlas-base-dev \ - doxygen ninja-build -``` - -# Building Halide with CMake - -## Basic build - -These instructions assume that your working directory is the Halide repo root. - -### Windows - -If you plan to use the Ninja generator, be sure to be in the developer command -prompt corresponding to your intended environment. Note that whatever your -intended target system (x86, x64, or arm), you must use the 64-bit _host tools_ -because the 32-bit tools run out of memory during the linking step with LLVM. -More information is available from [Microsoft's documentation][msvc-cmd]. - -You should either open the correct Developer Command Prompt directly or run the -[`vcvarsall.bat`][vcvarsall] script with the correct argument, ie. one of the -following: - -``` -D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 -D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64_x86 -D:\> "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64_arm -``` - -Then, assuming that vcpkg is installed to `D:\vcpkg`, simply run: - -``` -> cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=D:\vcpkg\scripts\buildsystems\vcpkg.cmake -S . -B build -> cmake --build .\build -``` - -Valid values of [`CMAKE_BUILD_TYPE`][cmake_build_type] are `Debug`, -`RelWithDebInfo`, `MinSizeRel`, and `Release`. When using a single-configuration -generator (like Ninja) you must specify a build type when configuring Halide (or -any other CMake project). - -Otherwise, if you wish to create a Visual Studio based build system, you can -configure with: - -``` -> cmake -G "Visual Studio 16 2019" -Thost=x64 -A x64 ^ - -DCMAKE_TOOLCHAIN_FILE=D:\vcpkg\scripts\buildsystems\vcpkg.cmake ^ - -S . -B build -> cmake --build .\build --config Release -j %NUMBER_OF_PROCESSORS% -``` - -Because the Visual Studio generator is a _multi-config generator_, you don't set -`CMAKE_BUILD_TYPE` at configure-time, but instead pass the configuration to the -build (and test/install) commands with the `--config` flag. More documentation -is available in the [CMake User Interaction Guide][cmake-user-interaction]. - -The process is similar for 32-bit: - -``` -> cmake -G "Visual Studio 16 2019" -Thost=x64 -A Win32 ^ - -DCMAKE_TOOLCHAIN_FILE=D:\vcpkg\scripts\buildsystems\vcpkg.cmake ^ - -S . -B build -> cmake --build .\build --config Release -j %NUMBER_OF_PROCESSORS% -``` - -In both cases, the `-Thost=x64` flag ensures that the correct host tools are -used. - -**Note:** due to limitations in MSBuild, incremental builds using the VS -generators will not detect changes to headers in the `src/runtime` folder. We -recommend using Ninja for day-to-day development and use Visual Studio only if -you need it for packaging. - -### macOS and Linux - -The instructions here are straightforward. Assuming your environment is set up -correctly, just run: - -``` -dev@host:~/Halide$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S . -B build -dev@host:~/Halide$ cmake --build ./build -``` - -If you omit `-G Ninja`, a Makefile-based generator will likely be used instead. -In either case, [`CMAKE_BUILD_TYPE`][cmake_build_type] must be set to one of the -standard types: `Debug`, `RelWithDebInfo`, `MinSizeRel`, or `Release`. - -### CMake Presets - -If you are using CMake 3.21+, we provide several [presets][cmake_presets] to -make the above commands more convenient. The following CMake preset commands -correspond to the longer ones above. - -``` -> cmake --preset=win64 # VS 2019 generator, 64-bit build, vcpkg deps -> cmake --preset=win32 # VS 2019 generator, 32-bit build, vcpkg deps -> cmake --preset=release # Release mode, any single-config generator / compiler - -$ cmake --list-presets # Get full list of presets. -``` - -The Windows presets assume that the environment variable `VCPKG_ROOT` is set and -points to the root of the vcpkg installation. - -There are also presets to use some Clang sanitizers with the CMake build; -at present, only Fuzzer and ASAN (Address Sanitizer) are supported, and -only on linux-x86-64. To use these, you must build LLVM with additional options: - -``` - -D LLVM_ENABLE_PROJECTS="clang;lld;clang-tools-extra" - -D LLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" -``` - -To build / test with ASAN, use `--preset linux-x64-asan`. - -To build / test with the Fuzzer, use `--preset linux-x64-fuzzer`. - -## Installing - -Once built, Halide will need to be installed somewhere before using it in a -separate project. On any platform, this means running the -[`cmake --install`][cmake-install] command in one of two ways. For a -single-configuration generator (like Ninja), run either: - -``` -dev@host:~/Halide$ cmake --install ./build --prefix /path/to/Halide-install -> cmake --install .\build --prefix X:\path\to\Halide-install -``` - -For a multi-configuration generator (like Visual Studio) run: - -``` -dev@host:~/Halide$ cmake --install ./build --prefix /path/to/Halide-install --config Release -> cmake --install .\build --prefix X:\path\to\Halide-install --config Release -``` - -Of course, make sure that you build the corresponding config before attempting -to install it. - -## Build options - -Halide reads and understands several options that can configure the build. The -following are the most consequential and control how Halide is actually -compiled. - -| Option | Default | Description | -|------------------------------------------|-----------------------|---------------------------------------------------------------------------------------------------| -| [`BUILD_SHARED_LIBS`][build_shared_libs] | `ON` | Standard CMake variable that chooses whether to build as a static or shared library. | -| `Halide_BUNDLE_STATIC` | `OFF` | When building Halide as a static library, merge static library dependencies into libHalide.a. | -| `Halide_LLVM_SHARED_LIBS` | `OFF` | Link to the shared version of LLVM. Not available on Windows. | -| `Halide_ENABLE_RTTI` | _inherited from LLVM_ | Enable RTTI when building Halide. Recommended to be set to `ON` | -| `Halide_ENABLE_EXCEPTIONS` | `ON` | Enable exceptions when building Halide | -| `Halide_TARGET` | _empty_ | The default target triple to use for `add_halide_library` (and the generator tests, by extension) | - -The following options are _advanced_ and should not be required in typical workflows. Generally, these are used by -Halide's own CI infrastructure, or as escape hatches for third-party packagers. - -| Option | Default | Description | -|-----------------------------|--------------------------------------------------------------------|------------------------------------------------------------------------------------------| -| `Halide_CLANG_TIDY_BUILD` | `OFF` | Used internally to generate fake compile jobs for runtime files when running clang-tidy. | -| `Halide_CCACHE_BUILD` | `OFF` | Use ccache with Halide-recommended settings to accelerate rebuilds. | -| `Halide_CCACHE_PARAMS` | `CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines` | Options to pass to `ccache` when using `Halide_CCACHE_BUILD`. | -| `Halide_SOVERSION_OVERRIDE` | `${Halide_VERSION_MAJOR}` | Override the SOVERSION for libHalide. Expects a positive integer (i.e. not a version). | - -The following options are only available when building Halide directly, ie. not -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` | 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`: - -| Option | Default | Description | -|---------------------------|---------|-----------------------------------| -| `WITH_TEST_AUTO_SCHEDULE` | `ON` | enable the auto-scheduling tests | -| `WITH_TEST_CORRECTNESS` | `ON` | enable the correctness tests | -| `WITH_TEST_ERROR` | `ON` | enable the expected-error tests | -| `WITH_TEST_WARNING` | `ON` | enable the expected-warning tests | -| `WITH_TEST_PERFORMANCE` | `ON` | enable performance testing | -| `WITH_TEST_GENERATOR` | `ON` | enable the AOT generator tests | - -The following options are WebAssembly-specific. They only apply when -`TARGET_WEBASSEMBLY=ON`: - -| Option | Default | Description | -|-----------------------|---------|------------------------------------------------------------------------------------------| -| `Halide_WASM_BACKEND` | `wabt` | Select the backend for WASM testing. Can be `wabt`, `V8` or a false value such as `OFF`. | - -### Find module options - -Halide uses the following find modules to search for certain dependencies. These -modules accept certain variables containing hints for the search process. Before -setting any of these variables, closely study the [`find_package`][find_package] -documentation. - -All of these variables should be set at the CMake command line via the `-D` -flag. - -First, Halide expects to find LLVM and Clang through the `CONFIG` mode of -`find_package`. You can tell Halide where to find these dependencies by setting -the corresponding `_DIR` variables: - -| Variable | Description | -|-------------|------------------------------------------------| -| `LLVM_DIR` | `$LLVM_ROOT/lib/cmake/LLVM/LLVMConfig.cmake` | -| `Clang_DIR` | `$LLVM_ROOT/lib/cmake/Clang/ClangConfig.cmake` | - -Here, `$LLVM_ROOT` is assumed to point to the root of an LLVM installation tree. -This is either a system path or one produced by running `cmake --install` (as -detailed in the main README.md). When building LLVM (and any other `CONFIG` -packages) manually, it is a common mistake to point CMake to a _build tree_ -rather than an _install tree_. Doing so often produces inscrutable errors. - -When using CMake 3.18 or above, some of Halide's tests will search for CUDA -using the [`FindCUDAToolkit`][findcudatoolkit] module. If it doesn't find your -CUDA installation automatically, you can point it to it by setting: - -| Variable | Description | -|--------------------|---------------------------------------------------| -| `CUDAToolkit_ROOT` | Path to the directory containing `bin/nvcc[.exe]` | -| `CUDA_PATH` | _Environment_ variable, same as above. | - -If the CMake version is lower than 3.18, the deprecated [`FindCUDA`][findcuda] -module will be used instead. It reads the variable `CUDA_TOOLKIT_ROOT_DIR` -instead of `CUDAToolkit_ROOT` above. - -Halide also searches for `libpng` and `libjpeg-turbo` through the -[`FindPNG`][findpng] and [`FindJPEG`][findjpeg] modules, respectively. They can -be overridden by setting the following variables. - -| Variable | Description | -|---------------------|----------------------------------------------------| -| `PNG_LIBRARIES` | Paths to the libraries to link against to use PNG. | -| `PNG_INCLUDE_DIRS` | Path to `png.h`, etc. | -| `JPEG_LIBRARIES` | Paths to the libraries needed to use JPEG. | -| `JPEG_INCLUDE_DIRS` | Paths to `jpeglib.h`, etc. | - -When `WITH_DOCS` is set to `ON`, Halide searches for Doxygen using the -[`FindDoxygen`][finddoxygen] module. It can be overridden by setting the -following variable. - -| Variable | Description | -|----------------------|---------------------------------| -| `DOXYGEN_EXECUTABLE` | Path to the Doxygen executable. | - -When compiling for an OpenCL target, Halide uses the [`FindOpenCL`][findopencl] -target to locate the libraries and include paths. These can be overridden by -setting the following variables: - -| Variable | Description | -|-----------------------|-------------------------------------------------------| -| `OpenCL_LIBRARIES` | Paths to the libraries to link against to use OpenCL. | -| `OpenCL_INCLUDE_DIRS` | Include directories for OpenCL. | - -Lastly, Halide searches for Python 3 using the [`FindPython3`][findpython3] -module, _not_ the deprecated `FindPythonInterp` and `FindPythonLibs` modules, -like other projects you might have encountered. You can select which Python -installation to use by setting the following variable. - -| Variable | Description | -|--------------------|-------------------------------------------------------| -| `Python3_ROOT_DIR` | Define the root directory of a Python 3 installation. | - -# Using Halide from your CMake build - -This section assumes some basic familiarity with CMake but tries to be explicit -in all its examples. To learn more about CMake, consult the -[documentation][cmake-docs] and engage with the community on the [CMake -Discourse][cmake-discourse]. - -Note: previous releases bundled a `halide.cmake` module that was meant to be -[`include()`][include]-ed into your project. This has been removed. Please -upgrade to the new package config module. - -## A basic CMake project - -There are two main ways to use Halide in your application: as a **JIT compiler** -for dynamic pipelines or an **ahead-of-time (AOT) compiler** for static -pipelines. CMake provides robust support for both use cases. - -No matter how you intend to use Halide, you will need some basic CMake -boilerplate. - -```cmake -cmake_minimum_required(VERSION 3.28) -project(HalideExample) - -set(CMAKE_CXX_STANDARD 17) # or newer -set(CMAKE_CXX_STANDARD_REQUIRED YES) -set(CMAKE_CXX_EXTENSIONS NO) - -find_package(Halide REQUIRED) -``` - -The [`cmake_minimum_required`][cmake_minimum_required] command is required to be -the first command executed in a CMake program. It disables all of the deprecated -behavior ("policies" in CMake lingo) from earlier versions. The -[`project`][project] command sets the name of the project (and has arguments for -versioning, language support, etc.) and is required by CMake to be called -immediately after setting the minimum version. - -The next three variables set the project-wide C++ standard. The first, -[`CMAKE_CXX_STANDARD`][cmake_cxx_standard], simply sets the standard version. -Halide requires at least C++17. The second, -[`CMAKE_CXX_STANDARD_REQUIRED`][cmake_cxx_standard_required], tells CMake to -fail if the compiler cannot provide the requested standard version. Lastly, -[`CMAKE_CXX_EXTENSIONS`][cmake_cxx_extensions] tells CMake to disable -vendor-specific extensions to C++. This is not necessary to simply use Halide, -but we require it when authoring new code in the Halide repo. - -Finally, we use [`find_package`][find_package] to locate Halide on your system. -If Halide is not globally installed, you will need to add the root of the Halide -installation directory to [`CMAKE_PREFIX_PATH`][cmake_prefix_path] at the CMake -command line. - -```console -dev@ubuntu:~/myproj$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="/path/to/Halide-install" -S . -B build -``` - -## JIT mode - -To use Halide in JIT mode (like the [tutorials][halide-tutorials] do, for -example), you can simply link to `Halide::Halide`. - -```cmake -# ... same project setup as before ... -add_executable(my_halide_app main.cpp) -target_link_libraries(my_halide_app PRIVATE Halide::Halide) -``` - -Then `Halide.h` will be available to your code and everything should just work. -That's it! - -## AOT mode - -Using Halide in AOT mode is more complicated so we'll walk through it step by -step. Note that this only applies to Halide generators, so it might be useful to -re-read the [tutorial][halide-generator-tutorial] on generators. Assume (like in -the tutorial) that you have a source file named `my_generators.cpp` and that in -it you have generator classes `MyFirstGenerator` and `MySecondGenerator` with -registered names `my_first_generator` and `my_second_generator` respectively. - -Then the first step is to add a **generator executable** to your build: - -```cmake -# ... same project setup as before ... -add_executable(my_generators my_generators.cpp) -target_link_libraries(my_generators PRIVATE Halide::Generator) -``` - -Using the generator executable, we can add a Halide library corresponding to -`MyFirstGenerator`. - -```cmake -# ... continuing from above -add_halide_library(my_first_generator FROM my_generators) -``` - -This will create a static library target in CMake that corresponds to the output -of running your generator. The second generator in the file requires generator -parameters to be passed to it. These are also easy to handle: - -```cmake -# ... continuing from above -add_halide_library(my_second_generator FROM my_generators - PARAMS parallel=false scale=3.0 rotation=ccw output.type=uint16) -``` - -Adding multiple configurations is easy, too: - -```cmake -# ... continuing from above -add_halide_library(my_second_generator_2 FROM my_generators - GENERATOR my_second_generator - PARAMS scale=9.0 rotation=ccw output.type=float32) - -add_halide_library(my_second_generator_3 FROM my_generators - GENERATOR my_second_generator - PARAMS parallel=false output.type=float64) -``` - -Here, we had to specify which generator to use (`my_second_generator`) since it -uses the target name by default. The functions in these libraries will be named -after the target names, `my_second_generator_2` and `my_second_generator_3`, by -default, but it is possible to control this via the `FUNCTION_NAME` parameter. - -Each one of these targets, ``, carries an associated `.runtime` -target, which is also a static library containing the Halide runtime. It is -transitively linked through `` to targets that link to ``. On an -operating system like Linux, where weak linking is available, this is not an -issue. However, on Windows, this can fail due to symbol redefinitions. In these -cases, you must declare that two Halide libraries share a runtime, like so: - -```cmake -# ... updating above -add_halide_library(my_second_generator_2 FROM my_generators - GENERATOR my_second_generator - USE_RUNTIME my_first_generator.runtime - PARAMS scale=9.0 rotation=ccw output.type=float32) - -add_halide_library(my_second_generator_3 FROM my_generators - GENERATOR my_second_generator - USE_RUNTIME my_first_generator.runtime - PARAMS parallel=false output.type=float64) -``` - -This will even work correctly when different combinations of targets are -specified for each halide library. A "greatest common denominator" target will -be chosen that is compatible with all of them (or the build will fail). - -### Autoschedulers - -When the autoschedulers are included in the release package, they are very -simple to apply to your own generators. For example, we could update the -definition of the `my_first_generator` library above to use the `Adams2019` -autoscheduler: - -```cmake -add_halide_library(my_second_generator FROM my_generators - AUTOSCHEDULER Halide::Adams2019) -``` - -### RunGenMain - -Halide provides a generic driver for generators to be used during development -for benchmarking and debugging. Suppose you have a generator executable called -`my_gen` and a generator within called `my_filter`. Then you can pass a variable -name to the `REGISTRATION` parameter of `add_halide_library` which will contain -the name of a generated C++ source that should be linked to `Halide::RunGenMain` -and `my_filter`. - -For example: - -```cmake -add_halide_library(my_filter FROM my_gen - REGISTRATION filter_reg_cpp) -add_executable(runner ${filter_reg_cpp}) -target_link_libraries(runner PRIVATE my_filter Halide::RunGenMain) -``` - -Then you can run, debug, and benchmark your generator through the `runner` -executable. - -## Halide package documentation - -Halide provides a CMake _package configuration_ module. The intended way to use -the CMake build is to run `find_package(Halide ...)` in your `CMakeLists.txt` -file. Closely read the [`find_package` documentation][find_package] before -proceeding. - -### Components - -The Halide package script understands a handful of optional components when -loading the package. - -First, if you plan to use the Halide Image IO library, you will want to include -the `png` and `jpeg` components when loading Halide. - -Second, Halide releases can contain a variety of configurations: static, shared, -debug, release, etc. CMake handles Debug/Release configurations automatically, -but generally only allows one type of library to be loaded. - -The package understands two components, `static` and `shared`, that specify -which type of library you would like to load. For example, if you want to make -sure that you link against shared Halide, you can write: - -```cmake -find_package(Halide REQUIRED COMPONENTS shared) -``` - -If the shared libraries are not available, this will result in a failure. - -If no component is specified, then the `Halide_SHARED_LIBS` variable is checked. -If it is defined and set to true, then the shared libraries will be loaded or -the package loading will fail. Similarly, if it is defined and set to false, the -static libraries will be loaded. - -If no component is specified and `Halide_SHARED_LIBS` is _not_ defined, then the -[`BUILD_SHARED_LIBS`][build_shared_libs] variable will be inspected. If it is -**not defined** or **defined and set to true**, then it will attempt to load the -shared libs and fall back to the static libs if they are not available. -Similarly, if `BUILD_SHARED_LIBS` is **defined and set to false**, then it will -try the static libs first then fall back to the shared libs. - -### Variables - -Variables that control package loading: - -| Variable | Description | -|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Halide_SHARED_LIBS` | override `BUILD_SHARED_LIBS` when loading the Halide package via `find_package`. Has no effect when using Halide via `add_subdirectory` as a Git or `FetchContent` submodule. | -| `Halide_RUNTIME_NO_THREADS` | skip linking of Threads library to runtime. Should be set if your toolchain does not support it (e.g. baremetal). | -| `Halide_RUNTIME_NO_DL_LIBS` | skip linking of DL library to runtime. Should be set if your toolchain does not support it (e.g. baremetal). | - -Variables set by the package: - -| Variable | Description | -|----------------------------|--------------------------------------------------------------------| -| `Halide_VERSION` | The full version string of the loaded Halide package | -| `Halide_VERSION_MAJOR` | The major version of the loaded Halide package | -| `Halide_VERSION_MINOR` | The minor version of the loaded Halide package | -| `Halide_VERSION_PATCH` | The patch version of the loaded Halide package | -| `Halide_VERSION_TWEAK` | The tweak version of the loaded Halide package | -| `Halide_HOST_TARGET` | The Halide target triple corresponding to "host" for this build. | -| `Halide_CMAKE_TARGET` | The Halide target triple corresponding to the active CMake target. | -| `Halide_ENABLE_EXCEPTIONS` | Whether Halide was compiled with exception support | -| `Halide_ENABLE_RTTI` | Whether Halide was compiled with RTTI | - -Variables that control package behavior: - -| Variable | Description | -|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| -| `Halide_PYTHON_LAUNCHER` | Semicolon separated list containing a command to launch the Python interpreter. Can be used to set environment variables for Python generators. | -| `Halide_NO_DEFAULT_FLAGS` | Off by default. When enabled, suppresses recommended compiler flags that would be added by `add_halide_generator` | - - -### Imported targets - -Halide defines the following targets that are available to users: - -| Imported target | Description | -|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Halide::Halide` | this is the JIT-mode library to use when using Halide from C++. | -| `Halide::Generator` | this is the target to use when defining a generator executable. It supplies a `main()` function. | -| `Halide::Runtime` | adds include paths to the Halide runtime headers | -| `Halide::Tools` | adds include paths to the Halide tools, including the benchmarking utility. | -| `Halide::ImageIO` | adds include paths to the Halide image IO utility. Depends on `PNG::PNG` and `JPEG::JPEG` if they exist or were loaded through the corresponding package components. | -| `Halide::ThreadPool` | adds include paths to the Halide _simple_ thread pool utility library. This is not the same as the runtime's thread pool and is intended only for use by tests. Depends on `Threads::Threads`. | -| `Halide::RunGenMain` | used with the `REGISTRATION` parameter of `add_halide_library` to create simple runners and benchmarking tools for Halide libraries. | - -The following targets are not guaranteed to be available: - -| Imported target | Description | -|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Halide::Python` | this is a Python 3 package that can be referenced as `$/..` when setting up `PYTHONPATH` for Python tests or the like from CMake. | -| `Halide::Adams19` | the Adams et.al. 2019 autoscheduler (no GPU support) | -| `Halide::Li18` | the Li et.al. 2018 gradient autoscheduler (limited GPU support) | -| `Halide::Mullapudi2016` | the Mullapudi et.al. 2016 autoscheduler (no GPU support) | - -### Functions - -Currently, only two functions are defined: - -#### `add_halide_library` - -This is the main function for managing generators in AOT compilation. The full -signature follows: - -``` -add_halide_library( FROM - [GENERATOR generator-name] - [FUNCTION_NAME function-name] - [NAMESPACE cpp-namespace] - [USE_RUNTIME hl-target] - [PARAMS param1 [param2 ...]] - [TARGETS target1 [target2 ...]] - [FEATURES feature1 [feature2 ...]] - [PLUGINS plugin1 [plugin2 ...]] - [AUTOSCHEDULER scheduler-name] - [GRADIENT_DESCENT] - [C_BACKEND] - [REGISTRATION OUTVAR] - [HEADER OUTVAR] - [FUNCTION_INFO_HEADER OUTVAR] - [ OUTVAR]) - -extra-output = ASSEMBLY | BITCODE | COMPILER_LOG | FEATURIZATION - | LLVM_ASSEMBLY | PYTHON_EXTENSION - | PYTORCH_WRAPPER | SCHEDULE | STMT | STMT_HTML -``` - -This function creates a called `` corresponding to running the -`` (an executable target which links to `Halide::Generator`) -one time, using command line arguments derived from the other parameters. - -The arguments `GENERATOR` and `FUNCTION_NAME` default to ``. They -correspond to the `-g` and `-f` command line flags, respectively. - -`NAMESPACE` is syntactic sugar to specify the C++ namespace (if any) of the -generated function; you can also specify the C++ namespace (if any) directly -in the `FUNCTION_NAME` argument, but for repeated declarations or very long -namespaces, specifying this separately can provide more readable build files. - -If `USE_RUNTIME` is not specified, this function will create another target -called `.runtime` which corresponds to running the generator with `-r` -and a compatible list of targets. This runtime target is an INTERFACE dependency -of ``. If multiple runtime targets need to be linked together, setting -`USE_RUNTIME` to another Halide library, `` will prevent the generation -of `.runtime` and instead use `.runtime`. This argument is -most commonly used in conjunction with [`add_halide_runtime`](#add_halide_runtime). - -Parameters can be passed to a generator via the `PARAMS` argument. Parameters -should be space-separated. Similarly, `TARGETS` is a space-separated list of -targets for which to generate code in a single function. They must all share the -same platform/bits/os triple (eg. `arm-32-linux`). Features that are in common -among all targets, including device libraries (like `cuda`) should go in -`FEATURES`. If `TARGETS` is not specified, the value of `Halide_TARGET` specified -at configure time will be used. - -Every element of `TARGETS` must begin with the same `arch-bits-os` triple. This -function understands two _meta-triples_, `host` and `cmake`. The meta-triple -`host` is equal to the `arch-bits-os` triple used to compile Halide along with -all of the supported instruction set extensions. On platforms that support -running both 32 and 64-bit programs, this will not necessarily equal the -platform the compiler is running on or that CMake is targeting. - -The meta-triple `cmake` is equal to the `arch-bits-os` of the current CMake -target. This is useful if you want to make sure you are not unintentionally -cross-compiling, which would result in an [`IMPORTED` target][imported-target] -being created. When `TARGETS` is empty and the `host` target would not -cross-compile, then `host` will be used. Otherwise, `cmake` will be used and an -author warning will be issued. - -To use an autoscheduler, set the `AUTOSCHEDULER` argument to a target -named like `Namespace::Scheduler`, for example `Halide::Adams19`. This will set -the `autoscheduler` GeneratorParam on the generator command line to `Scheduler` -and add the target to the list of plugins. Additional plugins can be loaded by -setting the `PLUGINS` argument. If the argument to `AUTOSCHEDULER` does not -contain `::` or it does not name a target, it will be passed to the `-s` flag -verbatim. - -If `GRADIENT_DESCENT` is set, then the module will be built suitably for -gradient descent calculation in TensorFlow or PyTorch. See -`Generator::build_gradient_module()` for more documentation. This corresponds to -passing `-d 1` at the generator command line. - -If the `C_BACKEND` option is set, this command will invoke the configured C++ -compiler on a generated source. Note that a `.runtime` target is _not_ -created in this case, and the `USE_RUNTIME` option is ignored. Other options -work as expected. - -If `REGISTRATION` is set, the path (relative to `CMAKE_CURRENT_BINARY_DIR`) -to the generated `.registration.cpp` file will be set in `OUTVAR`. This can be -used to generate a runner for a Halide library that is useful for benchmarking -and testing, as documented above. This is equivalent to setting -`-e registration` at the generator command line. - -If `HEADER` is set, the path (relative to `CMAKE_CURRENT_BINARY_DIR`) to the -generated `.h` header file will be set in `OUTVAR`. This can be used with -`install(FILES)` to conveniently deploy the generated header along with your -library. - -If `FUNCTION_INFO_HEADER` is set, the path (relative to -`CMAKE_CURRENT_BINARY_DIR`) to the generated `.function_info.h` header file -will be set in `OUTVAR`. This produces a file that contains `constexpr` -descriptions of information about the generated functions (e.g., argument -type and information). It is generated separately from the normal `HEADER` -file because `HEADER` is intended to work with basic `extern "C"` linkage, -while `FUNCTION_INFO_HEADER` requires C++17 or later to use effectively. -(This can be quite useful for advanced usages, such as producing automatic -call wrappers, etc.) Examples of usage can be found in the generated file. - -Lastly, each of the `extra-output` arguments directly correspond to an extra -output (via `-e`) from the generator. The value `OUTVAR` names a variable into -which a path (relative to -[`CMAKE_CURRENT_BINARY_DIR`][cmake_current_binary_dir]) to the extra file will -be written. - -#### `add_halide_generator` - -This function aids in creating cross-compilable builds that use Halide generators. - -``` -add_halide_generator( - target - [PACKAGE_NAME package-name] - [PACKAGE_NAMESPACE namespace] - [EXPORT_FILE export-file] - [PYSTUB generator-name] - [[SOURCES] source1 ...] -) -``` - -Every named argument is optional, and the function uses the following default arguments: - -- If `PACKAGE_NAME` is not provided, it defaults to `${PROJECT_NAME}-halide_generators`. -- If `PACKAGE_NAMESPACE` is not provided, it defaults to `${PROJECT_NAME}::halide_generators::`. -- If `EXPORT_FILE` is not provided, it defaults to `${PROJECT_BINARY_DIR}/cmake/${ARG_PACKAGE_NAME}-config.cmake` - -The `SOURCES` keyword marks the beginning of sources to be used to build -``, if it is not loaded. All unparsed arguments will be interpreted as -sources. - -This function guarantees that a Halide generator target named -`` is available. It will first search for a package named -`` using `find_package`; if it is found, it is assumed that it -provides the target. Otherwise, it will create an executable target named -`target` and an `ALIAS` target ``. This function also -creates a custom target named `` if it does not exist and -`` would exist. In this case, `` will depend on -``, this enables easy building of _just_ the Halide generators managed -by this function. - -After the call, `_FOUND` will be set to true if the host -generators were imported (and hence won't be built). Otherwise, it will be set -to false. This variable may be used to conditionally set properties on -``. - -Please see [test/integration/xc](https://github.com/halide/Halide/tree/main/test/integration/xc) for a simple example -and [apps/hannk](https://github.com/halide/Halide/tree/main/apps/hannk) for a complete app that uses it extensively. - -If `PYSTUB` is specified, then a Python Extension will be built that -wraps the Generator with CPython glue to allow use of the Generator -Python 3.x. The result will be a a shared library of the form -`_pystub..so`, where describes the specific Python -version and platform (e.g., `cpython-310-darwin` for Python 3.10 on macOS.). -See `README_python.md` for examples of use. - -#### `add_halide_python_extension_library` - -This function wraps the outputs of one or more `add_halide_library` targets with glue code to produce -a Python Extension library. - -``` -add_halide_python_extension_library( - target - [MODULE_NAME module-name] - HALIDE_LIBRARIES library1 ... -) -``` - -`FROM` specifies any valid Generator target. If omitted, - -`HALIDE_LIBRARIES` is a list of one of more `add_halide_library` targets. Each will be added to the -extension as a callable method of the module. Note that every library specified must be built with -the `PYTHON_EXTENSION` keyword specified, and all libraries must use the same Halide runtime. - -The result will be a a shared library of the form -`..so`, where describes the specific Python version and -platform (e.g., `cpython-310-darwin` for Python 3.10 on macOS.) - -#### `add_halide_runtime` - -This function generates a library containing a Halide runtime. Most user code will never -need to use this, as `add_halide_library()` will call it for you if necessary. The most common -use case is usually in conjunction with `add_halide_python_extension_library()`, as a way to -ensure that all the halide libraries share an identical runtime. - -``` -add_halide_runtime( - target - [TARGETS target1 [target2 ...]] -) -``` - -The `TARGETS` argument has identical semantics to the argument of the same name -for [`add_halide_library`](#add_halide_library). - -## Cross compiling - -Cross-compiling in CMake can be tricky, since CMake doesn't easily support -compiling for both the host platform and the cross-platform within the same -build. Unfortunately, Halide generator executables are just about always -designed to run on the host platform. Each project will be set up differently -and have different requirements, but here are some suggestions for effective use -of CMake in these scenarios. - -### Use `add_halide_generator` - -If you are writing new programs that use Halide, you might wish to use our -helper, `add_halide_generator`. When using this helper, you are expected to -build your project twice: once for your build host and again for your intended -target. - -When building the host build, you can use the `` (see the -documentation above) target to build _just_ the generators. Then, in the -target build, set `_ROOT` to the host build directory. - -For example: - -``` -$ cmake -G Ninja -S . -B build-host -DCMAKE_BUILD_TYPE=Release -$ cmake --build build-host --target -$ cmake -G Ninja -S . -B build-target -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_TOOLCHAIN_FILE=/path/to/target-tc.cmake \ - -D_ROOT:FILEPATH=$PWD/build-host -$ cmake --build build-target -``` - -### Use a super-build - -A CMake super-build consists of breaking down a project into sub-projects that -are isolated by [toolchain][cmake-toolchains]. The basic structure is to have an -outermost project that only coordinates the sub-builds via the -[`ExternalProject`][externalproject] module. - -One would then use Halide to build a generator executable in one self-contained -project, then export that target to be used in a separate project. The second -project would be configured with the target [toolchain][cmake-toolchains] and -would call `add_halide_library` with no `TARGETS` option and set `FROM` equal to -the name of the imported generator executable. Obviously, this is a significant -increase in complexity over a typical CMake project. - -This is very compatible with the `add_halide_generator` strategy above. - -### Use `ExternalProject` directly - -A lighter weight alternative to the above is to use -[`ExternalProject`][externalproject] directly in your parent build. Configure -the parent build with the target [toolchain][cmake-toolchains], and configure -the inner project to use the host toolchain. Then, manually create an -[`IMPORTED` target][imported-executable] for your generator executable and call -`add_halide_library` as described above. - -The main drawback of this approach is that creating accurate `IMPORTED` targets -is difficult since predicting the names and locations of your binaries across -all possible platform and CMake project generators is difficult. In particular, -it is hard to predict executable extensions in cross-OS builds. - -### Use an emulator or run on device - -The [`CMAKE_CROSSCOMPILING_EMULATOR`][cmake_crosscompiling_emulator] variable -allows one to specify a command _prefix_ to run a target-system binary on the -host machine. One could set this to a custom shell script that uploads the -generator executable, runs it on the device and copies back the results. - -### Bypass CMake - -The previous two options ensure that the targets generated by -`add_halide_library` will be _normal_ static libraries. This approach does not -use [`ExternalProject`][externalproject], but instead produces `IMPORTED` -targets. The main drawback of `IMPORTED` targets is that they are considered -second-class in CMake. In particular, they cannot be installed with the typical -[`install(TARGETS)` command][install-targets]. Instead, they must be installed -using [`install(FILES)`][install-files] and the -[`$`][target-file] generator expression. - -# Contributing CMake code to Halide - -When contributing new CMake code to Halide, keep in mind that the minimum -version is 3.22. Therefore, it is possible (and indeed required) to use modern -CMake best practices. - -Like any large and complex system with a dedication to preserving backwards -compatibility, CMake is difficult to learn and full of traps. While not -comprehensive, the following serves as a guide for writing quality CMake code -and outlines the code quality expectations we have as they apply to CMake. - -## General guidelines and best practices - -The following are some common mistakes that lead to subtly broken builds. - -- **Reading the build directory.** While setting up the build, the build - directory should be considered _write only_. Using the build directory as a - read/write temporary directory is acceptable as long as all temp files are - cleaned up by the end of configuration. -- **Not using [generator expressions][cmake-genex].** Declarative is better than - imperative and this is no exception. Conditionally adding to a target property - can leak unwanted details about the build environment into packages. Some - information is not accurate or available except via generator expressions, eg. - the build configuration. -- **Using the wrong variable.** `CMAKE_SOURCE_DIR` doesn't always point to the - Halide source root. When someone uses Halide via - [`FetchContent`][fetchcontent], it will point to _their_ source root instead. - The correct variable is [`Halide_SOURCE_DIR`][project-name_source_dir]. If you - want to know if the compiler is MSVC, check it directly with the - [`MSVC`][msvc] variable; don't use [`WIN32`][win32]. That will be wrong when - compiling with clang on Windows. In most cases, however, a generator - expression will be more appropriate. -- **Using directory properties.** Directory properties have vexing behavior and - are essentially deprecated from CMake 3.0+. Propagating target properties is - the way of the future. -- **Using the wrong visibility.** Target properties can be `PRIVATE`, - `INTERFACE`, or both (aka `PUBLIC`). Pick the most conservative one for each - scenario. Refer to the [transitive usage requirements][cmake-propagation] docs - for more information. -- **Needlessly expanding variables** The [`if`][cmake_if] and - [`foreach`][cmake_foreach] commands generally expand variables when provided by - name. Expanding such variables manually can unintentionally change the behavior - of the command. Use `foreach (item IN LISTS list)` instead of - `foreach (item ${list})`. Similarly, use `if (varA STREQUAL varB)` instead of - `if ("${varA}" STREQUAL "${varB}")` and _definitely_ don't use - `if (${varA} STREQUAL ${varB})` since that will fail (in the best case) if - either variable's value contains a semi-colon (due to argument expansion). - -### Prohibited commands list - -As mentioned above, using directory properties is brittle and they are therefore -_not allowed_. The following functions may not appear in any new CMake code. - -| Command | Alternative | -|-------------------------------------|----------------------------------------------------------------------------------------------------| -| `add_compile_definitions` | Use [`target_compile_definitions`][target_compile_definitions] | -| `add_compile_options` | Use [`target_compile_options`][target_compile_options] | -| `add_definitions` | Use [`target_compile_definitions`][target_compile_definitions] | -| `add_link_options` | Use [`target_link_options`][target_link_options], but prefer not to use either | -| `get_directory_property` | Use cache variables or target properties | -| `get_property(... DIRECTORY)` | Use cache variables or target properties | -| `include_directories` | Use [`target_include_directories`][target_include_directories] | -| `link_directories` | Use [`target_link_libraries`][target_link_libraries] | -| `link_libraries` | Use [`target_link_libraries`][target_link_libraries] | -| `remove_definitions` | [Generator expressions][cmake-genex] in [`target_compile_definitions`][target_compile_definitions] | -| `set_directory_properties` | Use cache variables or target properties | -| `set_property(... DIRECTORY)` | Use cache variables or target properties | -| `target_link_libraries(target lib)` | Use [`target_link_libraries`][target_link_libraries] _with a visibility specifier_ (eg. `PRIVATE`) | - -As an example, it was once common practice to write code similar to this: - -```cmake -# WRONG: do not do this -include_directories(include) -add_library(my_lib source1.cpp ..) -``` - -However, this has two major pitfalls. First, it applies to _all_ targets created -in that directory, even those before the call to `include_directories` and those -created in [`include()`][include]-ed CMake files. As CMake files get larger and -more complex, this behavior gets harder to pinpoint. This is particularly vexing -when using the `link_libraries` or `add_defintions` commands. Second, this form -does not provide a way to _propagate_ the include directory to consumers of -`my_lib`. The correct way to do this is: - -```cmake -# CORRECT -add_library(my_lib source1.cpp ...) -target_include_directories(my_lib PUBLIC $) -``` - -This is better in many ways. It only affects the target in question. It -propagates the include path to the targets linking to it (via `PUBLIC`). It also -does not incorrectly export the host-filesystem-specific include path when -installing or packaging the target (via `$`). - -If common properties need to be grouped together, use an INTERFACE target -(better) or write a function (worse). There are also several functions that are -disallowed for other reasons: - -| Command | Reason | Alternative | -|---------------------------------|-----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------| -| `aux_source_directory` | Interacts poorly with incremental builds and Git | List source files explicitly | -| `build_command` | CTest internal function | Use CTest build-and-test mode via [`CMAKE_CTEST_COMMAND`][cmake_ctest_command] | -| `cmake_host_system_information` | Usually misleading information. | Inspect [toolchain][cmake-toolchains] variables and use generator expressions. | -| `cmake_policy(... OLD)` | OLD policies are deprecated by definition. | Instead, fix the code to work with the new policy. | -| `create_test_sourcelist` | We use our own unit testing solution | See the [adding tests](#adding-tests) section. | -| `define_property` | Adds unnecessary complexity | Use a cache variable. Exceptions under special circumstances. | -| `enable_language` | Halide is C/C++ only | [`FindCUDAToolkit`][findcudatoolkit] or [`FindCUDA`][findcuda], appropriately guarded. | -| `file(GLOB ...)` | Interacts poorly with incremental builds and Git | List source files explicitly. Allowed if not globbing for source files. | -| `fltk_wrap_ui` | Halide does not use FLTK | None | -| `include_external_msproject` | Halide must remain portable | Write a CMake package config file or find module. | -| `include_guard` | Use of recursive inclusion is not allowed | Write (recursive) functions. | -| `include_regular_expression` | Changes default dependency checking behavior | None | -| `load_cache` | Superseded by [`FetchContent`][fetchcontent]/[`ExternalProject`][externalproject] | Use aforementioned modules | -| `macro` | CMake macros are not hygienic and are therefore error-prone | Use functions instead. | -| `site_name` | Privacy: do not want leak host name information | Provide a cache variable, generate a unique name. | -| `variable_watch` | Debugging helper | None. Not needed in production. | - -Lastly, do not introduce any dependencies via [`find_package`][find_package] -without broader approval. Confine dependencies to the `dependencies/` subtree. - -### Prohibited variables list - -Any variables that are specific to languages that are not enabled should, of -course, be avoided. But of greater concern are variables that are easy to misuse -or should not be overridden for our end-users. The following (non-exhaustive) -list of variables shall not be used in code merged into main. - -| Variable | Reason | Alternative | -|---------------------------------|-----------------------------------------------|---------------------------------------------------------------------------------------------------------| -| `CMAKE_ROOT` | Code smell | Rely on `find_package` search options; include `HINTS` if necessary | -| `CMAKE_DEBUG_TARGET_PROPERTIES` | Debugging helper | None | -| `CMAKE_FIND_DEBUG_MODE` | Debugging helper | None | -| `CMAKE_RULE_MESSAGES` | Debugging helper | None | -| `CMAKE_VERBOSE_MAKEFILE` | Debugging helper | None | -| `CMAKE_BACKWARDS_COMPATIBILITY` | Deprecated | None | -| `CMAKE_BUILD_TOOL` | Deprecated | `${CMAKE_COMMAND} --build` or [`CMAKE_MAKE_PROGRAM`][cmake_make_program] (but see below) | -| `CMAKE_CACHEFILE_DIR` | Deprecated | [`CMAKE_BINARY_DIR`][cmake_binary_dir], but see below | -| `CMAKE_CFG_INTDIR` | Deprecated | `$`, `$`, target resolution of [`add_custom_command`][add_custom_command], etc. | -| `CMAKE_CL_64` | Deprecated | [`CMAKE_SIZEOF_VOID_P`][cmake_sizeof_void_p] | -| `CMAKE_COMPILER_IS_*` | Deprecated | [`CMAKE__COMPILER_ID`][cmake_lang_compiler_id] | -| `CMAKE_HOME_DIRECTORY` | Deprecated | [`CMAKE_SOURCE_DIR`][cmake_source_dir], but see below | -| `CMAKE_DIRECTORY_LABELS` | Directory property | None | -| `CMAKE_BUILD_TYPE` | Only applies to single-config generators. | `$` | -| `CMAKE_*_FLAGS*` (w/o `_INIT`) | User-only | Write a [toolchain][cmake-toolchains] file with the corresponding `_INIT` variable | -| `CMAKE_COLOR_MAKEFILE` | User-only | None | -| `CMAKE_ERROR_DEPRECATED` | User-only | None | -| `CMAKE_CONFIGURATION_TYPES` | We only support the four standard build types | None | - -Of course feel free to insert debugging helpers _while developing_ but please -remove them before review. Finally, the following variables are allowed, but -their use must be motivated: - -| Variable | Reason | Alternative | -|------------------------------------------------|-----------------------------------------------------|----------------------------------------------------------------------------------------------| -| [`CMAKE_SOURCE_DIR`][cmake_source_dir] | Points to global source root, not Halide's. | [`Halide_SOURCE_DIR`][project-name_source_dir] or [`PROJECT_SOURCE_DIR`][project_source_dir] | -| [`CMAKE_BINARY_DIR`][cmake_binary_dir] | Points to global build root, not Halide's | [`Halide_BINARY_DIR`][project-name_binary_dir] or [`PROJECT_BINARY_DIR`][project_binary_dir] | -| [`CMAKE_MAKE_PROGRAM`][cmake_make_program] | CMake abstracts over differences in the build tool. | Prefer CTest's build and test mode or CMake's `--build` mode | -| [`CMAKE_CROSSCOMPILING`][cmake_crosscompiling] | Often misleading. | Inspect relevant variables directly, eg. [`CMAKE_SYSTEM_NAME`][cmake_system_name] | -| [`BUILD_SHARED_LIBS`][build_shared_libs] | Could override user setting | None, but be careful to restore value when overriding for a dependency | - -Any use of these functions and variables will block a PR. - -## Adding tests - -When adding a file to any of the folders under `test`, be aware that CI expects -that every `.c` and `.cpp` appears in the `CMakeLists.txt` file _on its own -line_, possibly as a comment. This is to avoid globbing and also to ensure that -added files are not missed. - -For most test types, it should be as simple as adding to the existing lists, -which must remain in alphabetical order. Generator tests are trickier, but -following the existing examples is a safe way to go. - -## Adding apps - -If you're contributing a new app to Halide: great! Thank you! There are a few -guidelines you should follow when writing a new app. - -- Write the app as if it were a top-level project. You should call - `find_package(Halide)` and set the C++ version to 11. -- Call [`enable_testing()`][enable_testing] and add a small test that runs the - app. -- Don't assume your app will have access to a GPU. Write your schedules to be - robust to varying buildbot hardware. -- Don't assume your app will be run on a specific OS, architecture, or bitness. - Write your apps to be robust (ideally efficient) on all supported platforms. -- If you rely on any additional packages, don't include them as `REQUIRED`, - instead test to see if their targets are available and, if not, call - `return()` before creating any targets. In this case, print a - `message(STATUS "[SKIP] ...")`, too. -- Look at the existing apps for examples. -- Test your app with ctest before opening a PR. Apps are built as part of the - test, rather than the main build. - -[add_custom_command]: - https://cmake.org/cmake/help/latest/command/add_custom_command.html -[add_library]: https://cmake.org/cmake/help/latest/command/add_library.html -[add_subdirectory]: - https://cmake.org/cmake/help/latest/command/add_subdirectory.html -[atlas]: http://math-atlas.sourceforge.net/ -[brew-cmake]: https://formulae.brew.sh/cask/cmake#default -[build_shared_libs]: - https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html -[choco-cmake]: https://chocolatey.org/packages/cmake/ -[choco-doxygen]: https://chocolatey.org/packages/doxygen.install -[choco-ninja]: https://chocolatey.org/packages/ninja -[chocolatey]: https://chocolatey.org/ -[cmake-apt]: https://apt.kitware.com/ -[cmake-discourse]: https://discourse.cmake.org/ -[cmake-docs]: https://cmake.org/cmake/help/latest/ -[cmake-download]: https://cmake.org/download/ -[cmake-from-source]: https://cmake.org/install/ -[cmake-genex]: - https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html -[cmake-install]: - https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project -[cmake-propagation]: - https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#transitive-usage-requirements -[cmake-toolchains]: - https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html -[cmake-user-interaction]: - https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables -[cmake_binary_dir]: - https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html -[cmake_build_type]: - https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html -[cmake_crosscompiling]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING.html -[cmake_crosscompiling_emulator]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING_EMULATOR.html -[cmake_ctest_command]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CTEST_COMMAND.html -[cmake_current_binary_dir]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_BINARY_DIR.html -[cmake_cxx_extensions]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_EXTENSIONS.html -[cmake_cxx_standard]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD.html -[cmake_cxx_standard_required]: - https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD_REQUIRED.html -[cmake_foreach]: - https://cmake.org/cmake/help/latest/command/foreach.html -[cmake_if]: - https://cmake.org/cmake/help/latest/command/if.html -[cmake_lang_compiler_id]: - https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html -[cmake_make_program]: - https://cmake.org/cmake/help/latest/variable/CMAKE_MAKE_PROGRAM.html -[cmake_minimum_required]: - https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html -[cmake_prefix_path]: - https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html -[cmake_presets]: - https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html -[cmake_sizeof_void_p]: - https://cmake.org/cmake/help/latest/variable/CMAKE_SIZEOF_VOID_P.html -[cmake_source_dir]: - https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html -[cmake_system_name]: - https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html -[doxygen-download]: https://www.doxygen.nl/download.html -[doxygen]: https://www.doxygen.nl/index.html -[eigen]: http://eigen.tuxfamily.org/index.php?title=Main_Page -[enable_testing]: - https://cmake.org/cmake/help/latest/command/enable_testing.html -[externalproject]: - https://cmake.org/cmake/help/latest/module/ExternalProject.html -[fetchcontent]: https://cmake.org/cmake/help/latest/module/FetchContent.html -[find_package]: https://cmake.org/cmake/help/latest/command/find_package.html -[findcuda]: https://cmake.org/cmake/help/latest/module/FindCUDA.html -[findcudatoolkit]: - https://cmake.org/cmake/help/latest/module/FindCUDAToolkit.html -[finddoxygen]: https://cmake.org/cmake/help/latest/module/FindDoxygen.html -[findjpeg]: https://cmake.org/cmake/help/latest/module/FindJPEG.html -[findopencl]: https://cmake.org/cmake/help/latest/module/FindOpenCL.html -[findpng]: https://cmake.org/cmake/help/latest/module/FindPNG.html -[findpython3]: https://cmake.org/cmake/help/latest/module/FindPython3.html -[findx11]: https://cmake.org/cmake/help/latest/module/FindX11.html -[halide-generator-tutorial]: - https://halide-lang.org/tutorials/tutorial_lesson_15_generators.html -[halide-tutorials]: https://halide-lang.org/tutorials/tutorial_introduction.html -[homebrew]: https://brew.sh -[imported-executable]: - https://cmake.org/cmake/help/latest/command/add_executable.html#imported-executables -[imported-target]: - https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#imported-targets -[include]: https://cmake.org/cmake/help/latest/command/include.html -[install-files]: https://cmake.org/cmake/help/latest/command/install.html#files -[install-targets]: - https://cmake.org/cmake/help/latest/command/install.html#targets -[libjpeg]: https://www.libjpeg-turbo.org/ -[libpng]: http://www.libpng.org/pub/png/libpng.html -[lld]: https://lld.llvm.org/ -[msvc]: https://cmake.org/cmake/help/latest/variable/MSVC.html -[msvc-cmd]: - https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019 -[ninja-download]: https://github.com/ninja-build/ninja/releases -[ninja]: https://ninja-build.org/ -[openblas]: https://www.openblas.net/ -[project]: https://cmake.org/cmake/help/latest/command/project.html -[project-name_binary_dir]: - https://cmake.org/cmake/help/latest/variable/PROJECT-NAME_BINARY_DIR.html -[project-name_source_dir]: - https://cmake.org/cmake/help/latest/variable/PROJECT-NAME_SOURCE_DIR.html -[project_source_dir]: - https://cmake.org/cmake/help/latest/variable/PROJECT_SOURCE_DIR.html -[project_binary_dir]: - https://cmake.org/cmake/help/latest/variable/PROJECT_BINARY_DIR.html -[pypi-cmake]: https://pypi.org/project/cmake/ -[python]: https://www.python.org/downloads/ -[target-file]: - https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#target-dependent-queries -[target_compile_definitions]: - https://cmake.org/cmake/help/latest/command/target_compile_definitions.html -[target_compile_options]: - https://cmake.org/cmake/help/latest/command/target_compile_options.html -[target_include_directories]: - https://cmake.org/cmake/help/latest/command/target_include_directories.html -[target_link_libraries]: - https://cmake.org/cmake/help/latest/command/target_link_libraries.html -[target_link_options]: - https://cmake.org/cmake/help/latest/command/target_link_options.html -[vcpkg]: https://github.com/Microsoft/vcpkg -[vcvarsall]: - https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#vcvarsall-syntax -[venv]: https://docs.python.org/3/tutorial/venv.html -[vs2019-cmake-docs]: - https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019 -[win32]: https://cmake.org/cmake/help/latest/variable/WIN32.html diff --git a/doc/BuildingHalideWithCMake.md b/doc/BuildingHalideWithCMake.md new file mode 100644 index 000000000000..113c957dff94 --- /dev/null +++ b/doc/BuildingHalideWithCMake.md @@ -0,0 +1,626 @@ +# Building Halide with CMake + +This is a detailed guide to building Halide with CMake. If you want to learn how +to use Halide in your own CMake projects, see [HalideCMakePackage.md]. If you +are looking for Halide's CMake coding guidelines, see [CodeStyleCMake.md]. + + +* [Building Halide with CMake](#building-halide-with-cmake) +* [Installing CMake](#installing-cmake) + * [Cross-platform](#cross-platform) + * [Windows](#windows) + * [macOS](#macos) + * [Ubuntu Linux](#ubuntu-linux) + * [Optional: Install Ninja](#optional-install-ninja) +* [Dependencies](#dependencies) + * [Summary](#summary) + * [Installing dependencies](#installing-dependencies) + * [vcpkg](#vcpkg) + * [Windows](#windows-1) + * [Homebrew](#homebrew) + * [Ubuntu / Debian](#ubuntu--debian) + * [Python](#python) +* [Building Halide](#building-halide) + * [Basic build](#basic-build) + * [Windows](#windows-2) + * [macOS and Linux](#macos-and-linux) + * [CMake Presets](#cmake-presets) + * [Common presets](#common-presets) + * [Vcpkg presets](#vcpkg-presets) + * [Sanitizer presets](#sanitizer-presets) + * [Build options](#build-options) + * [Installing](#installing) +* [Building Halide with pip](#building-halide-with-pip) + + +# Installing CMake + +This section covers installing a recent version of CMake and the correct +dependencies for building and using Halide. If you have not used CMake before, +we strongly suggest reading through the [CMake documentation][cmake-docs] first. + +Halide requires at least version 3.28. Fortunately, getting a recent version of +CMake couldn't be easier, and there are multiple good options on any system to +do so. Generally, one should always have the most recent version of CMake +installed system-wide. CMake is committed to backwards compatibility and even +the most recent release can build projects over a decade old. + +## Cross-platform + +Kitware provides packages for CMake on [PyPI][pypi-cmake] which can be installed +via `pip` into a [virtual environment][venv]. There are binary wheels available +for nearly all relevant platforms, including: + +| OS | x86-32 | x86-64 | ARM64 | +|-------------------|--------------------|--------------------|----------------------------| +| Windows | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| macOS | :x: | 10.10+ | 11.0+ (incl. `universal2`) | +| Linux (musl 1.1+) | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Linux (glibc) | glibc 2.12+ | glibc 2.12+ | glibc 2.17+ | + +We recommend installing CMake using [pipx] to avoid package conflicts and +redundant installations. After installing pipx, run: + +```shell +$ pipx install cmake +``` + +Alternatively, you can use a normal virtual environment: + +```shell +$ python -m pip install cmake +``` + +If you don't want Python to manage your CMake installation, you can either +follow the platform-specific instructions below or install CMake +from [Kitware's binary releases][cmake-download]. If all else fails, you might +need to build CMake from source (e.g. on 32-bit ARM). In that case, follow the +directions posted on [Kitware's website][cmake-from-source]. + +## Windows + +On Windows, there are two primary methods for installing an up-to-date CMake: + +1. You can get CMake through the Visual Studio 2022 installer. +2. You can use Windows's built-in package manager, [winget]: + ```shell + winget install Kitware.CMake + ``` + +We prefer the first option for its simplicity. See +Microsoft's [documentation][vs-cmake-docs] for more details. + +## macOS + +[Homebrew] keeps its [CMake package][brew-cmake] up to date. Simply run: + +```shell +$ brew install cmake +``` + +## Ubuntu Linux + +There are a few good ways to install CMake on Ubuntu: + +1. If you're running 24.04 LTS, then simply running + `sudo apt install cmake` will install CMake 3.28. +2. If you're running an older LTS or would like to use the newest CMake, try + installing via the [snap store]: `snap install cmake`. Note this will + conflict with an APT-provided CMake. +3. Kitware also provides an [APT repository][cmake-apt] with up-to-date + releases. Compatible with 20.04 LTS+ and is the best option for 32-bit ARM. + +For other Linux distributions, check with your distribution's package manager. + +**Note:** On WSL 1, snap is not available; in this case, prefer to use APT. On +WSL 2, all methods are available. + +## Optional: Install Ninja + +We strongly recommend using [Ninja] as your go-to CMake generator for working +with Halide. It has a much richer dependency structure than the alternatives, +and it is the only generator capable of producing accurate incremental builds. + +It is available in most package repositories: + +* Python: `pipx install ninja` +* Visual Studio Installer: alongside CMake +* winget: `winget install Ninja-build.Ninja` +* Homebrew: `brew install ninja` +* APT: `apt install ninja-build` + +You can also place a [pre-built binary][ninja-download] from their website in +the PATH. + +# Dependencies + +## Summary + +The following is a complete list of required and optional dependencies for +building the core pieces of Halide. + +| Dependency | Version | Required when... | Notes | +|---------------|--------------------|----------------------------|-----------------------------------------------------| +| [LLVM] | _see policy below_ | _always_ | WebAssembly and X86 targets are required. | +| [Clang] | `==LLVM` | _always_ | | +| [LLD] | `==LLVM` | _always_ | | +| [flatbuffers] | `~=23.5.26` | `WITH_SERIALIZATION=ON` | | +| [wabt] | `==1.0.36` | `Halide_WASM_BACKEND=wabt` | Does not have a stable API; exact version required. | +| [V8] | trunk | `Halide_WASM_BACKEND=V8` | Difficult to build. See [WebAssembly.md] | +| [Python] | `>=3.8` | `WITH_PYTHON_BINDINGS=ON` | | +| [pybind11] | `~=2.10.4` | `WITH_PYTHON_BINDINGS=ON` | | + +Halide maintains the following compatibility policy with LLVM: Halide version +`N` supports LLVM versions `N`, `N-1`, and `N-2`. Our binary distributions +always include the latest `N` patch at time of release. For most users, we +recommend using a pre-packaged binary release of LLVM rather than trying to +build it yourself. + +To build the apps, documentation, and tests, an extended set is needed. + +| Dependency | Required when... | Notes | +|---------------------------------|-----------------------------------|-----------------------------------------------------------------------------| +| [CUDA Toolkit][FindCUDAToolkit] | building `apps/cuda_mat_mul` | When compiling Halide pipelines that use CUDA, only the drivers are needed. | +| [Doxygen][FindDoxygen] | `WITH_DOCS=ON` | | +| [Eigen3][Eigen3CMake] | building `apps/linear_algebra` | | +| [libjpeg][FindJPEG] | `WITH_TESTS=ON` | Optionally used by `halide_image_io.h` and `Halide::ImageIO` in CMake. | +| [libpng][FindPNG] | `WITH_TESTS=ON` | (same as libjpeg) | +| [BLAS][FindBLAS] | building `apps/linear_algebra` | [ATLAS] and [OpenBLAS] are supported implementations | +| [OpenCL][FindOpenCL] | compiling pipelines with `opencl` | | + +It is best practice to configure your environment so that CMake can find +dependencies without package-specific hints. For instance, if you want CMake to +use a particular version of Python, create a virtual environment and activate it +_before_ configuring Halide. Similarly, the `CMAKE_PREFIX_PATH` variable can be +set to a local directory where from-source dependencies have been installed. +Carefully consult the [find_package] documentation to learn how the search +procedure works. + +If the build still fails to find a dependency, each package provides a bespoke +interface for providing hints and overriding incorrect results. Documentation +for these packages is linked in the table above. + +## Installing dependencies + +### vcpkg + +Halide has first-class support for using [vcpkg] to manage dependencies. The +list of dependencies and features is contained inside `vcpkg.json` at the root +of the repository. + +By default, a minimum set of LLVM backends will be enabled to compile JIT code +for the host and the serialization feature will be enabled. When using the vcpkg +toolchain file, you can set `-DVCPKG_MANIFEST_FEATURES=developer` +to enable building all dependencies (except Doxygen, which is not available on +vcpkg). + +By default, running `vcpkg install` will try to build all of LLVM. This is often +undesirable as it takes very long to do and consumes a lot of disk space, +especially as `vcpkg` requires special configuration to disable the debug build. +It will _also_ attempt to build Python 3 as a dependency of pybind11. + +To mitigate this issue, we provide a [vcpkg-overlay] that disables building LLVM +and Python. When using the vcpkg toolchain, you can enable it by setting +`-DVCPKG_OVERLAY_PORTS=cmake/vcpkg`. + +If you do choose to use vcpkg to build LLVM (the easiest way on Windows), note +that it is safe to delete the intermediate build files and caches in +`D:\vcpkg\buildtrees` and `%APPDATA%\local\vcpkg`. + +For convenience, we provide [CMake presets](#cmake-presets) that set these flags +appropriately per-platform. They are documented further below. + +### Windows + +On Windows, we recommend using `vcpkg` to install library dependencies. + +To build the documentation, you will need to install [Doxygen]. This can be done +either from the [Doxygen website][doxygen-download] or through [winget]: + +```shell +$ winget install DimitriVanHeesch.Doxygen +``` + +To build the Python bindings, you will need to install Python 3. This should be +done by running the official installer from the [Python website][python]. Be +sure to download the debugging symbols through the installer. This will require +using the "Advanced Installation" workflow. Although it is not strictly +necessary, it is convenient to install Python system-wide on Windows (i.e. +`C:\Program Files`) because CMake looks at standard paths and registry keys. +This removes the need to manually set the `PATH`. + +Once Python is installed, you can install the Python module dependencies either +globally or in a [virtual environment][venv] by running + +```shell +$ python -m pip install -r requirements.txt +``` + +from the root of the repository. + +### Homebrew + +On macOS, it is possible to install all dependencies via [Homebrew][homebrew]: + +```shell +$ brew install llvm flatbuffers wabt python pybind11 doxygen eigen libpng libjpeg openblas +``` + +The `llvm` package includes `clang`, `clang-format`, and `lld`, too. To ensure +CMake can find the keg-only dependencies, set the following: + +```shell +$ export CMAKE_PREFIX_PATH="/opt/homebrew:/opt/homebrew/opt/llvm:/opt/homebrew/opt/jpeg" +``` + +### Ubuntu / Debian + +On Ubuntu you should install the following packages (this includes the Python +module dependencies): + +``` +$ sudo apt install clang-tools lld llvm-dev libclang-dev liblld-dev \ + libpng-dev libjpeg-dev libgl-dev python3-dev python3-numpy python3-scipy \ + python3-imageio python3-pybind11 libopenblas-dev libeigen3-dev \ + libatlas-base-dev doxygen +``` + +### Python + +When running the Python package, you will need to install additional +dependencies. These are tabulated in `requirements.txt` and may be installed +with: + +```shell +$ python -m pip install -U pip "setuptools[core]" wheel +$ python -m pip install -r requirements.txt +``` + +# Building Halide + +## Basic build + +These instructions assume that your working directory is the Halide repository +root. + +### Windows + +If you plan to use the Ninja generator, be sure to launch the developer command +prompt corresponding to your intended environment. Note that whatever your +intended target system (x86, x64, or ARM), you must use the 64-bit _host tools_ +because the 32-bit tools run out of memory during the linking step with LLVM. +More information is available from [Microsoft's documentation][msvc-cmd]. + +You should either open the correct Developer Command Prompt directly or run the +[`vcvarsall.bat`][vcvarsall] script with the correct argument, i.e. one of the +following: + +```shell +$ "C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 +$ "C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64_x86 +$ "C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64_arm +``` + +Then, assuming that vcpkg is installed to `D:\vcpkg`, simply run: + +```shell +$ cmake -G Ninja -S . -B build --toolchain D:\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=Release +$ cmake --build .\build +``` + +Valid values of [`CMAKE_BUILD_TYPE`][cmake_build_type] are `Debug`, +`RelWithDebInfo`, `MinSizeRel`, and `Release`. When using a single-configuration +generator (like Ninja) you must specify a build type in the configuration step. + +Otherwise, if you wish to create a Visual Studio based build system, you can +configure with: + +```shell +$ cmake -G "Visual Studio 17 2022" -Thost=x64 -A x64 -S . -B build ^ + --toolchain D:\vcpkg\scripts\buildsystems\vcpkg.cmake +$ cmake --build .\build --config Release -j %NUMBER_OF_PROCESSORS% +``` + +Because the Visual Studio generator is a _multi-config generator_, you don't set +`CMAKE_BUILD_TYPE` at configure-time, but instead pass the configuration to the +build (and test/install) commands with the `--config` flag. More documentation +is available in the [CMake User Interaction Guide][cmake-user-interaction]. + +The process is similar for 32-bit: + +``` +> cmake -G "Visual Studio 17 2022" -Thost=x64 -A Win32 -S . -B build ^ + --toolchain D:\vcpkg\scripts\buildsystems\vcpkg.cmake +> cmake --build .\build --config Release -j %NUMBER_OF_PROCESSORS% +``` + +In both cases, the `-Thost=x64` flag ensures that the correct host tools are +used. + +**Note:** due to limitations in MSBuild, incremental builds using the VS +generators will miss dependencies (including changes to headers in the +`src/runtime` folder). We recommend using Ninja for day-to-day development and +use Visual Studio only if you need it for packaging. + +### macOS and Linux + +The instructions here are straightforward. Assuming your environment is set up +correctly, just run: + +```shell +$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release +$ cmake --build build +``` + +If you omit `-G Ninja`, a Makefile-based generator will likely be used instead. +In either case, [`CMAKE_BUILD_TYPE`][cmake_build_type] must be set to one of the +standard types: `Debug`, `RelWithDebInfo`, `MinSizeRel`, or `Release`. + +## CMake Presets + +### Common presets + +Halide provides several [presets][cmake_presets] to make the above commands more +convenient. The following CMake preset commands correspond to the longer ones +above. + +```shell +$ cmake --preset=win64 # VS 2022 generator, 64-bit build, vcpkg deps +$ cmake --preset=win32 # VS 2022 generator, 32-bit build, vcpkg deps +$ cmake --preset=macOS # Ninja generator, macOS host build, Homebrew deps +$ cmake --preset=debug # Debug mode, any single-config generator / compiler +$ cmake --preset=release # Release mode, any single-config generator / compiler +``` + +### Vcpkg presets + +Halide provides two sets of corresponding vcpkg-enabled presets: _base_ and +_full_. + +| Base preset | Full preset | +|-----------------|----------------------| +| `win32` | `win32-vcpkg-full` | +| `win64` | `win64-vcpkg-full` | +| `macOS-vcpkg` | `macOS-vcpkg-full` | +| `debug-vcpkg` | `debug-vcpkg-full` | +| `release-vcpkg` | `release-vcpkg-full` | + +In simple terms, the base presets rely on the system to provide LLVM and Python, +while the full presets delegate this to vcpkg (which consumes a large amount of +hard disk space and time). + +The `macOS-vcpkg` preset adds `/opt/homebrew/opt/llvm` to +`CMAKE_PREFIX_PATH`. + +### Sanitizer presets + +There are also presets to use some Clang sanitizers with the CMake build; at +present, only Fuzzer and ASAN (Address Sanitizer) are supported, and only on +linux-x86-64. + +* `linux-x64-asan`: Use the Address Sanitizer +* `linux-x64-fuzzer`: Use the Clang fuzzer plugin + +To use these, you must build LLVM with additional options: + +``` +-DLLVM_ENABLE_PROJECTS="clang;lld;clang-tools-extra" +-DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" +``` + +## Build options + +Halide reads and understands several options that can configure the build. The +following are the most consequential and control how Halide is actually +compiled. + +| Option | Default | Description | +|------------------------------------------|-----------------------|---------------------------------------------------------------------------------------------------| +| [`BUILD_SHARED_LIBS`][build_shared_libs] | `ON` | Standard CMake variable that chooses whether to build as a static or shared library. | +| `Halide_BUNDLE_STATIC` | `OFF` | When building Halide as a static library, merge static library dependencies into libHalide.a. | +| `Halide_LLVM_SHARED_LIBS` | `OFF` | Link to the shared version of LLVM. Not available on Windows. | +| `Halide_ENABLE_RTTI` | _inherited from LLVM_ | Enable RTTI when building Halide. Recommended to be set to `ON` | +| `Halide_ENABLE_EXCEPTIONS` | `ON` | Enable exceptions when building Halide | +| `Halide_TARGET` | _empty_ | The default target triple to use for `add_halide_library` (and the generator tests, by extension) | +| `WITH_AUTOSCHEDULERS` | `ON` | Enable building the autoschedulers. Requires `BUILD_SHARED_LIBS`. | +| `WITH_SERIALIZATION` | `ON` | Include experimental Serialization/Deserialization features | + +The following options are disabled by default when building Halide 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_DOCS` | `OFF` | Enable building the documentation via Doxygen | +| `WITH_PACKAGING` | `ON` | Include the `install()` rules for Halide. | +| `WITH_PYTHON_BINDINGS` | `ON` | Enable building Python 3 bindings | +| `WITH_TESTS` | `ON` | Enable building unit and integration tests | +| `WITH_TUTORIALS` | `ON` | Enable building the tutorials | +| `WITH_UTILS` | `ON` | Enable building various utilities including the trace visualizer | + +The following options are _advanced_ and should not be required in typical +workflows. Generally, these are used by Halide's own CI infrastructure, or as +escape hatches for third-party packagers. + +| Option | Default | Description | +|-----------------------------|--------------------------------------------------------------------|------------------------------------------------------------------------------------------| +| `Halide_CLANG_TIDY_BUILD` | `OFF` | Used internally to generate fake compile jobs for runtime files when running clang-tidy. | +| `Halide_CCACHE_BUILD` | `OFF` | Use ccache with Halide-recommended settings to accelerate rebuilds. | +| `Halide_CCACHE_PARAMS` | `CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines` | Options to pass to `ccache` when using `Halide_CCACHE_BUILD`. | +| `Halide_VERSION_OVERRIDE` | `${Halide_VERSION}` | Override the VERSION for libHalide. | +| `Halide_SOVERSION_OVERRIDE` | `${Halide_VERSION_MAJOR}` | Override the SOVERSION for libHalide. Expects a positive integer (i.e. not a version). | + +The following options control whether to build certain test subsets. They only +apply when `WITH_TESTS=ON`: + +| Option | Default | Description | +|---------------------------|------------|---------------------------------------| +| `WITH_TEST_AUTO_SCHEDULE` | `ON` | enable the auto-scheduling tests | +| `WITH_TEST_CORRECTNESS` | `ON` | enable the correctness tests | +| `WITH_TEST_ERROR` | `ON` | enable the expected-error tests | +| `WITH_TEST_FUZZ` | _detected_ | enable the libfuzzer-based fuzz tests | +| `WITH_TEST_GENERATOR` | `ON` | enable the AOT generator tests | +| `WITH_TEST_PERFORMANCE` | `ON` | enable performance testing | +| `WITH_TEST_RUNTIME` | `ON` | enable testing the runtime modules | +| `WITH_TEST_WARNING` | `ON` | enable the expected-warning tests | + +The following option selects the execution engine for in-process WASM testing: + +| Option | Default | Description | +|-----------------------|---------|------------------------------------------------------------------------------------------| +| `Halide_WASM_BACKEND` | `wabt` | Select the backend for WASM testing. Can be `wabt`, `V8` or a false value such as `OFF`. | + +## Installing + +Once built, Halide will need to be installed somewhere before using it in a +separate project. On any platform, this means running the +[`cmake --install`][cmake-install] command in one of two ways. For a +single-configuration generator (like Ninja), run either: + +```shell +$ cmake --install ./build --prefix /path/to/Halide-install +$ cmake --install .\build --prefix X:\path\to\Halide-install +``` + +For a multi-configuration generator (like Visual Studio) run: + +```shell +$ cmake --install ./build --prefix /path/to/Halide-install --config Release +$ cmake --install .\build --prefix X:\path\to\Halide-install --config Release +``` + +Of course, make sure that you build the corresponding config before attempting +to install it. + +# Building Halide with pip + +Halide also supports installation via the standard Python packaging workflow. +Running `pip install .` at the root of the repository will build a wheel and +install it into the currently active Python environment. + +However, this comes with a few caveats: + +1. `Halide_USE_FETCHCONTENT` is disabled, so the environment must be prepared + for CMake to find its dependencies. This is easiest to do by setting either + `CMAKE_PREFIX_PATH` to pre-built dependencies or by setting + `CMAKE_TOOLCHAIN_FILE` to vcpkg. +2. The build settings are fixed, meaning that `wabt` is required on non-Windows + systems, `flatbuffers` is always required, and the Python bindings must be + built. +3. The generated wheel will likely only work on your system. In particular, it + will not be repaired with `auditwheel` or `delocate`. + +Even so, this is a very good method of installing Halide. It supports both +Python and C++ `find_package` workflows. + + +[ATLAS]: http://math-atlas.sourceforge.net/ + +[BuildingHalideWithCMake.md]: ./BuildingHalideWithCMake.md + +[Clang]: https://clang.llvm.org + +[CodeStyleCMake.md]: ./CodeStyleCMake.md + +[Eigen3CMake]: https://eigen.tuxfamily.org/dox/TopicCMakeGuide.html + +[Eigen3]: http://eigen.tuxfamily.org/index.php?title=Main_Page + +[FindBLAS]: https://cmake.org/cmake/help/latest/module/FindBLAS.html + +[FindCUDAToolkit]: https://cmake.org/cmake/help/latest/module/FindCUDAToolkit.html + +[FindCUDA]: https://cmake.org/cmake/help/latest/module/FindCUDA.html + +[FindDoxygen]: https://cmake.org/cmake/help/latest/module/FindDoxygen.html + +[FindJPEG]: https://cmake.org/cmake/help/latest/module/FindJPEG.html + +[FindOpenCL]: https://cmake.org/cmake/help/latest/module/FindOpenCL.html + +[FindPNG]: https://cmake.org/cmake/help/latest/module/FindPNG.html + +[FindPython]: https://cmake.org/cmake/help/latest/module/FindPython.html + +[HalideCMakePackage.md]: ./HalideCMakePackage.md + +[LLVM]: https://github.com/llvm/llvm-project + +[Ninja]: https://ninja-build.org/ + +[OpenBLAS]: https://www.openblas.net/ + +[V8]: https://v8.dev + +[WebAssembly.md]: ./WebAssembly.md + +[add_subdirectory]: https://cmake.org/cmake/help/latest/command/add_subdirectory.html + +[brew-cmake]: https://formulae.brew.sh/cask/cmake#default + +[build_shared_libs]: https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html + +[cmake-apt]: https://apt.kitware.com/ + +[cmake-docs]: https://cmake.org/cmake/help/latest/ + +[cmake-download]: https://cmake.org/download/ + +[cmake-from-source]: https://cmake.org/install/ + +[cmake-install]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project + +[cmake-user-interaction]: https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables + +[cmake_build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html + +[cmake_presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html + +[doxygen-download]: https://www.doxygen.nl/download.html + +[doxygen]: https://www.doxygen.nl/index.html + +[enable_testing]: https://cmake.org/cmake/help/latest/command/enable_testing.html + +[fetchcontent]: https://cmake.org/cmake/help/latest/module/FetchContent.html + +[find_package]: https://cmake.org/cmake/help/latest/command/find_package.html + +[flatbuffers]: https://github.com/google/flatbuffers + +[homebrew]: https://brew.sh + +[libjpeg]: https://www.libjpeg-turbo.org/ + +[libpng]: http://www.libpng.org/pub/png/libpng.html + +[lld]: https://lld.llvm.org/ + +[msvc-cmd]: https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line + +[ninja-download]: https://github.com/ninja-build/ninja/releases + +[pipx]: https://pipx.pypa.io/stable/ + +[pybind11]: https://github.com/pybind/pybind11 + +[pypi-cmake]: https://pypi.org/project/cmake/ + +[python]: https://www.python.org/downloads/ + +[snap store]: https://snapcraft.io/cmake + +[vcpkg-overlay]: https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports + +[vcpkg]: https://github.com/Microsoft/vcpkg + +[vcvarsall]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line#developer_command_file_locations + +[venv]: https://docs.python.org/3/tutorial/venv.html + +[vs-cmake-docs]: https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio + +[wabt]: https://github.com/WebAssembly/wabt + +[winget]: https://learn.microsoft.com/en-us/windows/package-manager/winget/ \ No newline at end of file diff --git a/doc/CodeStyleCMake.md b/doc/CodeStyleCMake.md new file mode 100644 index 000000000000..6a08d1b77317 --- /dev/null +++ b/doc/CodeStyleCMake.md @@ -0,0 +1,393 @@ +# Contributing CMake code to Halide + +This document specifies the coding standards we adhere to when authoring new +CMake code. If you need directions for building Halide, +see [BuildingHalideWithCMake.md]. If you are looking for Halide's CMake package +documentation, see [HalideCMakePackage.md]. + +This document is necessary for two major reasons. First, due to its long +history, size, and dedication to backwards compatibility, CMake is _incredibly_ +difficult to learn and full of traps. Second, Halide bundles its own LLVM-based +native code generator, which CMake deeply does not expect. This means we +routinely push CMake's build model to its limit. + +Therefore, we must be careful to write high-quality CMake code so that it is +clear when CMake's limitations are being tested. While not comprehensive, the +guide outlines the code quality expectations we have as they apply to CMake. + +When contributing new CMake code to Halide, keep in mind that the minimum +version is 3.28. Therefore, it is not only possible, but _required_, to use +modern CMake best practices. + + +* [Contributing CMake code to Halide](#contributing-cmake-code-to-halide) +* [General guidelines and best practices](#general-guidelines-and-best-practices) + * [Prohibited commands list](#prohibited-commands-list) + * [Prohibited variables list](#prohibited-variables-list) +* [Adding tests](#adding-tests) +* [Adding apps](#adding-apps) + + +# General guidelines and best practices + +The following are some common mistakes that lead to subtly broken builds. + +- **Reading the build directory.** While setting up the build, the build + directory should be considered _write only_. Using the build directory as a + read/write temporary directory is acceptable as long as all temp files are + cleaned up by the end of configuration. +- **Not using [generator expressions][cmake-genex].** Declarative is better than + imperative and this is no exception. Conditionally adding to a target property + can leak unwanted details about the build environment into packages. Some + information is not accurate or available except via generator expressions, + e.g. the build configuration. +- **Using the wrong variable.** `CMAKE_SOURCE_DIR` doesn't always point to the + Halide source root. When someone uses Halide via + [`FetchContent`][FetchContent], it will point to _their_ source root instead. + The correct variable is [`Halide_SOURCE_DIR`][project-name_source_dir]. If you + want to know if the compiler is MSVC, check it directly with the + [`MSVC`][msvc] variable; don't use [`WIN32`][win32]. That will be wrong when + compiling with clang on Windows. In most cases, however, a generator + expression will be more appropriate. +- **Using directory properties.** Directory properties have vexing behavior and + are essentially deprecated from CMake 3.0+. Propagating target properties is + the way of the future. +- **Using the wrong visibility.** Target properties can be `PRIVATE`, + `INTERFACE`, or both (aka `PUBLIC`). Pick the most conservative one for each + scenario. Refer to the [transitive usage requirements][cmake-propagation] docs + for more information. +- **Needlessly expanding variables** The [`if`][cmake_if] and + [`foreach`][cmake_foreach] commands generally expand variables when provided + by name. Expanding such variables manually can unintentionally change the + behavior of the command. Use `foreach (item IN LISTS list)` instead of + `foreach (item ${list})`. Similarly, use `if (varA STREQUAL varB)` instead of + `if ("${varA}" STREQUAL "${varB}")` and _definitely_ don't use + `if (${varA} STREQUAL ${varB})` since that will fail (in the best case) if + either variable's value contains a semicolon (due to argument expansion). + +## Prohibited commands list + +As mentioned above, using directory properties is brittle, and they are +therefore _not allowed_. The following functions may not appear in any new CMake +code. + +| Command | Alternative | +|-------------------------------------|----------------------------------------------------------------------------------------------------| +| `add_compile_definitions` | Use [`target_compile_definitions`][target_compile_definitions] | +| `add_compile_options` | Use [`target_compile_options`][target_compile_options] | +| `add_definitions` | Use [`target_compile_definitions`][target_compile_definitions] | +| `add_link_options` | Use [`target_link_options`][target_link_options], but prefer not to use either | +| `include_directories` | Use [`target_include_directories`][target_include_directories] | +| `link_directories` | Use [`target_link_libraries`][target_link_libraries] | +| `link_libraries` | Use [`target_link_libraries`][target_link_libraries] | +| `remove_definitions` | [Generator expressions][cmake-genex] in [`target_compile_definitions`][target_compile_definitions] | +| `set_directory_properties` | Use (cache) variables or target properties | +| `set_property(DIRECTORY)` | Use (cache) variables or target properties (custom properties excluded, but require justification) | +| `target_link_libraries(target lib)` | Use [`target_link_libraries`][target_link_libraries] _with a visibility specifier_ (eg. `PRIVATE`) | + +As an example, it was once common practice to write code similar to this: + +```cmake +# WRONG: do not do this +include_directories(include) +add_library(my_lib source1.cpp ..) +``` + +However, this has two major pitfalls. First, it applies to _all_ targets created +in that directory, even those before the call to `include_directories` and those +created in [`include()`][include]-ed CMake files. As CMake files get larger and +more complex, this behavior gets harder to pinpoint. This is particularly vexing +when using the `link_libraries` or `add_definitions` commands. Second, this form +does not provide a way to _propagate_ the include directory to consumers of +`my_lib`. The correct way to do this is: + +```cmake +# CORRECT +add_library(my_lib source1.cpp ...) +target_sources( + my_lib + PUBLIC + FILE_SET HEADERS + BASE_DIRS include + FILES include/header1.h +) +``` + +This is better in many ways. It only affects the target in question. It +propagates the include path to the targets linking to it (via `PUBLIC`). It also +correctly exports the host-filesystem-specific include path when installing or +packaging the target and installs the headers themselves, too. + +If common properties need to be grouped together, use an INTERFACE target +(better) or write a function (worse). + +There are also several functions that are disallowed for other reasons: + +| Command | Reason | Alternative | +|---------------------------------|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------| +| `aux_source_directory` | Interacts poorly with incremental builds and Git | List source files explicitly | +| `build_command` | CTest internal function | Use CTest build-and-test mode via [`CMAKE_CTEST_COMMAND`][cmake_ctest_command] | +| `cmake_host_system_information` | Usually misleading information. | Inspect [toolchain][cmake-toolchains] variables and use generator expressions. | +| `cmake_policy(... OLD)` | OLD policies are deprecated by definition. | Instead, fix the code to work with the new policy. | +| `create_test_sourcelist` | We use our own unit testing solution | See the [adding tests](#adding-tests) section. | +| `define_property` | Adds unnecessary complexity | Use a cache variable. Exceptions under special circumstances. | +| `enable_language` | Halide is C/C++ only | [`FindCUDAToolkit`][FindCUDAToolkit], appropriately guarded. | +| `file(GLOB ...)` | Interacts poorly with incremental builds and Git | List source files explicitly. Allowed if not globbing for source files. | +| `fltk_wrap_ui` | Halide does not use FLTK | None | +| `include_external_msproject` | Halide must remain portable | Write a CMake package config file or find module. | +| `include_guard` | Use of recursive inclusion is not allowed | Write (recursive) functions. | +| `include_regular_expression` | Changes default dependency checking behavior | None | +| `load_cache` | Superseded by [`FetchContent`][FetchContent]/[`ExternalProject`][ExternalProject] | Use aforementioned modules | +| `macro` | CMake macros are not hygienic and are therefore error-prone | Use functions instead. | +| `site_name` | Privacy: do not want leak host name information | Provide a cache variable, generate a unique name. | +| `variable_watch` | Debugging helper | None. Not needed in production. | + +Do not introduce any dependencies via [`find_package`][find_package] +without broader approval. Importantly, never introduce a new use of +`FetchContent`; prefer to add dependencies to `vcpkg.json`. + +## Prohibited variables list + +Any variables that are specific to languages that are not enabled should, of +course, be avoided. But of greater concern are variables that are easy to misuse +or should not be overridden for our end-users. The following (non-exhaustive) +list of variables shall not be used in code merged into main. + +| Variable | Reason | Alternative | +|---------------------------------|-----------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `CMAKE_ROOT` | Code smell | Rely on `find_package` search options; include `HINTS` if necessary | +| `CMAKE_DEBUG_TARGET_PROPERTIES` | Debugging helper | None | +| `CMAKE_FIND_DEBUG_MODE` | Debugging helper | None | +| `CMAKE_RULE_MESSAGES` | Debugging helper | None | +| `CMAKE_VERBOSE_MAKEFILE` | Debugging helper | None | +| `CMAKE_BACKWARDS_COMPATIBILITY` | Deprecated | None | +| `CMAKE_BUILD_TOOL` | Deprecated | `${CMAKE_COMMAND} --build` or [`CMAKE_MAKE_PROGRAM`][cmake_make_program] (but see below) | +| `CMAKE_CACHEFILE_DIR` | Deprecated | [`CMAKE_BINARY_DIR`][cmake_binary_dir], but see below | +| `CMAKE_CFG_INTDIR` | Deprecated | `$`, `$`, target resolution of [`add_custom_command`][add_custom_command], etc. | +| `CMAKE_CL_64` | Deprecated | [`CMAKE_SIZEOF_VOID_P`][cmake_sizeof_void_p] | +| `CMAKE_COMPILER_IS_*` | Deprecated | [`CMAKE__COMPILER_ID`][cmake_lang_compiler_id] | +| `CMAKE_HOME_DIRECTORY` | Deprecated | [`CMAKE_SOURCE_DIR`][cmake_source_dir], but see below | +| `CMAKE_DIRECTORY_LABELS` | Directory property | None | +| `CMAKE_BUILD_TYPE` | Only applies to single-config generators. | `$` | +| `CMAKE_*_FLAGS*` (w/o `_INIT`) | User-only | Write a [toolchain][cmake-toolchains] file with the corresponding `_INIT` variable | +| `CMAKE_COLOR_MAKEFILE` | User-only | None | +| `CMAKE_ERROR_DEPRECATED` | User-only | None | +| `CMAKE_CONFIGURATION_TYPES` | We only support the four standard build types | None | + +Of course feel free to insert debugging helpers _while developing_ but please +remove them before review. Finally, the following variables are allowed, but +their use must be motivated: + +| Variable | Reason | Alternative | +|------------------------------------------------|-----------------------------------------------------|----------------------------------------------------------------------------------------------| +| [`CMAKE_SOURCE_DIR`][cmake_source_dir] | Points to global source root, not Halide's. | [`Halide_SOURCE_DIR`][project-name_source_dir] or [`PROJECT_SOURCE_DIR`][project_source_dir] | +| [`CMAKE_BINARY_DIR`][cmake_binary_dir] | Points to global build root, not Halide's | [`Halide_BINARY_DIR`][project-name_binary_dir] or [`PROJECT_BINARY_DIR`][project_binary_dir] | +| [`CMAKE_MAKE_PROGRAM`][cmake_make_program] | CMake abstracts over differences in the build tool. | Prefer CTest's build and test mode or CMake's `--build` mode | +| [`CMAKE_CROSSCOMPILING`][cmake_crosscompiling] | Often misleading. | Inspect relevant variables directly, eg. [`CMAKE_SYSTEM_NAME`][cmake_system_name] | +| [`BUILD_SHARED_LIBS`][build_shared_libs] | Could override user setting | None, but be careful to restore value when overriding for a dependency | + +Any use of these functions or variables will block a PR. + +# Adding tests + +When adding a file to any of the folders under `test`, be aware that CI expects +that every `.c` and `.cpp` appears in the `CMakeLists.txt` file _on its own +line_, possibly as a comment. This is to avoid globbing and also to ensure that +added files are not missed. + +For most test types, it should be as simple as adding to the existing lists, +which must remain in alphabetical order. Generator tests are trickier, but +following the existing examples is a safe way to go. + +# Adding apps + +If you're contributing a new app to Halide: great! Thank you! There are a few +guidelines you should follow when writing a new app. + +- Write the app as if it were a top-level project. You should call + `find_package(Halide)` and set the C++ version to 11. +- Call [`enable_testing()`][enable_testing] and add a small test that runs the + app. +- Don't assume your app will have access to a GPU. Write your schedules to be + robust to varying buildbot hardware. +- Don't assume your app will be run on a specific OS, architecture, or bitness. + Write your apps to be robust (ideally efficient) on all supported platforms. +- If you rely on any additional packages, don't include them as `REQUIRED`, + instead test to see if their targets are available and, if not, call + `return()` before creating any targets. In this case, print a + `message(STATUS "[SKIP] ...")`, too. +- Look at the existing apps for examples. +- Test your app with ctest before opening a PR. Apps are built as part of the + test, rather than the main build. + +[BuildingHalideWithCMake.md]: ./BuildingHalideWithCMake.md + +[CodeStyleCMake.md]: ./CodeStyleCMake.md + +[ExternalProject]: https://cmake.org/cmake/help/latest/module/ExternalProject.html + +[FetchContent]: https://cmake.org/cmake/help/latest/module/FetchContent.html + +[FindCUDAToolkit]: https://cmake.org/cmake/help/latest/module/FindCUDAToolkit.html + +[HalideCMakePackage.md]: ./HalideCMakePackage.md + +[add_custom_command]: https://cmake.org/cmake/help/latest/command/add_custom_command.html + +[add_library]: https://cmake.org/cmake/help/latest/command/add_library.html + +[add_subdirectory]: https://cmake.org/cmake/help/latest/command/add_subdirectory.html + +[atlas]: http://math-atlas.sourceforge.net/ + +[brew-cmake]: https://formulae.brew.sh/cask/cmake#default + +[build_shared_libs]: https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html + +[cmake-apt]: https://apt.kitware.com/ + +[cmake-discourse]: https://discourse.cmake.org/ + +[cmake-docs]: https://cmake.org/cmake/help/latest/ + +[cmake-download]: https://cmake.org/download/ + +[cmake-from-source]: https://cmake.org/install/ + +[cmake-genex]: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html + +[cmake-install]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project + +[cmake-propagation]: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#transitive-usage-requirements + +[cmake-toolchains]: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html + +[cmake-user-interaction]: https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables + +[cmake_binary_dir]: https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html + +[cmake_build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html + +[cmake_crosscompiling]: https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING.html + +[cmake_crosscompiling_emulator]: https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING_EMULATOR.html + +[cmake_ctest_command]: https://cmake.org/cmake/help/latest/variable/CMAKE_CTEST_COMMAND.html + +[cmake_current_binary_dir]: https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_BINARY_DIR.html + +[cmake_cxx_extensions]: https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_EXTENSIONS.html + +[cmake_cxx_standard]: https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD.html + +[cmake_cxx_standard_required]: https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD_REQUIRED.html + +[cmake_foreach]: https://cmake.org/cmake/help/latest/command/foreach.html + +[cmake_if]: https://cmake.org/cmake/help/latest/command/if.html + +[cmake_lang_compiler_id]: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html + +[cmake_make_program]: https://cmake.org/cmake/help/latest/variable/CMAKE_MAKE_PROGRAM.html + +[cmake_minimum_required]: https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html + +[cmake_prefix_path]: https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html + +[cmake_presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html + +[cmake_sizeof_void_p]: https://cmake.org/cmake/help/latest/variable/CMAKE_SIZEOF_VOID_P.html + +[cmake_source_dir]: https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html + +[cmake_system_name]: https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html + +[doxygen-download]: https://www.doxygen.nl/download.html + +[doxygen]: https://www.doxygen.nl/index.html + +[eigen]: http://eigen.tuxfamily.org/index.php?title=Main_Page + +[enable_testing]: https://cmake.org/cmake/help/latest/command/enable_testing.html + +[find_package]: https://cmake.org/cmake/help/latest/command/find_package.html + +[findcuda]: https://cmake.org/cmake/help/latest/module/FindCUDA.html + +[finddoxygen]: https://cmake.org/cmake/help/latest/module/FindDoxygen.html + +[findjpeg]: https://cmake.org/cmake/help/latest/module/FindJPEG.html + +[findopencl]: https://cmake.org/cmake/help/latest/module/FindOpenCL.html + +[findpng]: https://cmake.org/cmake/help/latest/module/FindPNG.html + +[findpython3]: https://cmake.org/cmake/help/latest/module/FindPython3.html + +[findx11]: https://cmake.org/cmake/help/latest/module/FindX11.html + +[halide-generator-tutorial]: https://halide-lang.org/tutorials/tutorial_lesson_15_generators.html + +[halide-tutorials]: https://halide-lang.org/tutorials/tutorial_introduction.html + +[homebrew]: https://brew.sh + +[imported-executable]: https://cmake.org/cmake/help/latest/command/add_executable.html#imported-executables + +[imported-target]: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#imported-targets + +[include]: https://cmake.org/cmake/help/latest/command/include.html + +[install-files]: https://cmake.org/cmake/help/latest/command/install.html#files + +[install-targets]: https://cmake.org/cmake/help/latest/command/install.html#targets + +[libjpeg]: https://www.libjpeg-turbo.org/ + +[libpng]: http://www.libpng.org/pub/png/libpng.html + +[lld]: https://lld.llvm.org/ + +[msvc-cmd]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line + +[msvc]: https://cmake.org/cmake/help/latest/variable/MSVC.html + +[ninja-download]: https://github.com/ninja-build/ninja/releases + +[ninja]: https://ninja-build.org/ + +[openblas]: https://www.openblas.net/ + +[project-name_binary_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT-NAME_BINARY_DIR.html + +[project-name_source_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT-NAME_SOURCE_DIR.html + +[project]: https://cmake.org/cmake/help/latest/command/project.html + +[project_binary_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT_BINARY_DIR.html + +[project_source_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT_SOURCE_DIR.html + +[pypi-cmake]: https://pypi.org/project/cmake/ + +[python]: https://www.python.org/downloads/ + +[target-file]: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#target-dependent-queries + +[target_compile_definitions]: https://cmake.org/cmake/help/latest/command/target_compile_definitions.html + +[target_compile_options]: https://cmake.org/cmake/help/latest/command/target_compile_options.html + +[target_include_directories]: https://cmake.org/cmake/help/latest/command/target_include_directories.html + +[target_link_libraries]: https://cmake.org/cmake/help/latest/command/target_link_libraries.html + +[target_link_options]: https://cmake.org/cmake/help/latest/command/target_link_options.html + +[vcpkg]: https://github.com/Microsoft/vcpkg + +[vcvarsall]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line#vcvarsall-syntax + +[venv]: https://docs.python.org/3/tutorial/venv.html + +[win32]: https://cmake.org/cmake/help/latest/variable/WIN32.html diff --git a/README_fuzz_testing.md b/doc/FuzzTesting.md similarity index 100% rename from README_fuzz_testing.md rename to doc/FuzzTesting.md diff --git a/doc/HalideCMakePackage.md b/doc/HalideCMakePackage.md new file mode 100644 index 000000000000..c1814f7f954c --- /dev/null +++ b/doc/HalideCMakePackage.md @@ -0,0 +1,808 @@ +# Using Halide from your CMake build + +This is a detailed guide to building your own Halide programs with the official +CMake package. If you need directions for building Halide, +see [BuildingHalideWithCMake.md]. If you are looking for Halide's CMake coding +guidelines, see [CodeStyleCMake.md]. + +This document assumes some basic familiarity with CMake but tries to be explicit +in all its examples. To learn more about CMake, consult the +[documentation][cmake-docs] and engage with the community on +the [CMake Discourse][cmake-discourse]. + + +* [Using Halide from your CMake build](#using-halide-from-your-cmake-build) +* [A basic CMake project](#a-basic-cmake-project) + * [JIT mode](#jit-mode) + * [AOT mode](#aot-mode) + * [Autoschedulers](#autoschedulers) + * [RunGenMain](#rungenmain) +* [Halide package documentation](#halide-package-documentation) + * [Components](#components) + * [Variables](#variables) + * [Imported targets](#imported-targets) + * [Functions](#functions) + * [`add_halide_generator`](#add_halide_generator) + * [`add_halide_library`](#add_halide_library) + * [`add_halide_python_extension_library`](#add_halide_python_extension_library) + * [`add_halide_runtime`](#add_halide_runtime) +* [Cross compiling](#cross-compiling) + * [Use `add_halide_generator`](#use-add_halide_generator) + * [Use a super-build](#use-a-super-build) + * [Use `ExternalProject` directly](#use-externalproject-directly) + * [Use an emulator or run on device](#use-an-emulator-or-run-on-device) + * [Bypass CMake](#bypass-cmake) + + +# A basic CMake project + +There are two main ways to use Halide in your application: as a **JIT compiler** +for dynamic pipelines or an **ahead-of-time (AOT) compiler** for static +pipelines. CMake provides robust support for both use cases. + +No matter how you intend to use Halide, you will need some basic CMake +boilerplate. + +```cmake +cmake_minimum_required(VERSION 3.28) +project(HalideExample) + +set(CMAKE_CXX_STANDARD 17) # or newer +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +find_package(Halide REQUIRED) +``` + +The [`cmake_minimum_required`][cmake_minimum_required] command is required to be +the first command executed in a CMake program. It disables all the deprecated +behavior ("policies" in CMake lingo) from earlier versions. The +[`project`][project] command sets the name of the project (and accepts arguments +for versioning, language support, etc.) and is required by CMake to be called +immediately after setting the minimum version. + +The next three variables set the project-wide C++ standard. The first, +[`CMAKE_CXX_STANDARD`][cmake_cxx_standard], simply sets the standard version. +Halide requires at least C++17. The second, +[`CMAKE_CXX_STANDARD_REQUIRED`][cmake_cxx_standard_required], tells CMake to +fail if the compiler cannot provide the requested standard version. Lastly, +[`CMAKE_CXX_EXTENSIONS`][cmake_cxx_extensions] tells CMake to disable +vendor-specific extensions to C++. This is not necessary to simply use Halide, +but we do not allow such extensions in the Halide repo. + +Finally, we use [`find_package`][find_package] to locate Halide on your system. +If Halide is not globally installed, you will need to add the root of the Halide +installation directory to [`CMAKE_PREFIX_PATH`][cmake_prefix_path] at the CMake +command line. + +```shell +$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="/path/to/Halide-install" +``` + +## JIT mode + +To use Halide in JIT mode (like the [tutorials][halide-tutorials] do, for +example), you can simply link to `Halide::Halide`. + +```cmake +# ... same project setup as before ... +add_executable(my_halide_app main.cpp) +target_link_libraries(my_halide_app PRIVATE Halide::Halide) +``` + +Then `Halide.h` will be available to your code and everything should just work. +That's it! + +## AOT mode + +Using Halide in AOT mode is more complicated so we'll walk through it step by +step. Note that this only applies to Halide generators, so it might be useful to +re-read the [tutorial on generators][halide-generator-tutorial]. Assume (like in +the tutorial) that you have a source file named `my_generators.cpp` and that in +it, you have generator classes `MyFirstGenerator` and `MySecondGenerator` with +registered names `my_first_generator` and `my_second_generator` respectively. + +Then the first step is to add a **generator executable** to your build: + +```cmake +# ... same project setup as before ... +add_halide_generator(my_generators SOURCES my_generators.cpp) +``` + +Using the generator executable, we can add a Halide library corresponding to +`MyFirstGenerator`. + +```cmake +# ... continuing from above +add_halide_library(my_first_generator FROM my_generators) +``` + +This will create a static library target in CMake that corresponds to the output +of running your generator. The second generator in the file requires generator +parameters to be passed to it. These are also easy to handle: + +```cmake +# ... continuing from above +add_halide_library(my_second_generator FROM my_generators + PARAMS parallel=false scale=3.0 rotation=ccw output.type=uint16) +``` + +Adding multiple configurations is easy, too: + +```cmake +# ... continuing from above +add_halide_library(my_second_generator_2 FROM my_generators + GENERATOR my_second_generator + PARAMS scale=9.0 rotation=ccw output.type=float32) + +add_halide_library(my_second_generator_3 FROM my_generators + GENERATOR my_second_generator + PARAMS parallel=false output.type=float64) +``` + +Here, we had to specify which generator to use (`my_second_generator`) since it +uses the target name by default. The functions in these libraries will be named +after the target names, `my_second_generator_2` and `my_second_generator_3`, by +default, but it is possible to control this via the `FUNCTION_NAME` parameter. + +Each one of these targets, ``, carries an associated `.runtime` +target, which is also a static library containing the Halide runtime. It is +transitively linked through `` to targets that link to ``. On an +operating system like Linux, where weak linking is available, this is not an +issue. However, on Windows, this can fail due to symbol redefinitions. In these +cases, you must declare that two Halide libraries share a runtime, like so: + +```cmake +# ... updating above +add_halide_library(my_second_generator_2 FROM my_generators + GENERATOR my_second_generator + USE_RUNTIME my_first_generator.runtime + PARAMS scale=9.0 rotation=ccw output.type=float32) + +add_halide_library(my_second_generator_3 FROM my_generators + GENERATOR my_second_generator + USE_RUNTIME my_first_generator.runtime + PARAMS parallel=false output.type=float64) +``` + +This will even work correctly when different combinations of targets are +specified for each halide library. A "greatest common denominator" target will +be chosen that is compatible with all of them (or the build will fail). + +### Autoschedulers + +When the autoschedulers are included in the release package, they are very +simple to apply to your own generators. For example, we could update the +definition of the `my_first_generator` library above to use the `Adams2019` +autoscheduler: + +```cmake +add_halide_library(my_second_generator FROM my_generators + AUTOSCHEDULER Halide::Adams2019) +``` + +### RunGenMain + +Halide provides a generic driver for generators to be used during development +for benchmarking and debugging. Suppose you have a generator executable called +`my_gen` and a generator within called `my_filter`. Then you can pass a variable +name to the `REGISTRATION` parameter of `add_halide_library` which will contain +the name of a generated C++ source that should be linked to `Halide::RunGenMain` +and `my_filter`. + +For example: + +```cmake +add_halide_library(my_filter FROM my_gen + REGISTRATION filter_reg_cpp) +add_executable(runner ${filter_reg_cpp}) +target_link_libraries(runner PRIVATE my_filter Halide::RunGenMain) +``` + +Then you can run, debug, and benchmark your generator through the `runner` +executable. Learn how to interact with these executables +in [RunGen.md](./RunGen.md). + +# Halide package documentation + +Halide provides a CMake _package configuration_ module. The intended way to use +the CMake build is to run `find_package(Halide ...)` in your `CMakeLists.txt` +file. Closely read the [`find_package` documentation][find_package] before +proceeding. + +## Components + +The Halide package script understands a handful of optional components when +loading the package. + +First, if you plan to use the Halide Image IO library, you will want to include +the `png` and `jpeg` components when loading Halide. + +Second, Halide releases can contain a variety of configurations: static, shared, +debug, release, etc. CMake handles Debug/Release configurations automatically, +but generally only allows one type of library to be loaded. + +The package understands two components, `static` and `shared`, that specify +which type of library you would like to load. For example, if you want to make +sure that you link against shared Halide, you can write: + +```cmake +find_package(Halide REQUIRED COMPONENTS shared) +``` + +If the shared libraries are not available, this will result in a failure. + +If no component is specified, then the `Halide_SHARED_LIBS` variable is checked. +If it is defined and set to true, then the shared libraries will be loaded or +the package loading will fail. Similarly, if it is defined and set to false, the +static libraries will be loaded. + +If no component is specified and `Halide_SHARED_LIBS` is _not_ defined, then the +[`BUILD_SHARED_LIBS`][build_shared_libs] variable will be inspected. If it is +**not defined** or **defined and set to true**, then it will attempt to load the +shared libs and fall back to the static libs if they are not available. +Similarly, if `BUILD_SHARED_LIBS` is **defined and set to false**, then it will +try the static libs first then fall back to the shared libs. + +To ensure that the Python bindings are available, include the `Python` +component. + +## Variables + +Variables that control package loading: + +| Variable | Description | +|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Halide_SHARED_LIBS` | override `BUILD_SHARED_LIBS` when loading the Halide package via `find_package`. Has no effect when using Halide via `add_subdirectory` as a Git or `FetchContent` submodule. | +| `Halide_RUNTIME_NO_THREADS` | skip linking of Threads library to runtime. Should be set if your toolchain does not support it (e.g. baremetal). | +| `Halide_RUNTIME_NO_DL_LIBS` | skip linking of DL library to runtime. Should be set if your toolchain does not support it (e.g. baremetal). | + +Variables set by the package: + +| Variable | Description | +|----------------------------|--------------------------------------------------------------------| +| `Halide_VERSION` | The full version string of the loaded Halide package | +| `Halide_VERSION_MAJOR` | The major version of the loaded Halide package | +| `Halide_VERSION_MINOR` | The minor version of the loaded Halide package | +| `Halide_VERSION_PATCH` | The patch version of the loaded Halide package | +| `Halide_VERSION_TWEAK` | The tweak version of the loaded Halide package | +| `Halide_HOST_TARGET` | The Halide target triple corresponding to "host" for this build. | +| `Halide_CMAKE_TARGET` | The Halide target triple corresponding to the active CMake target. | +| `Halide_ENABLE_EXCEPTIONS` | Whether Halide was compiled with exception support | +| `Halide_ENABLE_RTTI` | Whether Halide was compiled with RTTI | +| `WITH_AUTOSCHEDULERS` | Whether the autoschedulers are available | + +Variables that control package behavior: + +| Variable | Description | +|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| `Halide_PYTHON_LAUNCHER` | Semicolon separated list containing a command to launch the Python interpreter. Can be used to set environment variables for Python generators. | +| `Halide_NO_DEFAULT_FLAGS` | Off by default. When enabled, suppresses recommended compiler flags that would be added by `add_halide_generator` | + +## Imported targets + +Halide defines the following targets that are available to users: + +| Imported target | Description | +|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Halide::Halide` | this is the JIT-mode library to use when using Halide from C++. | +| `Halide::Generator` | this is the target to use when manually defining a generator executable. It supplies a `main()` function. | +| `Halide::Runtime` | adds include paths to the Halide runtime headers | +| `Halide::Tools` | adds include paths to the Halide tools, including the benchmarking utility. | +| `Halide::ImageIO` | adds include paths to the Halide image IO utility. Depends on `PNG::PNG` and `JPEG::JPEG` if they exist or were loaded through the corresponding package components. | +| `Halide::ThreadPool` | adds include paths to the Halide _simple_ thread pool utility library. This is not the same as the runtime's thread pool and is intended only for use by tests. Depends on `Threads::Threads`. | +| `Halide::RunGenMain` | used with the `REGISTRATION` parameter of `add_halide_library` to create simple runners and benchmarking tools for Halide libraries. | + +The following targets only guaranteed when requesting the `Python` component +(`Halide_Python_FOUND` will be true): + +| Imported target | Description | +|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Halide::Python` | this is a Python 3 package that can be referenced as `$/..` when setting up `PYTHONPATH` for Python tests or the like from CMake. | + +The following targets only guaranteed when `WITH_AUTOSCHEDULERS` is true: + +| Imported target | Description | +|-------------------------|-----------------------------------------------------------------| +| `Halide::Adams2019` | the Adams et.al. 2019 autoscheduler (no GPU support) | +| `Halide::Anderson2021` | the Anderson, et.al. 2021 autoscheduler (full GPU support) | +| `Halide::Li2018` | the Li et.al. 2018 gradient autoscheduler (limited GPU support) | +| `Halide::Mullapudi2016` | the Mullapudi et.al. 2016 autoscheduler (no GPU support) | + +## Functions + +The Halide package provides several useful functions for dealing with AOT +compilation steps. + +### `add_halide_generator` + +This function aids in creating cross-compilable builds that use Halide +generators. + +``` +add_halide_generator( + target + [PACKAGE_NAME package-name] + [PACKAGE_NAMESPACE namespace] + [EXPORT_FILE export-file] + [PYSTUB generator-name] + [LINK_LIBRARIES lib1 ...] + [[SOURCES] source1 ...] +) +``` + +Every named argument is optional, and the function uses the following default +arguments: + +- If `PACKAGE_NAME` is not provided, it defaults to + `${PROJECT_NAME}-halide_generators`. +- If `PACKAGE_NAMESPACE` is not provided, it defaults to + `${PROJECT_NAME}::halide_generators::`. +- If `EXPORT_FILE` is not provided, it defaults to + `${PROJECT_BINARY_DIR}/cmake/${ARG_PACKAGE_NAME}-config.cmake` + +This function guarantees that a Halide generator target named +`` is available. It will first search for a package named +`` using `find_package`; if it is found, it is assumed that it +provides the target. Otherwise, it will create an executable target named +`target` and an `ALIAS` target ``. This function also creates +a custom target named `` if it does not exist and +`` would exist. In this case, `` will depend on +``, this enables easy building of _just_ the Halide generators managed +by this function. + +After the call, `_FOUND` will be set to true if the host +generators were imported (and hence won't be built). Otherwise, it will be set +to false. This variable may be used to conditionally set properties on +``. + +Please +see [test/integration/xc](https://github.com/halide/Halide/tree/main/test/integration/xc) +for a simple example +and [apps/hannk](https://github.com/halide/Halide/tree/main/apps/hannk) for a +complete app that uses it extensively. + +The `SOURCES` keyword marks the beginning of sources to be used to build +``, if it is not loaded. All unparsed arguments will be interpreted as +sources. + +The `LINK_LIBRARIES` argument lists libraries that should be linked to +`` when it is being built in the present build system. + +If `PYSTUB` is specified, then a Python Extension will be built that wraps the +Generator with CPython glue to allow use of the Generator Python 3. The result +will be a shared library of the form +`_pystub..so`, where `` describes the specific Python +version and platform (e.g., `cpython-310-darwin` for Python 3.10 on macOS). See +[Python.md](Python.md) for examples of use. + +### `add_halide_library` + +This is the main function for managing generators in AOT compilation. The full +signature follows: + +``` +add_halide_library( FROM + [GENERATOR generator-name] + [FUNCTION_NAME function-name] + [NAMESPACE cpp-namespace] + [USE_RUNTIME hl-target] + [PARAMS param1 [param2 ...]] + [TARGETS target1 [target2 ...]] + [FEATURES feature1 [feature2 ...]] + [FEATURES[] feature1 [feature2 ...]] + [PLUGINS plugin1 [plugin2 ...]] + [AUTOSCHEDULER scheduler-name] + [FUNCTION_INFO_HEADER OUTVAR] + [HEADER OUTVAR] + [REGISTRATION OUTVAR] + [ OUTVAR] + [GRADIENT_DESCENT] + [C_BACKEND] + [NO_THREADS] + [NO_DL_LIBS]) + +triple = -- +arch = x86 | arm | powerpc | hexagon | wasm | riscv +bits = 32 | 64 +os = linux | windows | osx | android | ios | qurt | noos | fuchsia | wasmrt + +extra-output = ASSEMBLY | BITCODE | COMPILER_LOG | C_SOURCE | FEATURIZATION + | HLPIPE | LLVM_ASSEMBLY | PYTHON_EXTENSION | PYTORCH_WRAPPER + | SCHEDULE | STMT | STMT_HTML +``` + +This function creates a called `` corresponding to running the +`` (an executable target which links to `Halide::Generator`) +one time, using command line arguments derived from the other parameters. + +The arguments `GENERATOR` and `FUNCTION_NAME` default to ``. They +correspond to the `-g` and `-f` command line flags, respectively. + +`NAMESPACE` is syntactic sugar to specify the C++ namespace (if any) of the +generated function; you can also specify the C++ namespace (if any) directly in +the `FUNCTION_NAME` argument, but for repeated declarations or very long +namespaces, specifying this separately can provide more readable build files. + +If `USE_RUNTIME` is not specified, this function will create another target +called `.runtime` which corresponds to running the generator with `-r` +and a compatible list of targets. This runtime target is an `INTERFACE` +dependency of ``. If multiple runtime targets need to be linked +together, setting `USE_RUNTIME` to another Halide runtime library, `` +will prevent the generation of `.runtime` and instead use +`.runtime`. This argument is most commonly used in conjunction with [ +`add_halide_runtime`](#add_halide_runtime). + +Parameters can be passed to a generator via the `PARAMS` argument. Parameters +should be space-separated. Similarly, `TARGETS` is a space-separated list of +targets for which to generate code in a single function. They must all share the +same platform/bits/os triple (e.g. `arm-32-linux`). Features that are in common +among all targets, including device libraries (like `cuda`) should go in +`FEATURES`. If `TARGETS` is not specified, the value of `Halide_TARGET` +specified at configure time will be used. + +Every element of `TARGETS` must begin with the same `arch-bits-os` triple. This +function understands two _meta-triples_, `host` and `cmake`. The meta-triple +`host` is equal to the `arch-bits-os` triple used to compile Halide along with +all the supported instruction set extensions. On platforms that support running +both 32 and 64-bit programs, this will not necessarily equal the platform the +compiler is running on or that CMake is targeting. + +The meta-triple `cmake` is equal to the `arch-bits-os` of the current CMake +target. This is useful if you want to make sure you are not unintentionally +cross-compiling, which would result in an [`IMPORTED` target][imported-target] +being created. When `TARGETS` is empty and the `host` target would not +cross-compile, then `host` will be used. Otherwise, `cmake` will be used and an +author warning will be issued. + +When `CMAKE_OSX_ARCHITECTURES` is set and the `TARGETS` argument resolves to +`cmake`, the generator will be run once for each architecture and the results +will be fused together using `lipo`. This behavior extends to runtime targets. + +To use an autoscheduler, set the `AUTOSCHEDULER` argument to a target named like +`Namespace::Scheduler`, for example `Halide::Adams2019`. This will set the +`autoscheduler` GeneratorParam on the generator command line to `Scheduler` +and add the target to the list of plugins. Additional plugins can be loaded by +setting the `PLUGINS` argument. If the argument to `AUTOSCHEDULER` does not +contain `::` or it does not name a target, it will be passed to the `-s` flag +verbatim. + +If `GRADIENT_DESCENT` is set, then the module will be built suitably for +gradient descent calculation in TensorFlow or PyTorch. See +`Generator::build_gradient_module()` for more documentation. This corresponds to +passing `-d 1` at the generator command line. + +If the `C_BACKEND` option is set, this command will invoke the configured C++ +compiler on a generated source. Note that a `.runtime` target is _not_ +created in this case, and the `USE_RUNTIME` option is ignored. Other options +work as expected. + +If `REGISTRATION` is set, the path (relative to `CMAKE_CURRENT_BINARY_DIR`) +to the generated `.registration.cpp` file will be set in `OUTVAR`. This can be +used to generate a runner for a Halide library that is useful for benchmarking +and testing, as documented above. This is equivalent to setting +`-e registration` at the generator command line. + +If `HEADER` is set, the path (relative to `CMAKE_CURRENT_BINARY_DIR`) to the +generated `.h` header file will be set in `OUTVAR`. This can be used with +`install(FILES)` to conveniently deploy the generated header along with your +library. + +If `FUNCTION_INFO_HEADER` is set, the path (relative to +`CMAKE_CURRENT_BINARY_DIR`) to the generated `.function_info.h` header file will +be set in `OUTVAR`. This produces a file that contains `constexpr` +descriptions of information about the generated functions (e.g., argument type +and information). It is generated separately from the normal `HEADER` +file because `HEADER` is intended to work with basic `extern "C"` linkage, while +`FUNCTION_INFO_HEADER` requires C++17 or later to use effectively. +(This can be quite useful for advanced usages, such as producing automatic call +wrappers, etc.) Examples of usage can be found in the generated file. + +Each of the `extra-output` arguments directly correspond to an extra output (via +`-e`) from the generator. The value `OUTVAR` names a variable into which a +path (relative to +[`CMAKE_CURRENT_BINARY_DIR`][cmake_current_binary_dir]) to the extra file will +be written. + +When `NO_THREADS` is passed, the library targets will not depend on +`Threads::Threads`. It is your responsibility to link to an equivalent target. + +When `NO_DL_LIBS` is passed, the library targets will not depend on +`${CMAKE_DL_LIBS}`. It is your responsibility to link to an equivalent library. + +### `add_halide_python_extension_library` + +This function wraps the outputs of one or more `add_halide_library` targets with +glue code to produce a Python Extension library. + +``` +add_halide_python_extension_library( + target + [MODULE_NAME module-name] + HALIDE_LIBRARIES library1 ... +) +``` + +`HALIDE_LIBRARIES` is a list of one of more `add_halide_library` targets. Each +will be added to the extension as a callable method of the module. Note that +every library specified must be built with the `PYTHON_EXTENSION` keyword +specified, and all libraries must use the same Halide runtime. + +The result will be a shared library of the form +`..so`, where describes the specific Python version and +platform (e.g., `cpython-310-darwin` for Python 3.10 on macOS.) + +### `add_halide_runtime` + +This function generates a library containing a Halide runtime. Most user code +will never need to use this, as `add_halide_library()` will call it for you if +necessary. The most common use case is usually in conjunction with +`add_halide_python_extension_library()`, as a way to ensure that all the halide +libraries share an identical runtime. + +``` +add_halide_runtime( + target + [TARGETS target1 [target2 ...]] + [NO_THREADS] + [NO_DL_LIBS] +) +``` + +The `TARGETS`, `NO_THREADS`, and `NO_DL_LIBS` arguments have identical semantics +to the argument of the same name for [ +`add_halide_library`](#add_halide_library). + +# Cross compiling + +Cross-compiling in CMake can be tricky, since CMake doesn't easily support +compiling for both the host platform and the cross platform within the same +build. Unfortunately, Halide generator executables are just about always +designed to run on the host platform. Each project will be set up differently +and have different requirements, but here are some suggestions for effective use +of CMake in these scenarios. + +## Use `add_halide_generator` + +If you are writing new programs that use Halide, you might wish to use +`add_halide_generator`. When using this helper, you are expected to build your +project twice: once for your build host and again for your intended target. + +When building the host build, you can use the `` (see the +documentation above) target to build _just_ the generators. Then, in the target +build, set `_ROOT` to the host build directory. + +For example: + +``` +$ cmake -G Ninja -S . -B build-host -DCMAKE_BUILD_TYPE=Release +$ cmake --build build-host --target +$ cmake -G Ninja -S . -B build-target --toolchain /path/to/target-tc.cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -D_ROOT:FILEPATH=$PWD/build-host +$ cmake --build build-target +``` + +## Use a super-build + +A CMake super-build consists of breaking down a project into subprojects that +are isolated by [toolchain][cmake-toolchains]. The basic structure is to have an +outermost project that only coordinates the sub-builds via the +[`ExternalProject`][ExternalProject] module. + +One would then use Halide to build a generator executable in one self-contained +project, then export that target to be used in a separate project. The second +project would be configured with the target [toolchain][cmake-toolchains] and +would call `add_halide_library` with no `TARGETS` option and set `FROM` equal to +the name of the imported generator executable. Obviously, this is a significant +increase in complexity over a typical CMake project. + +This is very compatible with the `add_halide_generator` strategy above. + +## Use `ExternalProject` directly + +A lighter weight alternative to the above is to use +[`ExternalProject`][ExternalProject] directly in your parent build. Configure +the parent build with the target [toolchain][cmake-toolchains], and configure +the inner project to use the host toolchain. Then, manually create an +[`IMPORTED` target][imported-executable] for your generator executable and call +`add_halide_library` as described above. + +The main drawback of this approach is that creating accurate `IMPORTED` targets +is difficult since predicting the names and locations of your binaries across +all possible platform and CMake project generators is difficult. In particular, +it is hard to predict executable extensions in cross-OS builds. + +## Use an emulator or run on device + +The [`CMAKE_CROSSCOMPILING_EMULATOR`][cmake_crosscompiling_emulator] variable +allows one to specify a command _prefix_ to run a target-system binary on the +host machine. One could set this to a custom shell script that uploads the +generator executable, runs it on the device and copies back the results. + +Another option is to install `qemu-user-static` to transparently emulate the +cross-built generator. + +## Bypass CMake + +The previous two options ensure that the targets generated by +`add_halide_library` will be _normal_ static libraries. This approach does not +use [`ExternalProject`][ExternalProject], but instead produces `IMPORTED` +targets. The main drawback of `IMPORTED` targets is that they are considered +second-class in CMake. In particular, they cannot be installed with the typical +[`install(TARGETS)` command][install-targets]. Instead, they must be installed +using [`install(FILES)`][install-files] and the +[`$`][target-file] generator expression. + + +[BuildingHalideWithCMake.md]: ./BuildingHalideWithCMake.md + +[CodeStyleCMake.md]: ./CodeStyleCMake.md + +[ExternalProject]: https://cmake.org/cmake/help/latest/module/ExternalProject.html + +[HalideCMakePackage.md]: ./HalideCMakePackage.md + +[add_custom_command]: https://cmake.org/cmake/help/latest/command/add_custom_command.html + +[add_library]: https://cmake.org/cmake/help/latest/command/add_library.html + +[add_subdirectory]: https://cmake.org/cmake/help/latest/command/add_subdirectory.html + +[atlas]: http://math-atlas.sourceforge.net/ + +[brew-cmake]: https://formulae.brew.sh/cask/cmake#default + +[build_shared_libs]: https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html + +[cmake-apt]: https://apt.kitware.com/ + +[cmake-discourse]: https://discourse.cmake.org/ + +[cmake-docs]: https://cmake.org/cmake/help/latest/ + +[cmake-download]: https://cmake.org/download/ + +[cmake-from-source]: https://cmake.org/install/ + +[cmake-genex]: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html + +[cmake-install]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project + +[cmake-propagation]: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#transitive-usage-requirements + +[cmake-toolchains]: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html + +[cmake-user-interaction]: https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables + +[cmake_binary_dir]: https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html + +[cmake_build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html + +[cmake_crosscompiling]: https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING.html + +[cmake_crosscompiling_emulator]: https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING_EMULATOR.html + +[cmake_ctest_command]: https://cmake.org/cmake/help/latest/variable/CMAKE_CTEST_COMMAND.html + +[cmake_current_binary_dir]: https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_BINARY_DIR.html + +[cmake_cxx_extensions]: https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_EXTENSIONS.html + +[cmake_cxx_standard]: https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD.html + +[cmake_cxx_standard_required]: https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD_REQUIRED.html + +[cmake_foreach]: https://cmake.org/cmake/help/latest/command/foreach.html + +[cmake_if]: https://cmake.org/cmake/help/latest/command/if.html + +[cmake_lang_compiler_id]: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html + +[cmake_make_program]: https://cmake.org/cmake/help/latest/variable/CMAKE_MAKE_PROGRAM.html + +[cmake_minimum_required]: https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html + +[cmake_prefix_path]: https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html + +[cmake_presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html + +[cmake_sizeof_void_p]: https://cmake.org/cmake/help/latest/variable/CMAKE_SIZEOF_VOID_P.html + +[cmake_source_dir]: https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html + +[cmake_system_name]: https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html + +[doxygen-download]: https://www.doxygen.nl/download.html + +[doxygen]: https://www.doxygen.nl/index.html + +[eigen]: http://eigen.tuxfamily.org/index.php?title=Main_Page + +[enable_testing]: https://cmake.org/cmake/help/latest/command/enable_testing.html + +[fetchcontent]: https://cmake.org/cmake/help/latest/module/FetchContent.html + +[find_package]: https://cmake.org/cmake/help/latest/command/find_package.html + +[findcuda]: https://cmake.org/cmake/help/latest/module/FindCUDA.html + +[findcudatoolkit]: https://cmake.org/cmake/help/latest/module/FindCUDAToolkit.html + +[finddoxygen]: https://cmake.org/cmake/help/latest/module/FindDoxygen.html + +[findjpeg]: https://cmake.org/cmake/help/latest/module/FindJPEG.html + +[findopencl]: https://cmake.org/cmake/help/latest/module/FindOpenCL.html + +[findpng]: https://cmake.org/cmake/help/latest/module/FindPNG.html + +[findpython3]: https://cmake.org/cmake/help/latest/module/FindPython3.html + +[findx11]: https://cmake.org/cmake/help/latest/module/FindX11.html + +[halide-generator-tutorial]: https://halide-lang.org/tutorials/tutorial_lesson_15_generators.html + +[halide-tutorials]: https://halide-lang.org/tutorials/tutorial_introduction.html + +[homebrew]: https://brew.sh + +[imported-executable]: https://cmake.org/cmake/help/latest/command/add_executable.html#imported-executables + +[imported-target]: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#imported-targets + +[include]: https://cmake.org/cmake/help/latest/command/include.html + +[install-files]: https://cmake.org/cmake/help/latest/command/install.html#files + +[install-targets]: https://cmake.org/cmake/help/latest/command/install.html#targets + +[libjpeg]: https://www.libjpeg-turbo.org/ + +[libpng]: http://www.libpng.org/pub/png/libpng.html + +[lld]: https://lld.llvm.org/ + +[msvc-cmd]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line + +[msvc]: https://cmake.org/cmake/help/latest/variable/MSVC.html + +[ninja-download]: https://github.com/ninja-build/ninja/releases + +[ninja]: https://ninja-build.org/ + +[openblas]: https://www.openblas.net/ + +[project-name_binary_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT-NAME_BINARY_DIR.html + +[project-name_source_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT-NAME_SOURCE_DIR.html + +[project]: https://cmake.org/cmake/help/latest/command/project.html + +[project_binary_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT_BINARY_DIR.html + +[project_source_dir]: https://cmake.org/cmake/help/latest/variable/PROJECT_SOURCE_DIR.html + +[pypi-cmake]: https://pypi.org/project/cmake/ + +[python]: https://www.python.org/downloads/ + +[target-file]: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#target-dependent-queries + +[target_compile_definitions]: https://cmake.org/cmake/help/latest/command/target_compile_definitions.html + +[target_compile_options]: https://cmake.org/cmake/help/latest/command/target_compile_options.html + +[target_include_directories]: https://cmake.org/cmake/help/latest/command/target_include_directories.html + +[target_link_libraries]: https://cmake.org/cmake/help/latest/command/target_link_libraries.html + +[target_link_options]: https://cmake.org/cmake/help/latest/command/target_link_options.html + +[vcpkg]: https://github.com/Microsoft/vcpkg + +[vcvarsall]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line#vcvarsall-syntax + +[venv]: https://docs.python.org/3/tutorial/venv.html + +[win32]: https://cmake.org/cmake/help/latest/variable/WIN32.html diff --git a/doc/Hexagon.md b/doc/Hexagon.md new file mode 100644 index 000000000000..2bcdc8c99c16 --- /dev/null +++ b/doc/Hexagon.md @@ -0,0 +1,73 @@ +# Halide for Hexagon HVX + +Halide supports offloading work to Qualcomm Hexagon DSP on Qualcomm Snapdragon +845/710 devices or newer. The Hexagon DSP provides a set of 128 byte vector +instruction extensions - the Hexagon Vector eXtensions (HVX). HVX is well suited +for image processing, and Halide for Hexagon HVX will generate the appropriate +HVX vector instructions from a program authored in Halide. + +Halide can be used to compile Hexagon object files directly, by using a target +such as `hexagon-32-qurt-hvx`. + +Halide can also be used to offload parts of a pipeline to Hexagon using the +`hexagon` scheduling directive. To enable the `hexagon` scheduling directive, +include the `hvx` target feature in your target. The currently supported +combination of targets is to use the HVX target features with an x86 linux +host (to use the simulator) or with an ARM android target (to use Hexagon DSP +hardware). For examples of using the `hexagon` scheduling directive on both the +simulator and a Hexagon DSP, see the blur example app. + +To build and run an example app using the Hexagon target, + +1. Obtain and build trunk LLVM and Clang. (Earlier versions of LLVM may work but + are not actively tested and thus not recommended.) +2. Download and install the Hexagon SDK and Hexagon Tools. Hexagon SDK 4.3.0 or + later is needed. Hexagon Tools 8.4 or later is needed. +3. Build and run an example for Hexagon HVX + +## 1. Obtain and build trunk LLVM and Clang + +(Follow the instructions given previously, just be sure to check out the `main` +branch.) + +## 2. Download and install the Hexagon SDK and Hexagon Tools + +Go to https://qpm.qualcomm.com/#/main/home + +1. Go to Tools, and download Qualcomm Package Manager 3. Install the package + manager on your machine. +2. Run the installed Qualcomm Package Manager and install the Qualcomm Hexagon + SDK 5.x (or 4.x). The SDK can be selected from the Qualcomm Hexagon SDK + Products. +3. Set an environment variable to point to the SDK installation location + ``` + export SDK_LOC=/location/of/SDK + ``` + +## 3. Build and run an example for Hexagon HVX + +In addition to running Hexagon code on device, Halide also supports running +Hexagon code on the simulator from the Hexagon tools. + +To build and run the blur example in Halide/apps/blur on the simulator: + +``` +cd apps/blur +export HL_HEXAGON_SIM_REMOTE=../../src/runtime/hexagon_remote/bin/v65/hexagon_sim_remote +export HL_HEXAGON_TOOLS=$SDK_LOC/Hexagon_Tools/8.x/Tools/ +LD_LIBRARY_PATH=../../src/runtime/hexagon_remote/bin/host/:$HL_HEXAGON_TOOLS/lib/iss/:. HL_TARGET=host-hvx make test +``` + +## To build and run the blur example in Halide/apps/blur on Android: + +To build the example for Android, first ensure that you have Android NDK r19b or +later installed, and the ANDROID_NDK_ROOT environment variable points to it. +(Note that Qualcomm Hexagon SDK v4.3.0 includes Android NDK r19c, which is +fine.) + +Now build and run the blur example using the script to run it on device: + +``` +export HL_HEXAGON_TOOLS=$SDK_LOC/HEXAGON_Tools/8.4.11/Tools/ +HL_TARGET=arm-64-android-hvx ./adb_run_on_device.sh +``` diff --git a/README_python.md b/doc/Python.md similarity index 56% rename from README_python.md rename to doc/Python.md index 0ac7f94abd96..78828149e896 100644 --- a/README_python.md +++ b/doc/Python.md @@ -1,38 +1,39 @@ # Halide Bindings for Python - - -- [Python Requirements](#python-requirements) -- [Compilation Instructions](#compilation-instructions) -- [Documentation and Examples](#documentation-and-examples) -- [Differences from C++ API](#differences-from-c-api) -- [Example of Simple Usage](#example-of-simple-usage) -- [Halide Generators In Python](#halide-generators-in-python) - - [Writing a Generator in Python](#writing-a-generator-in-python) - - [@hl.generator\("name"\)](#hlgeneratorname) - - [hl.GeneratorParam](#hlgeneratorparam) - - [hl.InputBuffer, hl.InputScalar](#hlinputbuffer-hlinputscalar) - - [hl.OutputBuffer, hl.OutputScalar](#hloutputbuffer-hloutputscalar) - - [Names](#names) - - [generate\(\) method](#generate-method) - - [Types for Inputs and Outputs](#types-for-inputs-and-outputs) - - [Using a Generator for JIT compilation](#using-a-generator-for-jit-compilation) - - [Using a Generator for AOT compilation](#using-a-generator-for-aot-compilation) - - [Calling Generator-Produced code from Python](#calling-generator-produced-code-from-python) - - [Advanced Generator-Related Topics](#advanced-generator-related-topics) - - [Generator Aliases](#generator-aliases) - - [Dynamic Inputs and Outputs](#dynamic-inputs-and-outputs) - - [Calling a Generator Directly](#calling-a-generator-directly) - - [The Lifecycle Of A Generator](#the-lifecycle-of-a-generator) - - [Notable Differences Between C++ and Python Generators](#notable-differences-between-c-and-python-generators) -- [Keeping Up To Date](#keeping-up-to-date) -- [License](#license) - - + +* [Halide Bindings for Python](#halide-bindings-for-python) + * [Acquiring the Python bindings](#acquiring-the-python-bindings) + * [Building the Python bindings](#building-the-python-bindings) + * [Using CMake directly](#using-cmake-directly) + * [Using wheel infrastructure](#using-wheel-infrastructure) + * [Documentation and Examples](#documentation-and-examples) + * [Differences from C++ API](#differences-from-c-api) + * [Example of Simple Usage](#example-of-simple-usage) + * [Halide Generators In Python](#halide-generators-in-python) + * [Writing a Generator in Python](#writing-a-generator-in-python) + * [@hl.generator("name")](#hlgeneratorname) + * [hl.GeneratorParam](#hlgeneratorparam) + * [hl.InputBuffer, hl.InputScalar](#hlinputbuffer-hlinputscalar) + * [hl.OutputBuffer, hl.OutputScalar](#hloutputbuffer-hloutputscalar) + * [Names](#names) + * [generate() method](#generate-method) + * [Types for Inputs and Outputs](#types-for-inputs-and-outputs) + * [Using a Generator for JIT compilation](#using-a-generator-for-jit-compilation) + * [Using a Generator for AOT compilation](#using-a-generator-for-aot-compilation) + * [Calling Generator-Produced code from Python](#calling-generator-produced-code-from-python) + * [Advanced Generator-Related Topics](#advanced-generator-related-topics) + * [Generator Aliases](#generator-aliases) + * [Dynamic Inputs and Outputs](#dynamic-inputs-and-outputs) + * [Calling a Generator Directly](#calling-a-generator-directly) + * [The Lifecycle Of A Generator](#the-lifecycle-of-a-generator) + * [Notable Differences Between C++ and Python Generators](#notable-differences-between-c-and-python-generators) + * [Keeping Up To Date](#keeping-up-to-date) + * [License](#license) + Halide provides Python bindings for most of its public API. Python 3.8 (or -higher) is required. The Python bindings are supported on 64-bit Linux, OSX, -and Windows systems. +higher) is required. The Python bindings are supported on 64-bit Linux, OSX, and +Windows systems. In addition to the ability to write just-in-time Halide code using Python, you can write [Generators](#halide-generators-in-python) using the Python bindings, @@ -42,24 +43,88 @@ is required). You can also use existing Halide Generators (written in either C++ or Python) to produce Python extensions that can be used within Python code. -## Python Requirements +## Acquiring the Python bindings -Before building, you should ensure you have prerequite packages installed in -your local Python environment. The best way to get set up is to use a virtual -environment: +As of Halide 19.0.0, we provide binary wheels on PyPI which include the Python +bindings and the C++/CMake package for native development. Full releases may be +installed with `pip` like so: -```console +```shell +$ pip install halide +``` + +Every commit to `main` is published to Test PyPI as a development version and +these may be installed with a few extra flags: + +```shell +$ pip install halide --pre --extra-index-url https://test.pypi.org/simple +``` + +Currently, we provide wheels for: Windows x86-64, macOS x86-64, macOS arm64, and +Linux x86-64. The Linux wheels are built for manylinux_2_28, which makes them +broadly compatible (Debian 10, Ubuntu 18.10, Fedora 29). + +## Building the Python bindings + +If `pip` isn't enough for your purposes, or you are developing Halide directly, +you have two options for building and using the Python bindings. Note that the +bindings require Halide to be built with RTTI and exceptions **enabled**, which +in turn requires LLVM to be built with RTTI, but this is not the default for +LLVM. + +### Using CMake directly + +Before configuring with CMake, you should ensure you have prerequisite packages +installed in your local Python environment. The best way to get set up is to use +a virtual environment: + +```shell $ python3 -m venv venv $ . venv/bin/activate -$ python3 -m pip install -U setuptools wheel +$ python3 -m pip install -U pip "setuptools[core]" wheel $ python3 -m pip install -r requirements.txt ``` -## Compilation Instructions +Then build and install Halide: + +```shell +$ cmake -G Ninja -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_PYTHON_BINDINGS=ON +$ cmake --build build +$ cmake --install build --prefix .local +``` + +Now you can set the `PYTHONPATH` variable to point to the freshly built Python +package: + +```shell +$ export PYTHONPATH="$PWD/.local/lib/python3/site-packages" +``` + +### Using wheel infrastructure + +You can also follow the same procedure that we use to build the published +wheels. First, create a virtual environment as before, but omit +`requirements.txt` + +```shell +$ python3 -m venv venv +$ . venv/bin/activate +$ python3 -m pip install -U pip "setuptools[core]" wheel +``` + +Next, ensure you have installed Halide's dependencies to locations where CMake +can find them, given your environment. The variables `Halide_LLVM_ROOT`, +`flatbuffers_ROOT`, and `wabt_ROOT` specify locations for the relevant packages +directly. If they are all installed to a common prefix, you can add it to the +environment variable `CMAKE_PREFIX_PATH`. + +Then it should be as simple as: -Build as part of the CMake build with `-DWITH_PYTHON_BINDINGS=ON` (this is the -default). Note that this requires both Halide and LLVM to be built with RTTI and -exceptions **enabled**, which is not the default for LLVM. +```shell +$ pip install . +``` ## Documentation and Examples @@ -84,90 +149,87 @@ from the Halide build directory. The Python bindings attempt to mimic the Halide C++ API as closely as possible, with some differences where the C++ idiom is either inappropriate or impossible: -- Most APIs that take a variadic argument list of ints in C++ take an explicit - list in Python. For instance, the usual version of the `Buffer` ctor in C++ - offers both variadic and list versions: +- Most APIs that take a variadic argument list of ints in C++ take an explicit + list in Python. For instance, the usual version of the `Buffer` ctor in C++ + offers both variadic and list versions: - ``` - Buffer<>(Type t, int extent_dim_0, int extent_dim_1, ...., extent_dim_N, string name = ""); - Buffer<>(Type t, vector extents, string name = ""); - ``` + ```cpp + Buffer<>(Type t, int extent_dim_0, int extent_dim_1, ...., extent_dim_N, string name = ""); + Buffer<>(Type t, vector extents, string name = ""); + ``` - In Python, only the second variant is provided. + In Python, only the second variant is provided. -- `Func` and `Buffer` access is done using `[]` rather than `()` +- `Func` and `Buffer` access is done using `[]` rather than `()` - - For zero-dimensional `Func` and `Buffer`, you must explicitly specify - `[()]` -- that is, use an empty tuple as the index -- because `[]` is - not syntactically acceptable in Python. + - For zero-dimensional `Func` and `Buffer`, you must explicitly specify + `[()]` -- that is, use an empty tuple as the index -- because `[]` is not + syntactically acceptable in Python. -- Some classes in the Halide API aren't provided because standard Python - idioms are a better fit: +- Some classes in the Halide API aren't provided because standard Python idioms + are a better fit: - - `Halide::Tuple` doesn't exist in the Python bindings; an ordinary Python - tuple of `Halide::Expr` is used instead. - - `Halide::Realization` doesn't exist in the Python bindings; an ordinary - Python tuple of `Halide::Buffer` is used instead. + - `Halide::Tuple` doesn't exist in the Python bindings; an ordinary Python + tuple of `Halide::Expr` is used instead. + - `Halide::Realization` doesn't exist in the Python bindings; an ordinary + Python tuple of `Halide::Buffer` is used instead. -- static and instance method overloads with the same name in the same class - aren't allowed, so some convenience methods are missing from `Halide::Var` +- static and instance method overloads with the same name in the same class + aren't allowed, so some convenience methods are missing from `Halide::Var` -- Templated types (notably `Halide::Buffer<>` and `Halide::Param<>`) aren't - provided, for obvious reasons; only the equivalents of - `Halide::Buffer` and `Halide::Param` are supported. +- Templated types (notably `Halide::Buffer<>` and `Halide::Param<>`) aren't + provided, for obvious reasons; only the equivalents of + `Halide::Buffer` and `Halide::Param` are supported. -- The functions in `Halide::ConciseCasts` are present in the toplevel Halide - module in Python, rather than a submodule: e.g., use `halide.i8_sat()`, not - `halide.ConciseCasts.i8_sat()`. +- The functions in `Halide::ConciseCasts` are present in the toplevel Halide + module in Python, rather than a submodule: e.g., use `halide.i8_sat()`, not + `halide.ConciseCasts.i8_sat()`. -- Only things in the `Halide` namespace are supported; classes and methods - that involve using the `Halide::Internal` namespace are not provided. +- Only things in the `Halide` namespace are supported; classes and methods that + involve using the `Halide::Internal` namespace are not provided. -- No mechanism is provided for overriding any runtime functions from Python - for JIT-compiled code. (Runtime functions for AOT-compiled code can be - overridden by building and linking a custom runtime, but not currently - via any runtime API, e.g. halide_set_custom_print() does not exist.) +- No mechanism is provided for overriding any runtime functions from Python for + JIT-compiled code. (Runtime functions for AOT-compiled code can be overridden + by building and linking a custom runtime, but not currently via any runtime + API, e.g. halide_set_custom_print() does not exist.) -- No mechanism is provided for supporting `Func::define_extern`. +- No mechanism is provided for supporting `Func::define_extern`. -- `Buffer::for_each_value()` isn't supported yet. +- `Buffer::for_each_value()` isn't supported yet. -- `Func::in` becomes `Func.in_` because `in` is a Python keyword. +- `Func::in` becomes `Func.in_` because `in` is a Python keyword. -- `Func::async` becomes `Func.async_` because `async` is a Python keyword. +- `Func::async` becomes `Func.async_` because `async` is a Python keyword. -- The `not` keyword cannot be used to negate boolean Halide expressions. - Instead, the `logical_not` function can be used and is equivalent to using - `operator!` in C++. +- The `not` keyword cannot be used to negate boolean Halide expressions. + Instead, the `logical_not` function can be used and is equivalent to using + `operator!` in C++. -- There is no way to override the logical `and`/`or` operators in Python to - work with `Expr`: you must use the bitwise `|` and `&` instead. (Note that - incorrectly using using `and`/`or` just short-circuits weirdly, rather than - failing with some helpful error; this is an issue that we have not yet found - any way to improve, unfortunately.) +- There is no way to override the logical `and`/`or` operators in Python to work + with `Expr`: you must use the bitwise `|` and `&` instead. (Note that + incorrectly using `and`/`or` just short-circuits weirdly, rather than failing + with some helpful error; this is an issue that we have not yet found any way + to improve, unfortunately.) -- Some error messages need to be made more informative. +- Some error messages need to be made more informative. -- Some exceptions are the "incorrect" type (compared to C++ expectations). +- Some exceptions are the "incorrect" type (compared to C++ expectations). -- Many hooks to override runtime functions (e.g. Func::set_error_handler) - aren't yet implemented. +- Many hooks to override runtime functions (e.g. Func::set_error_handler) + aren't yet implemented. -- The following parts of the Halide public API are currently missing entirely - from the Python bindings (but are all likely to be supported at some point - in the future): +- The following parts of the Halide public API are currently missing entirely + from the Python bindings (but are all likely to be supported at some point in + the future): - - `DeviceInterface` - - `evaluate()` + - `DeviceInterface` + - `evaluate()` ## Example of Simple Usage -The Python bindings for Halide are built as a standard part of the `install` -target, and are present in the Halide install location at -`$HALIDE_INSTALL/lib/python3/site-packages`; adding that to your `PYTHONPATH` -should allow you to simply `import halide`: +Here is a basic example of using Halide to produce a procedural image. -``` +```python # By convention, we import halide as 'hl' for terseness import halide as hl @@ -187,6 +249,7 @@ buf = f.realize([edge, edge, 3]) # Do something with the image. We'll just save it to a PNG. from halide import imageio + imageio.imwrite("/tmp/example.png", buf) ``` @@ -202,13 +265,13 @@ objects without any explicit conversion necessary. In Halide, a "Generator" is a unit of encapsulation for Halide code. It is a self-contained piece of code that can: -- Produce a chunk of Halide IR (in the form of an `hl.Pipeline`) that is - appropriate for compilation (via either JIT or AOT) -- Expose itself to the build system in a discoverable way -- Fully describe itself for the build system with metadata for (at least) the - type and number of inputs and outputs expected -- Allow for build-time customization of coder-specified parameters in a way - that doesn't require editing of source code +- Produce a chunk of Halide IR (in the form of an `hl.Pipeline`) that is + appropriate for compilation (via either JIT or AOT) +- Expose itself to the build system in a discoverable way +- Fully describe itself for the build system with metadata for (at least) the + type and number of inputs and outputs expected +- Allow for build-time customization of coder-specified parameters in a way that + doesn't require editing of source code Originally, Halide only supported writing Generators in C++. In this document, we'll use the term "C++ Generator" to mean "Generator written in C++ using the @@ -220,42 +283,43 @@ neutral with respect to the implementation language/API. A Python Generator is a class that: -- has the `@hl.generator` decorator applied to it -- declares zero or more member fields that are initialized with values of - `hl.InputBuffer` or `hl.InputScalar`, which specify the expected input(s) of - the resulting `Pipeline`. -- declares one or more member fields that are initialized with values of - `hl.OutputBuffer` or `hl.OutputScalar`, which specify the expected output(s) - of the resulting `Pipeline`. -- declares zero or more member fields that are initialized with values of - `hl.GeneratorParam`, which can be used to pass arbitrary information from - the build system to the Generator. A GeneratorParam can carry a value of - type `bool`, `int`, `float`, `str`, or `hl.Type`. -- declares a `generate()` method that fill in the Halide IR needed to define - all of the Outputs -- optionally declares a `configure()` method to dynamically add Inputs or - Outputs to the pipeline, based on (e.g.) the values of `GeneratorParam` - values or other external inputs +- has the `@hl.generator` decorator applied to it +- declares zero or more member fields that are initialized with values of + `hl.InputBuffer` or `hl.InputScalar`, which specify the expected input(s) of + the resulting `Pipeline`. +- declares one or more member fields that are initialized with values of + `hl.OutputBuffer` or `hl.OutputScalar`, which specify the expected output(s) + of the resulting `Pipeline`. +- declares zero or more member fields that are initialized with values of + `hl.GeneratorParam`, which can be used to pass arbitrary information from the + build system to the Generator. A GeneratorParam can carry a value of type + `bool`, `int`, `float`, `str`, or `hl.Type`. +- declares a `generate()` method that fill in the Halide IR needed to define all + the Outputs +- optionally declares a `configure()` method to dynamically add Inputs or + Outputs to the pipeline, based on (e.g.) the values of `GeneratorParam` + values or other external inputs Let's look at a fairly simple example: > **TODO:** this example is pretty contrived; is there an equally simple > Generator to use here that would demonstrate the basics? -``` +```python import halide as hl x = hl.Var('x') y = hl.Var('y') _operators = { - 'xor': lambda a, b: a ^ b, - 'and': lambda a, b: a & b, - 'or': lambda a, b: a | b + 'xor': lambda a, b: a ^ b, + 'and': lambda a, b: a & b, + 'or': lambda a, b: a | b } + # Apply a mask value to a 2D image using a logical operator that is selected at compile-time. -@hl.generator(name = "logical_op_generator") +@hl.generator(name="logical_op_generator") class LogicalOpGenerator: op = hl.GeneratorParam("xor") @@ -273,6 +337,7 @@ class LogicalOpGenerator: v = g.natural_vector_size(hl.UInt(8)) g.output.vectorize(x, v) + if __name__ == "__main__": hl.main() ``` @@ -288,11 +353,11 @@ Let's take the details here one at a time. This decorator adds appropriate "glue" machinery to the class to enforce various invariants. It also serves as the declares a "registered name" for the Generator, which is a unique name that the build system will use to identify the -Generator. If you omit the name, it defaults to defaults to `module.classname`; -if module is `__main__` then we omit it and just use the plain classname. Note -that the registered name need not match the classname. (Inside Halide, we use -the convention of `CamelCase` for class names and `snake_case` for registered -names, but you can use whatever convention you like.) +Generator. If you omit the name, it defaults to `module.classname`; if module is +`__main__` then we omit it and just use the plain classname. Note that the +registered name need not match the classname. (Inside Halide, we use the +convention of `CamelCase` for class names and `snake_case` for registered names, +but you can use whatever convention you like.) #### hl.GeneratorParam @@ -305,9 +370,9 @@ Note that the type of the default value *is* used to define the expected type of the `GeneratorParam`, and trying to set it to an incompatible value will throw an exception. The types that are acceptable to use in a `GeneratorParam` are: -- Python's `bool`, `int`, `float`, or `str` -- Halide's `hl.Type` -- ...that's all +- Python's `bool`, `int`, `float`, or `str` +- Halide's `hl.Type` +- ...that's all Note that the value of a `GeneratorParam` is read-only from the point of view of the Generator; they are set at Generator construction time and attempting to @@ -355,8 +420,8 @@ in the build system. #### Names -Note that all of the GeneratorParams, Inputs, and Outputs have names that are -implicitly filled in based on the fieldname of their initial assignment; unlike +Note that all the GeneratorParams, Inputs, and Outputs have names that are +implicitly filled in based on the field name of their initial assignment; unlike in C++ Generators, there isn't a way to "override" this name (i.e., the name in the IR will always exactly match the Python field name). Names have the same constraints as for C++ Generators (essentially, a C identifier, but without an @@ -377,9 +442,9 @@ way required, but is recommended to improve readability.) #### Types for Inputs and Outputs -For all of the Input and Output fields of Generators, you can specify native -Python types (instead of `hl.Type`) for certain cases that are unambiguous. At -present, we allow `bool` as an alias for `hl.Bool()`, `int` as an alias for +For all the Input and Output fields of Generators, you can specify native Python +types (instead of `hl.Type`) for certain cases that are unambiguous. At present, +we allow `bool` as an alias for `hl.Bool()`, `int` as an alias for `hl.Int(32)`, and `float` as an alias for `hl.Float(32)`. ### Using a Generator for JIT compilation @@ -387,7 +452,7 @@ present, we allow `bool` as an alias for `hl.Bool()`, `int` as an alias for You can use the `compile_to_callable()` method to JIT-compile a Generator into a `hl.Callable`, which is (essentially) just a dynamically-created function. -``` +```python import LogicalOpGenerator from halide import imageio import numpy as np @@ -421,7 +486,7 @@ value of the `HL_JIT_TARGET` environment variable, if set); you can override this behavior selectively by activating a `GeneratorContext` when the Generator is *created*: -``` +```python import LogicalOpGenerator # Compile with debugging enabled @@ -436,7 +501,7 @@ with hl.GeneratorContext(t): If you are using CMake, the simplest thing is to use `add_halide_library` and `add_halide_python_extension_library()`: -``` +```cmake # Build a Halide library as you usually would, but be sure to include `PYTHON_EXTENSION` add_halide_library(xor_filter FROM logical_op_generator @@ -456,16 +521,18 @@ add_halide_python_extension_library(my_extension (Note that this rule works for both C++ and Python Generators.) This compiles the Generator code in `logical_op_generator.py` with the -registered name `logical_op_generator` to produce the target `xor_filter`, and then wraps -the compiled output with a Python extension. The result will be a shared library of the form +registered name `logical_op_generator` to produce the target `xor_filter`, and +then wraps the compiled output with a Python extension. The result will be a +shared library of the form `..so`, where describes the specific Python version and platform (e.g., `cpython-310-darwin` for Python 3.10 on OSX.) Note that you can combine multiple Halide libraries into a single Python module; -this is convenient for packagaing, but also because all the libraries in a single -extension module share the same Halide runtime (and thus, the same caches, thread pools, etc.). +this is convenient for packaging, but also because all the libraries in a single +extension module share the same Halide runtime (and thus, the same caches, +thread pools, etc.). -``` +```cmake add_halide_library(xor_filter ...) add_halide_library(and_filter ...) add_halide_library(or_filter ...) @@ -475,11 +542,12 @@ add_halide_python_extension_library(my_extension HALIDE_LIBRARIES xor_filter and_filter or_filter) ``` -Note that you must take care to ensure that all of the `add_halide_library` targets -specified use the same Halide runtime; it may be necessary to use `add_halide_runtime` -to define an explicit runtime that is shared by all of the targets: +Note that you must take care to ensure that all of the `add_halide_library` +targets specified use the same Halide runtime; it may be necessary to use +`add_halide_runtime` +to define an explicit runtime that is shared by all the targets: -``` +```cmake add_halide_runtime(my_runtime) add_halide_library(xor_filter USE_RUNTIME my_runtime ...) @@ -495,7 +563,7 @@ If you're not using CMake, you can "drive" a Generator directly from your build system via command-line flags. The most common, minimal set looks something like this: -``` +```shell python3 /path/to/my/generator.py -g \ -o \ target= \ @@ -505,24 +573,25 @@ python3 /path/to/my/generator.py -g \ The argument to `-g` is the name supplied to the `@hl.generator` decorator. The argument to -o is a directory to use for the output files; by default, we'll produce a static library containing the object code, and a C++ header file with -a forward declaration. `target` specifies a Halide `Target` string decribing the -OS, architecture, features, etc that should be used for compilation. Any other -arguments to the command line that don't begin with `-` are presumed to name +a forward declaration. `target` specifies a Halide `Target` string describing +the OS, architecture, features, etc. that should be used for compilation. Any +other arguments to the command line that don't begin with `-` are presumed to +name `GeneratorParam` values to set. There are other flags and options too, of course; use `python3 /path/to/my/generator.py -help` to see a list with explanations. (Unfortunately, there isn't (yet) a way to produce a Python Extension just by -running a Generator; the logic for `add_halide_python_extension_library` is currently all -in the CMake helper files.) +running a Generator; the logic for `add_halide_python_extension_library` is +currently all in the CMake helper files.) ### Calling Generator-Produced code from Python As long as the shared library is in `PYTHONPATH`, it can be imported and used directly. For the example above: -``` +```python from my_module import xor_filter from halide import imageio import numpy as np @@ -548,17 +617,17 @@ Above, we're using common Python utilities (`numpy`) to construct the input/output buffers we want to pass to Halide. **Note**: Getting the memory order correct can be a little confusing for numpy. -By default numpy uses "C-style" +By default, numpy uses "C-style" [row-major](https://docs.scipy.org/doc/numpy-1.13.0/reference/internals.html) order, which sounds like the right option for Halide; however, this nomenclature assumes the matrix-math convention of ordering axes as `[rows, cols]`, whereas Halide (and imaging code in general) generally assumes `[x, y]` (i.e., `[cols, -rows]`). Thus what you usually want in Halide is column-major ordering. This +rows]`). Thus, what you usually want in Halide is column-major ordering. This means numpy arrays, by default, come with the wrong memory layout for Halide. But if you construct the numpy arrays yourself (like above), you can pass `order='F'` to make numpy use the Halide-compatible memory layout. If you're passing in an array constructed somewhere else, the easiest thing to do is to -`.transpose()` it before passing it to your Halide code.) +`.transpose()` it before passing it to your Halide code. ### Advanced Generator-Related Topics @@ -570,7 +639,7 @@ offers a convenient alternative to specifying multiple sets of GeneratorParams via the build system. To define alias(es) for a Generator, just add the `@hl.alias` decorator before `@hl.generator` decorator: -``` +```python @hl.alias( xor_generator={"op": "xor"}, and_generator={"op": "and"}, @@ -588,20 +657,21 @@ If you need to build `Input` and/or `Output` dynamically, you can define a are valid, but before `generate()` is called. Let's take our example and add an option to pass an offset to be added after the logical operator is done: -``` +```python import halide as hl x = hl.Var('x') y = hl.Var('y') _operators = { - 'xor': lambda a, b: a ^ b, - 'and': lambda a, b: a & b, - 'or': lambda a, b: a | b + 'xor': lambda a, b: a ^ b, + 'and': lambda a, b: a & b, + 'or': lambda a, b: a | b } + # Apply a mask value to a 2D image using a logical operator that is selected at compile-time. -@hl.generator(name = "logical_op_generator") +@hl.generator(name="logical_op_generator") class LogicalOpGenerator: op = hl.GeneratorParam("xor") with_offset = hl.GeneratorParam(False) @@ -614,7 +684,7 @@ class LogicalOpGenerator: def configure(g): # If with_offset is specified, we if g.with_offset: - g.add_input("offset", hl.InputScalar(hl.Int(32))) + g.add_input("offset", hl.InputScalar(hl.Int(32))) # See note the use of 'g' instead of 'self' here def generate(g): @@ -629,6 +699,7 @@ class LogicalOpGenerator: v = g.natural_vector_size(hl.UInt(8)) g.output.vectorize(x, v) + if __name__ == "__main__": hl.main() ``` @@ -649,7 +720,7 @@ it. This can be especially useful when writing library code, as you can This method is named `call()` and looks like this: -``` +```python @classmethod def call(cls, *args, **kwargs): ... @@ -661,7 +732,7 @@ which is a simple Python dict that allows for overriding `GeneratorParam`s. It returns a tuple of the Output values. For the earlier example, usage might be something like: -``` +```python import LogicalOpFilter x, y = hl.Var(), hl.Var() @@ -677,8 +748,8 @@ func_out = LogicalOpFilter.call(mask=mask_value, input=input_buf) # Above again, but with generator_params func_out = LogicalOpFilter.call(input_buf, mask_value, - generator_params = {"op": "and"}) -func_out = LogicalOpFilter.call(generator_params = {"op": and}, + generator_params={"op": "and"}) +func_out = LogicalOpFilter.call(generator_params={"op": "and"}, input=input_buf, mask=mask_value) ``` @@ -688,32 +759,32 @@ Whether being driven by a build system (for AOT use) or by another piece of Python code (typically for JIT use), the lifecycle of a Generator looks something like this: -- An instance of the Generator in question is created. It uses the - currently-active `GeneratorContext` (which contains the `Target` to be used - for code generation), which is stored in a thread-local stack. -- Some (or all) of the default values of the `GeneratorParam` members may be - replaced based on (e.g.) command-line arguments in the build system -- All `GeneratorParam` members are made immutable. -- The `configure()` method is called, allowing the Generator to use - `add_input()` or `add_output()` to dynamically add inputs and/or outputs. -- If any `Input` or `Output` members were defined with unspecified type or - dimensions (e.g. `some_input = hl.InputBuffer(None, 3)`), those types and - dimensions are filled in from `GeneratorParam` values (e.g. - `some_input.type` in this case). If any types or dimensions are left - unspecified after this step, an exception will be thrown. -- If the Generator is being invoked via its `call()` method (see below), the - default values for `Inputs` will be replaced by the values from the argument - list. -- The Generator instance has its `generate()` method called. -- The calling code will extract the values of all `Output` values and validate - that they match the type, dimensions, etc of the declarations. -- The calling code will then either call `compile_to_file()` and friends (for - AOT use), or return the output values to the caller (for JIT use). -- Finally, the Generator instance will be discarded, never to be used again. - -Note that almost all of the code doing the hand-wavy bits above is injected by -the `@hl.generator` decorator – the Generator author doesn't need to know or -care about the specific details, only that they happen. +- An instance of the Generator in question is created. It uses the + currently-active `GeneratorContext` (which contains the `Target` to be used + for code generation), which is stored in a thread-local stack. +- Some (or all) of the default values of the `GeneratorParam` members may be + replaced based on (e.g.) command-line arguments in the build system +- All `GeneratorParam` members are made immutable. +- The `configure()` method is called, allowing the Generator to use + `add_input()` or `add_output()` to dynamically add inputs and/or outputs. +- If any `Input` or `Output` members were defined with unspecified type or + dimensions (e.g. `some_input = hl.InputBuffer(None, 3)`), those types and + dimensions are filled in from `GeneratorParam` values (e.g. + `some_input.type` in this case). If any types or dimensions are left + unspecified after this step, an exception will be thrown. +- If the Generator is being invoked via its `call()` method (see below), the + default values for `Inputs` will be replaced by the values from the argument + list. +- The Generator instance has its `generate()` method called. +- The calling code will extract the values of all `Output` values and validate + that they match the type, dimensions, etc. of the declarations. +- The calling code will then either call `compile_to_file()` and friends (for + AOT use), or return the output values to the caller (for JIT use). +- Finally, the Generator instance will be discarded, never to be used again. + +Note that almost all the code doing the hand-wavy bits above is injected by the +`@hl.generator` decorator – the Generator author doesn't need to know or care +about the specific details, only that they happen. All Halide Generators are **single-use** instances – that is, any given Generator instance should be used at most once. If a Generator is to be executed @@ -726,35 +797,35 @@ If you have written C++ Generators in Halide in the past, you might notice some features are missing and/or different for Python Generators. Among the differences are: -- In C++, you can create a Generator, then call `set_generatorparam_value()` - to alter the values of GeneratorParams. In Python, there is no public - method to alter a GeneratorParam after the Generator is created; instead, - you must pass a dict of GeneratorParam values to the constructor, after - which the values are immutable for that Generator instance. -- Array Inputs/Outputs: in our experience, they are pretty rarely used, it - complicates the implementation in nontrivial ways, and the majority of use - cases for them can all be reasonably supported by dynamically adding inputs - or outputs (and saving the results in a local array). -- `Input` and `Output`: these were deliberately left out in order - to simplify Python Generators. It's possible that something similar might be - added in the future. -- GeneratorParams with LoopLevel types: these aren't useful without - `Input`/`Output`. -- GeneratorParams with Enum types: using a plain `str` type in Python is - arguably just as easy, if not easier. -- `get_externs_map()`: this allows registering ExternalCode objects to be - appended to the Generator's code. In our experience, this feature is very - rarely used. We will consider adding this in the future if necessary. -- Lazy Binding of Unspecified Input/Output Types: for C++ Generators, if you - left an Output's type (or dimensionality) unspecified, you didn't always - have to specify a `GeneratorParam` to make it into a concrete type: if the - type was always fully specified by the contents of the `generate()` method, - that was good enough. In Python Generators, by contrast, **all** types and - dimensions must be **explicitly** specified by either code declaration or by - `GeneratorParam` setting. This simplifies the internal code in nontrivial - ways, and also allows for (arguably) more readable code, since there are no - longer cases that require the reader to execute the code in their head in - order to deduce the output types. +- In C++, you can create a Generator, then call `set_generatorparam_value()` + to alter the values of GeneratorParams. In Python, there is no public method + to alter a GeneratorParam after the Generator is created; instead, you must + pass a dict of GeneratorParam values to the constructor, after which the + values are immutable for that Generator instance. +- Array Inputs/Outputs: in our experience, they are pretty rarely used, it + complicates the implementation in nontrivial ways, and the majority of use + cases for them can all be reasonably supported by dynamically adding inputs or + outputs (and saving the results in a local array). +- `Input` and `Output`: these were deliberately left out in order to + simplify Python Generators. It's possible that something similar might be + added in the future. +- GeneratorParams with LoopLevel types: these aren't useful without + `Input`/`Output`. +- GeneratorParams with Enum types: using a plain `str` type in Python is + arguably just as easy, if not easier. +- `get_externs_map()`: this allows registering ExternalCode objects to be + appended to the Generator's code. In our experience, this feature is very + rarely used. We will consider adding this in the future if necessary. +- Lazy Binding of Unspecified Input/Output Types: for C++ Generators, if you + left an Output's type (or dimensionality) unspecified, you didn't always have + to specify a `GeneratorParam` to make it into a concrete type: if the type was + always fully specified by the contents of the `generate()` method, that was + good enough. In Python Generators, by contrast, **all** types and dimensions + must be **explicitly** specified by either code declaration or by + `GeneratorParam` setting. This simplifies the internal code in nontrivial + ways, and also allows for (arguably) more readable code, since there are no + longer cases that require the reader to execute the code in their head in + order to deduce the output types. ## Keeping Up To Date @@ -767,8 +838,7 @@ in future releases. ## License The Python bindings use the same -[MIT license](https://github.com/halide/Halide/blob/main/LICENSE.txt) as -Halide. +[MIT license](https://github.com/halide/Halide/blob/main/LICENSE.txt) as Halide. Python bindings provided by Connelly Barnes (2012-2013), Fred Rotbart (2014), Rodrigo Benenson (2015) and the Halide open-source community. diff --git a/README_rungen.md b/doc/RunGen.md similarity index 100% rename from README_rungen.md rename to doc/RunGen.md diff --git a/README_vulkan.md b/doc/Vulkan.md similarity index 100% rename from README_vulkan.md rename to doc/Vulkan.md diff --git a/README_webassembly.md b/doc/WebAssembly.md similarity index 100% rename from README_webassembly.md rename to doc/WebAssembly.md diff --git a/README_webgpu.md b/doc/WebGPU.md similarity index 100% rename from README_webgpu.md rename to doc/WebGPU.md diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 7f7bb15d2894..ba6e0597ecc2 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -119,15 +119,16 @@ install(TARGETS ${utils} EXPORT Halide_Interfaces COMPONENT Halide_Development) ## install(FILES - ${Halide_SOURCE_DIR}/README_cmake.md ${Halide_SOURCE_DIR}/README.md - ${Halide_SOURCE_DIR}/README_python.md - ${Halide_SOURCE_DIR}/README_rungen.md - ${Halide_SOURCE_DIR}/README_webassembly.md ${Halide_SOURCE_DIR}/LICENSE.txt COMPONENT Halide_Documentation TYPE DOC) +install(DIRECTORY "${Halide_SOURCE_DIR}/doc" + COMPONENT Halide_Documentation + TYPE DOC + FILES_MATCHING PATTERN "*.md") + ## # Tools ##