Building an ArrayFire program with CMake

Brian KloppenborgArrayFire 5 Comments

Introduction

In the last week there were a few questions about how to use ArrayFire with CMake on Linux on the ArrayFire Google Groups page. Although we have some quick notes about this in the documentation I thought it would be fun to provide a fully worked example that demonstrates a cool CMake trick to reduce compilation time by a factor of three when linking against multiple backends. At the bottom of this post is a link to a GitHub repository containing a minimal CMake project setup which links a program against ArrayFire’s CPU, OpenCL, and CUDA backends.

The instructions below will work on Linux, OSX, and Windows; however, you will need to use CMake generators if you want to use XCode or Visual Studio to compile your code. For Visual Studio specifically, we suggest you follow the documentation to set up Visual Studio projects rather than using CMake generators because it is a much more straightforward process.

Prerequisites

Foremost, make sure that you have both a C++11 compatible compiler and CMake 3.0 (or later) installed on your system. Please see the documentation for specific commands for Fedora, Debian, Ubuntu, and a special requirement for the Tegra TK1. Next, ensure that you have downloaded (or compiled ArrayFire from source) and installed it to your system. Pay close attention to the installation path as this may need to be manually specified at a later stage.

Creating a project

Set up a directory as you would any other project. Let’s say we have two sources files, fooA.cpp and fooB.cpp which we wish to compile into an executable called fooExe and link against ArrayFire. In the top level folder of your project create a file called CMakeLists.txt and fill it with the following contents:

CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
PROJECT(foo)
FIND_PACKAGE(ArrayFire REQUIRED)
INCLUDE_DIRECTORIES(${ArrayFire_INCLUDE_DIRS})

ADD_EXECUTABLE(fooExe fooA.cpp fooB.cpp)
TARGET_LINK_LIBRARIES(fooExe ${ArrayFire_LIBRARIES} )

The FIND_PACKAGE command will attempt to find the ArrayFire library using ArrayFire’s pre-built ArrayFireConfigure.cmake script located in the share/ArrayFire/cmake sub-folder of the ArrayFire installation root. The word REQUIRED will cause CMake to fail during the setup stage if ArrayFire is not found. The ADD_EXECUTABLE command specifies that we wish to create an executable called fooExe which will be composed of the fooA.cpp and fooB.cpp source files. Next, the TARGET_LINK_LIBRARIES command instructs CMake to link the fooExe program with the most powerful ArrayFire backend library (typically CUDA). If you wish to link against a different backend, the ArrayFireConfigure.cmake script also provides the following variables for use:

  • ArrayFire_CPU_FOUND – True of the ArrayFire CPU library has been found.
  • ArrayFire_CPU_LIBRARIES – Location of ArrayFire’s CPU library, if found.
  • ArrayFire_CUDA_FOUND – True of the ArrayFire CUDA library has been found.
  • ArrayFire_CUDA_LIBRARIES – Location of ArrayFire’s CUDA library, if found.
  • ArrayFire_OpenCL_FOUND – True of the ArrayFire OpenCL library has been found.
  • ArrayFire_OpenCL_LIBRARIES – Location of ArrayFire’s OpenCL library, if found.
  • See the CMake documentation for inquires about the other portions of the file.

    Now, to build the project we execute the following commands:

    cmake .
    make
    

    The cmake command instructs CMake to parse the current directory’s CMakeLists.txt file and generate the necessary files for compiling the program. If the ArrayFire library is not found, you may get an error like the following:

    CMake Error at src/CMakeLists.txt:5 (FIND_PACKAGE):
      By not providing "FindArrayFire.cmake" in CMAKE_MODULE_PATH this project
      has asked CMake to find a package configuration file provided by
      "ArrayFire", but CMake did not find one.
    
      Could not find a package configuration file provided by "ArrayFire" with
      any of the following names:
    
        ArrayFireConfig.cmake
        arrayfire-config.cmake
    
      Add the installation prefix of "ArrayFire" to CMAKE_PREFIX_PATH or set
      "ArrayFire_DIR" to a directory containing one of the above files.  If
      "ArrayFire" provides a separate development package or SDK, be sure it has
      been installed.
    

    In this case you need to specify the full path to the folder which contains the ArrayFireConfigure.cmake script (found in the share/ArrayFire/cmake sub-directory of the installation folder). For example, if ArrayFire were installed to /opt this cmake command above would be replaced with the following:

    cmake -DArrayFire_DIR=/opt/share/ArrayFire/cmake .

    If all goes well, then CMake will generate the necessary files and the compilation will be successful and you will have a project running with ArrayFire in only a few lines of CMake code!

    Reduce compilation time when linking against multiple backends

    If you are building a project that will link against multiple ArrayFire backends, here is a pretty awesome trick to reduce compilation times significantly. We will take advantage of CMake Object Libraries to compile source code that is common to all backends into object files and then link them together with a specific ArrayFire backend. Here is a quick demonstration of this in action in a CMakeLists.txt file:

    ...
    FILE(GLOB SOURCE "*.cpp")
    ADD_LIBRARY(FOO_CORE OBJECT ${SOURCE})
    ADD_EXECUTABLE(fooExe $)
    TARGET_LINK_LIBRARIES(fooExe ${ArrayFire_LIBRARIES} )
    

    The FILE operation isn’t suggested by the CMake folks, but it makes getting all of the source files in a directory very quick. The ADD_LIBRARY command with the optional OBJECT argument creates a library of object files called FOO_CORE. Lastly, the ADD_EXECUTABLE command with the $ argument uses the compiled objects to create the fooExe executable.

    This looks very similar to what we did before, but because the source files are pre-compiled, we can create several executables that link against different ArrayFire backends without re-compiling the source files. Consider the following CMakeLists.txt file:

    ...
    FILE(GLOB SOURCE "*.cpp")
    ADD_LIBRARY(FOO_CORE OBJECT ${SOURCE})
    ADD_EXECUTABLE(fooExe-cuda $)
    TARGET_LINK_LIBRARIES(fooExe-cuda ${ArrayFire_CUDA_LIBRARIES} )
    ADD_EXECUTABLE(fooExe-opencl $)
    TARGET_LINK_LIBRARIES(fooExe-opencl ${ArrayFire_OpenCL_LIBRARIES} )
    

    Here we create two executables, fooExe-cuda and fooExe-opencl, but only compile the fooA.cpp and fooB.cpp files once! If you were to link against all three backends, like the ArrayFire benchmark suite does, this simple trick could reduce your compilation times by about a factor of three!

    A fully worked example

    Please see my ArrayFire CMake Project Example for a fully worked example that efficiently compiles code and links it with all three of ArrayFire’s backends.

    Comments 5

    1. Nice example of usage of the object libraries!

      Note that there seems to be a problem with the parsing of CMake generator expression “$” that only appear as a single “$”

        1. Thank you very much. Somehow our blogging platform stripped out the text! I have restored it to its correct state.

    2. hyperlink in “Please see my ArrayFire CMake Project Example for a fully worked example that efficiently compiles code and links it with all three of ArrayFire’s backends.” does not lead anywhere

    Leave a Reply to glehmann Cancel reply

    Your email address will not be published. Required fields are marked *