cmake_minimum_required(VERSION 3.12)
project(libfive)
# Properly distinguish between Apple and upstream Clang
cmake_policy(SET CMP0025 NEW)
include(CheckCXXCompilerFlag)
option(BUILD_STUDIO_APP "Build Studio application" ON)
option(BUILD_GUILE_BINDINGS "Build Guile bindings" ON)
option(BUILD_PYTHON_BINDINGS "Build Python bindings" ON)
option(LIBFIVE_PACKED_OPCODES
"Tightly pack opcodes (breaks compatibility with older saved f-reps)"
OFF)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RELEASE)
endif()
if (LIBFIVE_PACKED_OPCODES)
message("Using packed opcodes, which breaks compatibility with saved f-reps!")
add_definitions(-DLIBFIVE_PACKED_OPCODES)
endif()
# Build libfive with ccache if the package is present
set(LIBFIVE_CCACHE_BUILD OFF CACHE BOOL "Set to ON for a ccache enabled build")
if(LIBFIVE_CCACHE_BUILD)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(LIBFIVE_CCACHE_MAXSIZE "" CACHE STRING "Size of ccache")
set(LIBFIVE_CCACHE_DIR "" CACHE STRING "Directory to keep ccached data")
set(LIBFIVE_CCACHE_PARAMS "CCACHE_CPP2=yes CCACHE_HASHDIR=yes"
CACHE STRING "Parameters to pass through to ccache")
set(CCACHE_PROGRAM "${LIBFIVE_CCACHE_PARAMS} ${CCACHE_PROGRAM}")
if (LIBFIVE_CCACHE_MAXSIZE)
set(CCACHE_PROGRAM "CCACHE_MAXSIZE=${LIBFIVE_CCACHE_MAXSIZE} ${CCACHE_PROGRAM}")
endif()
if (LIBFIVE_CCACHE_DIR)
set(CCACHE_PROGRAM "CCACHE_DIR=${LIBFIVE_CCACHE_DIR} ${CCACHE_PROGRAM}")
endif()
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PROGRAM})
else()
message(FATAL_ERROR "Unable to find the program ccache. Set LIBFIVE_CCACHE_BUILD to OFF")
endif()
endif()
################################################################################
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -g -fPIC -pedantic -Werror=switch")
# Sometimes this flag is not supported (e.g. on Apple Silicon)
check_cxx_compiler_flag("-march=native" MARCH_SUPPORTED)
if(MARCH_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
set(CMAKE_CXX_FLAGS_DEBUG "-O0")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DEIGEN_NO_DEBUG")
else()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_CXX_FLAGS "/EHsc /WX /D_USE_MATH_DEFINES /D_SCL_SECURE_NO_WARNINGS")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244 /wd4305")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD/ /DEIGEN_NO_DEBUG /arch:AVX2")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
if(APPLE)
set(CMAKE_MACOSX_RPATH ON)
endif()
# Work around an issue with Boost::Interval on OpenBSD and MinGW on Windows
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD" OR MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__USE_ISOC99")
endif()
################################################################################
# Find all packages here at the top level so we can print debugging info
if(NOT MSVC)
# These packages have different naming conventions in MSVC vs Linux
find_package(Boost REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GUILE guile-2.2>=2.2.1)
if (NOT(GUILE_FOUND))
pkg_check_modules(GUILE guile-3.0>=3.0.0)
endif()
pkg_check_modules(EIGEN REQUIRED eigen3>=3.2.92)
else()
find_package(eigen3 REQUIRED)
find_package(boost REQUIRED 1.65.0)
endif()
# These packages have consistent names across OS's
find_package(PNG REQUIRED)
find_package(Python3 COMPONENTS Interpreter Development)
# Both Windows and Linux will attempt to build the Studio UI
if (BUILD_STUDIO_APP)
find_package(Qt5Core 5.12)
if (Qt5Core_FOUND)
set(QtCore_FOUND 1)
else()
find_package(Qt6Core 6.0)
if (Qt6Core_FOUND)
set(QtCore_FOUND 1)
endif()
endif()
endif()
if (UNIX AND NOT(APPLE))
find_package(Threads REQUIRED)
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -latomic")
endif(UNIX AND NOT(APPLE))
################################################################################
# Inform the user which subsystems will be built and any missing deps
message("Checking dependencies:")
message(" libfive: ✓")
if (BUILD_GUILE_BINDINGS)
if (GUILE_FOUND)
message(" Guile bindings: ✓")
else ()
message(" Guile bindings: ✘ (needs Guile 2.2 or later)")
endif()
else()
message(" Guile bindings: ✘ (skipping)")
endif()
if (BUILD_PYTHON_BINDINGS)
if (Python3_FOUND)
message(" Python bindings: ✓")
else()
message(" Python bindings: ✘ (Python 3 not found)")
endif()
else()
message(" Python bindings: ✘ (skipping)")
endif()
if (BUILD_STUDIO_APP)
if (GUILE_FOUND AND BUILD_GUILE_BINDINGS)
set(STUDIO_WITH_GUILE ON)
else()
set(STUDIO_WITH_GUILE OFF)
endif()
if (Python3_FOUND AND BUILD_PYTHON_BINDINGS)
set(STUDIO_WITH_PYTHON ON)
else()
set(STUDIO_WITH_PYTHON OFF)
endif()
if (QtCore_FOUND AND STUDIO_WITH_GUILE AND STUDIO_WITH_PYTHON)
message(" Studio: ✓ (Python + Guile)")
elseif (QtCore_FOUND AND STUDIO_WITH_GUILE)
message(" Studio: ✓ (Guile only)")
elseif (QtCore_FOUND AND STUDIO_WITH_PYTHON)
message(" Studio: ✓ (Python only)")
else ()
if (QtCore_FOUND)
message(" Studio: ✘ (needs Guile or Python)")
elseif (STUDIO_WITH_GUILE OR STUDIO_WITH_PYTHON)
message(" Studio: ✘ (needs Qt 5.12 or later)")
else()
message(" Studio: ✘ (needs Qt 5.12 or later, and Guile or Python")
endif()
endif()
else()
message(" Studio: ✘ (skipping)")
endif()
################################################################################
# Set a flag to detect the case where users run CMake in the wrong directory
set(LIBFIVE_BUILD_FROM_ROOT true)
# Always build the kernel and test suite
add_subdirectory(libfive)
if(BUILD_STUDIO_APP AND (STUDIO_WITH_GUILE OR STUDIO_WITH_PYTHON) AND QtCore_FOUND)
add_subdirectory(studio)
endif()