cmake_minimum_required(VERSION 3.14)

project(DREAMPlace LANGUAGES CXX)
set(CMAKE_VERBOSE_MAKEFILE ON)

if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    set (CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "Prefix prepended to install directories" FORCE )
endif()
message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING
        "Choose the type of build, options are: Debug Release."
        FORCE)
endif(NOT CMAKE_BUILD_TYPE)
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")

# This is a dirty fix for CMake on some machines that generates default CXX standard and override the custom settings, 
# because CUDA does not support C++17 and higher. 
# You can check the CMAKE_CXX_FLAGS in CMakeCache.txt to verify the issue. 
if(CMAKE_CXX_FLAGS)
string(REGEX REPLACE "-std=c\\+\\+([0-9a-z]+)" " " CMAKE_CXX_FLAGS_INIT ${CMAKE_CXX_FLAGS})
endif(CMAKE_CXX_FLAGS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_INIT}" CACHE STRING "Flags used by the compiler during all build types." FORCE)

if(NOT CMAKE_CXX_ABI)
    set(CMAKE_CXX_ABI 0 CACHE STRING
        "Choose the value for _GLIBCXX_USE_CXX11_ABI, options are: 0|1."
        FORCE)
endif(NOT CMAKE_CXX_ABI)
message(STATUS "CMAKE_CXX_ABI: _GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}")
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI})

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# for CUDA, must be put before finding PyTorch  
# link dynamic libraries rather than static ones 
set(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
# critical for cuda_add_library, as we need to turn off -O flags 
# to make sure the symbols generated by nvcc and gcc are the same 
set(CUDA_PROPAGATE_HOST_FLAGS ON)
#set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};--expt-extended-lambda)
# required for executable to run at the install directory 
# it will change the RPATH when installing 
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

include(cmake/TorchExtension.cmake)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(cmake/FilesystemLibrary.cmake)

# without this, clang will complain about linking 
#set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set_property(GLOBAL PROPERTY POSITION_INDEPENDENT_CODE TRUE)

find_package(OpenMP REQUIRED)
#find_program(PYTHON "python" REQUIRED)
find_package(ZLIB REQUIRED)
set(Boost_NO_BOOST_CMAKE TRUE)
find_package(Boost 1.55.0 REQUIRED)
message(STATUS "Boost_INCLUDE_DIRS = ${Boost_INCLUDE_DIRS}")

set(BUILD_DRAW_PLACE ON CACHE BOOL "Build drawing support for placement plots" FORCE)
if(APPLE)
  set(_BUILD_HETEROSTA_DEFAULT OFF)
else()
  set(_BUILD_HETEROSTA_DEFAULT ON)
endif()
set(BUILD_HETEROSTA ${_BUILD_HETEROSTA_DEFAULT} CACHE BOOL "Build HeteroSTA and the HeteroSTA timing operator")
set(BUILD_NCTUGR ON CACHE BOOL "Install NCTUgr wrapper scripts and optional router data" FORCE)
set(INSTALL_NCTUGR ON CACHE BOOL "Install thirdparty/NCTUgr.ICCAD2012" FORCE)
set(BUILD_DREAMPLACE_UNITTESTS OFF CACHE BOOL "Build DREAMPlace unit tests" FORCE)
set(INSTALL_DREAMPLACE_BENCHMARKS OFF CACHE BOOL "Install DREAMPlace benchmark helper scripts" FORCE)
set(INSTALL_DREAMPLACE_TEST_DATA OFF CACHE BOOL "Install DREAMPlace test benchmark data" FORCE)
message(STATUS "BUILD_DRAW_PLACE: ${BUILD_DRAW_PLACE}")
message(STATUS "BUILD_HETEROSTA: ${BUILD_HETEROSTA}")
message(STATUS "BUILD_NCTUGR: ${BUILD_NCTUGR}")
message(STATUS "BUILD_DREAMPLACE_UNITTESTS: ${BUILD_DREAMPLACE_UNITTESTS}")
message(STATUS "INSTALL_DREAMPLACE_BENCHMARKS: ${INSTALL_DREAMPLACE_BENCHMARKS}")
message(STATUS "INSTALL_DREAMPLACE_TEST_DATA: ${INSTALL_DREAMPLACE_TEST_DATA}")

if(BUILD_DRAW_PLACE)
  find_package(Cairo)
  message(STATUS "Cairo: ${CAIRO_INCLUDE_DIRS}")
  message(STATUS "Cairo: ${CAIRO_LIBRARIES}")
endif()

get_filename_component(OPS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dreamplace/ops ABSOLUTE)
get_filename_component(UTILITY_LIBRARY_DIRS ${CMAKE_CURRENT_BINARY_DIR}/dreamplace/ops/utility ABSOLUTE)
message(STATUS "OPS_DIR ${OPS_DIR}")
message(STATUS "UTILITY_LIBRARY_DIRS ${UTILITY_LIBRARY_DIRS}")

# thirdparty libraries 
# flute for steiner tree generation 
find_path(FLUTE_INCLUDE_DIRS flute.hpp PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/*)
string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} FLUTE_LINK_DIRS ${FLUTE_INCLUDE_DIRS})
message(STATUS "FLUTE_INCLUDE_DIRS ${FLUTE_INCLUDE_DIRS}")
message(STATUS "FLUTE_LINK_DIRS ${FLUTE_LINK_DIRS}")
include_directories(${FLUTE_INCLUDE_DIRS})

# Limbo for parsers 
set(LIMBO_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/Limbo)
message(STATUS "LIMBO_SOURCE_DIR ${LIMBO_SOURCE_DIR}")
set(LIMBO_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/Limbo)
message(STATUS "LIMBO_BINARY_DIR ${LIMBO_BINARY_DIR}")
if(CUDA_FOUND)
  # CUDA 12+ ships CCCL CUB headers that use cuda::std and break DreamPlace's
  # DREAMPLACE_NAMESPACE::cub wrapper (see pin_pos_cuda_segment_kernel.cu).
  # Use the bundled CUB release for CUDA 12+; toolkit CUB is fine on CUDA 11.x.
  if (${CUDA_VERSION_MAJOR} VERSION_GREATER_EQUAL "12")
    set(CUB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cub)
  elseif (${CUDA_VERSION_MAJOR} VERSION_GREATER_EQUAL "11")
    set(CUB_DIR ${CUDA_INCLUDE_DIRS})
  else()
    set(CUB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cub)
  endif()
endif()
message(STATUS "CUB_DIR ${CUB_DIR}")

# munkres-cpp for Hungarian algorithm 
set(MUNKRES_CPP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/munkres-cpp/src)
set(MUNKRES_CPP_LINK_DIRS ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/munkres-cpp)
message(STATUS "MUNKRES_CPP_INCLUDE_DIRS ${MUNKRES_CPP_INCLUDE_DIRS}")
message(STATUS "MUNKRES_CPP_LINK_DIRS ${MUNKRES_CPP_LINK_DIRS}")

set(BUILD_OPENTIMER ON CACHE BOOL "Build OpenTimer and the OpenTimer timing operator" FORCE)
message(STATUS "BUILD_OPENTIMER: ${BUILD_OPENTIMER}")

# lemon for network flow algorithms 
find_path(LEMON_SOURCE_DIR lemon PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/Limbo/limbo/thirdparty/* 
	NO_DEFAULT_PATH
	)
set(LEMON_INCLUDE_DIRS "${LEMON_SOURCE_DIR}")
string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} LEMON_BINARY_DIR ${LEMON_SOURCE_DIR})
list(APPEND LEMON_INCLUDE_DIRS "${LEMON_BINARY_DIR}")
set(LEMON_LINK_DIRS ${LEMON_BINARY_DIR}/lemon)
message(STATUS "LEMON_INCLUDE_DIRS ${LEMON_INCLUDE_DIRS}")
message(STATUS "LEMON_LINK_DIRS ${LEMON_LINK_DIRS}")

if(BUILD_OPENTIMER)
  set(OPENTIMER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/OpenTimer)
  message(STATUS "OPENTIMER_SOURCE_DIR ${OPENTIMER_SOURCE_DIR}")
  set(OPENTIMER_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/OpenTimer)
  message(STATUS "OPENTIMER_BINARY_DIR ${OPENTIMER_BINARY_DIR}")
  include_directories(${OPENTIMER_SOURCE_DIR})
endif()

if(BUILD_HETEROSTA)
  set(HETEROSTA_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/HeteroSTA)
endif()

if (CUDA_FOUND)
  if (NOT CMAKE_CUDA_ARCHITECTURES)
    message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES is required. Run build/build.sh to select a single GPU architecture.")
  endif(NOT CMAKE_CUDA_ARCHITECTURES)

  set(CUDA_ARCH_FLAGS)
  list(LENGTH CMAKE_CUDA_ARCHITECTURES NUM_CUDA_ARCHITECTURES)
  if(NOT NUM_CUDA_ARCHITECTURES EQUAL 1)
    message(FATAL_ERROR "DREAMPlace builds one CUDA architecture at a time; got ${CMAKE_CUDA_ARCHITECTURES}")
  endif()
  foreach(CUDA_ARCH ${CMAKE_CUDA_ARCHITECTURES})
    string(REGEX REPLACE "[^0-9]" "" CUDA_ARCH_NUMBER "${CUDA_ARCH}")
    if(CUDA_ARCH_NUMBER)
      list(APPEND CUDA_ARCH_FLAGS "-gencode" "arch=compute_${CUDA_ARCH_NUMBER},code=sm_${CUDA_ARCH_NUMBER}")
    else()
      message(FATAL_ERROR "Unsupported CUDA architecture entry: ${CUDA_ARCH}")
    endif()
  endforeach()
  message(STATUS "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
  message(STATUS "CUDA_ARCH_FLAGS: ${CUDA_ARCH_FLAGS}")

  list(APPEND CUDA_NVCC_FLAGS ${CUDA_ARCH_FLAGS} --compiler-options;-fPIC)
endif(CUDA_FOUND)

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dreamplace/configure.py.in ${CMAKE_CURRENT_BINARY_DIR}/dreamplace/configure.py)

add_subdirectory(thirdparty)
add_subdirectory(dreamplace)
if(BUILD_DREAMPLACE_UNITTESTS)
  add_subdirectory(unittest)
endif()
if(INSTALL_DREAMPLACE_BENCHMARKS)
  add_subdirectory(benchmarks)
endif()
if(INSTALL_DREAMPLACE_TEST_DATA)
  add_subdirectory(test)
endif()

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dreamplace/configure.py DESTINATION dreamplace)
