CMake: rationalize and refactor build system.

The two main changes, done together in this commit, are:
* Eliminating most instances of `aux_source_directory()`, replacing
  them with explicit file listings; and
* Moving these file listings into respective subdirectories by
  representing respective nextpnr components as interface libraries.

In addition, the GUI CMake script tree was simplified since it had
a lot of unused/redundant code.

The `aux_source_directory()` command is not recommended for use by
CMake itself because it misses dependency changes when adding/removing
files, and consequently causes build failures requiring a clean rebuild.

This commit does not touch anything related to architectures/families,
which are very complex and redundant all on their own.
This commit is contained in:
Catherine 2025-01-15 09:22:08 +00:00 committed by myrtle
parent d214308f5f
commit 155adc3f5d
20 changed files with 314 additions and 145 deletions

5
3rdparty/json11/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,5 @@
add_library(json11 STATIC
json11.cpp
json11.hpp
)
target_include_directories(json11 PUBLIC .)

9
3rdparty/oourafft/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,9 @@
add_library(oourafft INTERFACE)
target_include_directories(oourafft INTERFACE .)
target_sources(oourafft PUBLIC
fftsg.cc
fftsg.h
fftsg2d.cc
)

View File

@ -1,10 +1,11 @@
cmake_minimum_required(VERSION 3.13)
project(nextpnr CXX C)
cmake_minimum_required(VERSION 3.25)
project(nextpnr CXX)
include(CheckCXXCompilerFlag)
# Allow family.cmake add additional dependencies to gui_${family}.
cmake_policy(SET CMP0079 NEW)
if (NOT DEFINED CMAKE_SUPPRESS_DEVELOPER_WARNINGS)
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE INTERNAL "No dev warnings")
endif()
# We want to explictly include all include directories when generating the
# compilation database as not all clang/gcc share the same implicit includes
@ -14,11 +15,6 @@ if (CMAKE_EXPORT_COMPILE_COMMANDS)
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
# Enable IPO support.
cmake_policy(SET CMP0069 NEW)
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported)
option(BUILD_GUI "Build GUI" OFF)
option(BUILD_PYTHON "Build Python Integration" ON)
option(BUILD_TESTS "Build tests" OFF)
@ -31,12 +27,19 @@ option(PROFILER "Link against libprofiler" OFF)
option(USE_IPO "Compile nextpnr with IPO" ON)
option(USE_RUST "Enable Rust bindings" OFF)
if (USE_RUST)
add_subdirectory(3rdparty/corrosion)
corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE "release" IMPORTED_CRATES RUST_CRATES)
set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
if (BUILD_GUI AND NOT BUILD_PYTHON)
message(FATAL_ERROR "GUI requires Python to build")
endif()
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if (USE_IPO)
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported)
if (ipo_supported)
message(STATUS "Building with IPO")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
@ -103,8 +106,6 @@ if (EXTERNAL_CHIPDB)
add_definitions("-DEXTERNAL_CHIPDB_ROOT=\"${EXTERNAL_CHIPDB_ROOT}\"")
endif()
set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
# List of families to build
set(FAMILIES generic ice40 ecp5 nexus gowin machxo2 mistral himbaechel)
set(STABLE_FAMILIES generic ice40 ecp5)
@ -125,12 +126,9 @@ if (NOT ARCH)
endif()
if (ARCH STREQUAL "all+alpha")
SET(ARCH ${STABLE_FAMILIES} ${EXPERIMENTAL_FAMILIES})
endif()
if (ARCH STREQUAL "all")
SET(ARCH ${STABLE_FAMILIES})
set(ARCH ${STABLE_FAMILIES} ${EXPERIMENTAL_FAMILIES})
elseif (ARCH STREQUAL "all")
set(ARCH ${STABLE_FAMILIES})
endif()
foreach (item ${ARCH})
@ -164,61 +162,53 @@ else()
endif()
endif()
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/3rdparty/sanitizers-cmake/cmake;." ${CMAKE_MODULE_PATH})
if (COVERAGE)
include(CodeCoverage)
endif()
if (NOT DEFINED CMAKE_SUPPRESS_DEVELOPER_WARNINGS)
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE INTERNAL "No dev warnings")
endif()
find_package(Sanitizers)
# List of Boost libraries to include
set(boost_libs filesystem program_options iostreams system)
if (Threads_FOUND)
list(APPEND boost_libs thread)
endif()
if (BUILD_GUI AND NOT BUILD_PYTHON)
message(FATAL_ERROR "GUI requires Python to build")
endif()
if (BUILD_GUI)
# For higher quality backtraces
set(CMAKE_ENABLE_EXPORTS ON)
endif()
find_package(Boost REQUIRED COMPONENTS ${boost_libs})
if (BUILD_PYTHON)
# TODO: sensible minimum Python version
find_package(Python3 3.5 REQUIRED COMPONENTS Interpreter Development.Embed)
find_package(pybind11 CONFIG)
if (NOT pybind11_FOUND)
message(STATUS "Using built-in pybind11")
add_subdirectory(3rdparty/pybind11 EXCLUDE_FROM_ALL)
endif()
else()
find_package(Python3 3.5 REQUIRED COMPONENTS Interpreter)
add_definitions("-DNO_PYTHON")
endif()
find_package(Boost REQUIRED COMPONENTS ${boost_libs})
if (USE_RUST)
add_subdirectory(3rdparty/corrosion)
corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE "release" IMPORTED_CRATES RUST_CRATES)
endif()
if (BUILD_GUI)
# Find the Qt5 libraries
find_package(Qt5 COMPONENTS Core Widgets OpenGL REQUIRED)
find_package(OpenGL REQUIRED)
# For higher quality backtraces
set(CMAKE_ENABLE_EXPORTS ON)
add_subdirectory(3rdparty/QtPropertyBrowser ${CMAKE_CURRENT_BINARY_DIR}/generated/3rdparty/QtPropertyBrowser EXCLUDE_FROM_ALL)
else()
add_definitions("-DNO_GUI")
add_definitions(-DNO_GUI)
endif()
if (NOT DEFINED CURRENT_GIT_VERSION)
# Get the latest abbreviated commit hash of the working branch
# if not already defined outside (e.g. by package manager when building
# outside of git repository)
execute_process(
COMMAND git describe --tags --always
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE CURRENT_GIT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
find_package(Eigen3 REQUIRED NO_MODULE)
add_subdirectory(3rdparty/json11)
add_subdirectory(3rdparty/oourafft)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/3rdparty/sanitizers-cmake/cmake;." ${CMAKE_MODULE_PATH})
find_package(Sanitizers)
if (COVERAGE)
include(CodeCoverage)
endif()
if (BUILD_TESTS)
@ -226,60 +216,6 @@ if (BUILD_TESTS)
enable_testing()
endif()
if (BUILD_GUI)
add_subdirectory(3rdparty/QtPropertyBrowser ${CMAKE_CURRENT_BINARY_DIR}/generated/3rdparty/QtPropertyBrowser EXCLUDE_FROM_ALL)
endif()
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/common/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/version.h
)
if (NOT DEFINED PYBIND11_INCLUDE_DIR)
# Use bundled pybind11
set(PYBIND11_INCLUDE_DIR "3rdparty/pybind11/include")
endif()
include_directories(
common/kernel/
common/place/
common/route/
json/
frontend/
3rdparty/json11/
3rdparty/oourafft/
${PYBIND11_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
${Python3_INCLUDE_DIRS}
)
find_package(Eigen3 REQUIRED NO_MODULE)
link_libraries(Eigen3::Eigen)
aux_source_directory(common/kernel/ KERNEL_SRC_FILES)
aux_source_directory(common/place/ PLACE_SRC_FILES)
aux_source_directory(common/route/ ROUTE_SRC_FILES)
aux_source_directory(json/ JSON_PARSER_FILES)
aux_source_directory(3rdparty/json11 EXT_JSON11_FILES)
aux_source_directory(3rdparty/oourafft EXT_OOURAFFT_FILES)
aux_source_directory(frontend/ FRONTEND_FILES)
aux_source_directory(rust/ RUST_FILES)
set(COMMON_FILES
${KERNEL_SRC_FILES}
${PLACE_SRC_FILES}
${ROUTE_SRC_FILES}
${EXT_JSON11_FILES}
${EXT_OOURAFFT_FILES}
${JSON_PARSER_FILES}
${FRONTEND_FILES}
)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if (CMAKE_CROSSCOMPILING)
set(BBA_IMPORT "IMPORTFILE-NOTFOUND" CACHE FILEPATH
"Path to the `bba-export.cmake` export file from a native build")
@ -296,8 +232,44 @@ else()
set(BBASM_ENDIAN_FLAG "--le")
endif()
set(EXTRA_LIB_DEPS)
if (NOT DEFINED CURRENT_GIT_VERSION)
# Get the latest abbreviated commit hash of the working branch
# if not already defined outside (e.g. by package manager when building
# outside of git repository)
execute_process(
COMMAND git describe --tags --always
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE CURRENT_GIT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/common/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/version.h)
include_directories(
common/kernel
${Boost_INCLUDE_DIRS}
${Python3_INCLUDE_DIRS}
)
add_subdirectory(common/kernel)
add_subdirectory(common/place)
add_subdirectory(common/route)
add_subdirectory(frontend)
add_subdirectory(json)
add_subdirectory(rust)
set(COMMON_LIBRARIES
nextpnr_kernel
nextpnr_place
nextpnr_route
nextpnr_frontend
nextpnr_json
Eigen3::Eigen
oourafft
)
set(EXTRA_LIB_DEPS)
if (PROFILER)
list(APPEND EXTRA_LIB_DEPS profiler)
endif()
@ -315,7 +287,8 @@ foreach (family ${ARCH})
endif()
# Add the CLI binary target
add_executable(${PROGRAM_PREFIX}nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES})
add_executable(${PROGRAM_PREFIX}nextpnr-${family} ${${ufamily}_FILES})
target_link_libraries(${PROGRAM_PREFIX}nextpnr-${family} PRIVATE ${COMMON_LIBRARIES})
if (WASI)
# set(CMAKE_EXECUTABLE_SUFFIX) breaks CMake tests for some reason
set_property(TARGET ${PROGRAM_PREFIX}nextpnr-${family} PROPERTY SUFFIX ".wasm")
@ -341,8 +314,8 @@ foreach (family ${ARCH})
endif()
add_executable(${PROGRAM_PREFIX}nextpnr-${family}-test ${${ufamily}_TEST_FILES}
${COMMON_FILES} ${${ufamily}_FILES} ${GUI_TEST_FILES})
target_link_libraries(${PROGRAM_PREFIX}nextpnr-${family}-test PRIVATE gtest_main)
${${ufamily}_FILES} ${GUI_TEST_FILES})
target_link_libraries(${PROGRAM_PREFIX}nextpnr-${family}-test PRIVATE ${COMMON_LIBRARIES} gtest_main)
add_sanitizers(${PROGRAM_PREFIX}nextpnr-${family}-test)
add_test(${family}-test ${CMAKE_CURRENT_BINARY_DIR}/nextpnr-${family}-test)
@ -372,9 +345,9 @@ foreach (family ${ARCH})
endif()
add_sanitizers(${target})
if (BUILD_GUI)
target_include_directories(${target} PRIVATE gui/${family}/ gui/)
target_compile_definitions(${target} PRIVATE QT_NO_KEYWORDS)
target_link_libraries(${target} LINK_PUBLIC gui_${family} ${GUI_LIBRARY_FILES_${ufamily}})
target_include_directories(${target} PRIVATE gui/${family}/ gui/)
target_link_libraries(${target} LINK_PUBLIC gui_${family})
endif()
if (BUILD_PYTHON)
target_link_libraries(${target} LINK_PUBLIC ${Python3_LIBRARIES})

View File

@ -0,0 +1,81 @@
add_library(nextpnr_kernel INTERFACE)
target_include_directories(nextpnr_kernel INTERFACE .)
target_sources(nextpnr_kernel PUBLIC
arch_api.h
archcheck.cc
arch_pybindings_shared.h
array2d.h
base_arch.h
base_clusterinfo.h
basectx.cc
basectx.h
bits.cc
bits.h
chain_utils.h
command.cc
command.h
constraints.h
constraints.impl.h
context.cc
context.h
design_utils.cc
design_utils.h
deterministic_rng.h
dynamic_bitarray.h
embed.cc
embed.h
exclusive_state_groups.h
exclusive_state_groups.impl.h
handle_error.cc
hashlib.h
idstring.cc
idstring.h
idstringlist.cc
idstringlist.h
indexed_store.h
log.cc
log.h
nextpnr_assertions.cc
nextpnr_assertions.h
nextpnr_base_types.h
nextpnr.cc
nextpnr.h
nextpnr_namespaces.cc
nextpnr_namespaces.h
nextpnr_types.cc
nextpnr_types.h
property.cc
property.h
pybindings.cc
pybindings.h
pycontainers.h
pywrappers.h
relptr.h
report.cc
scope_lock.h
sdc.cc
sdf.cc
sso_array.h
str_ring_buffer.cc
str_ring_buffer.h
svg.cc
timing.cc
timing.h
timing_log.cc
util.h
)
target_link_libraries(nextpnr_kernel INTERFACE
nextpnr_frontend
nextpnr_json
nextpnr_rust
json11
)
if (BUILD_PYTHON)
target_link_libraries(nextpnr_kernel INTERFACE
pybind11_headers
)
endif()

View File

@ -0,0 +1,23 @@
add_library(nextpnr_place INTERFACE)
target_include_directories(nextpnr_place INTERFACE .)
target_sources(nextpnr_place PUBLIC
detail_place_cfg.h
detail_place_core.cc
detail_place_core.h
fast_bels.h
parallel_refine.cc
parallel_refine.h
place_common.cc
place_common.h
placer1.cc
placer1.h
placer_heap.cc
placer_heap.h
placer_static.cc
placer_static.h
static_util.h
timing_opt.cc
timing_opt.h
)

View File

@ -0,0 +1,10 @@
add_library(nextpnr_route INTERFACE)
target_include_directories(nextpnr_route INTERFACE .)
target_sources(nextpnr_route PUBLIC
router1.cc
router1.h
router2.cc
router2.h
)

9
frontend/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
add_library(nextpnr_frontend INTERFACE)
target_include_directories(nextpnr_frontend INTERFACE .)
target_sources(nextpnr_frontend PUBLIC
frontend_base.h
json_frontend.cc
json_frontend.h
)

View File

@ -1,15 +1,78 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
add_compile_definitions(QT_NO_KEYWORDS)
set(GUI_SOURCES
application.cc
application.h
basewindow.cc
basewindow.h
designwidget.cc
designwidget.h
fpgaviewwidget.cc
fpgaviewwidget.h
line_editor.cc
line_editor.h
lineshader.cc
lineshader.h
pyconsole.cc
pyconsole.h
pythontab.cc
pythontab.h
quadtree.h
treemodel.cc
treemodel.h
worker.cc
worker.h
${family}/mainwindow.cc
${family}/mainwindow.h
)
qt5_add_resources(GUI_QT_RESOURCES
base.qrc
${family}/nextpnr.qrc
)
add_library(gui_${family} STATIC
${GUI_SOURCES}
${GUI_QT_RESOURCES}
)
target_include_directories(gui_${family} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/${family}
${CMAKE_SOURCE_DIR}/${family}
${CMAKE_SOURCE_DIR}/3rdparty/QtPropertyBrowser/src
${CMAKE_SOURCE_DIR}/3rdparty/imgui
${CMAKE_SOURCE_DIR}/3rdparty/qtimgui
${CMAKE_BINARY_DIR}/generated
)
target_compile_definitions(gui_${family} PRIVATE
NEXTPNR_NAMESPACE=nextpnr_${family}
ARCH_${ufamily}
ARCHNAME=${family}
)
target_link_libraries(gui_${family} PUBLIC
Qt5::Widgets
)
target_link_libraries(gui_${family} PRIVATE
nextpnr_kernel
Qt5::OpenGL
QtPropertyBrowser
${EXTRA_LIB_DEPS}
)
# Currently always the case when the GUI is built.
if (BUILD_PYTHON)
set(PYTHON_CONSOLE_SRC
target_sources(gui_${family} PRIVATE
../3rdparty/python-console/ColumnFormatter.cpp
../3rdparty/python-console/ParseHelper.cpp
../3rdparty/python-console/ParseHelper.BlockParseState.cpp
../3rdparty/python-console/ParseHelper.BracketParseState.cpp
../3rdparty/python-console/ParseHelper.ContinuationParseState.cpp
../3rdparty/python-console/ParseMessage.cpp
../3rdparty/python-console/modified/pyredirector.cc
../3rdparty/python-console/modified/pyinterpreter.cc
../3rdparty/imgui/imgui_widgets.cpp
@ -19,30 +82,9 @@ if (BUILD_PYTHON)
../3rdparty/qtimgui/ImGuiRenderer.cpp
../3rdparty/qtimgui/QtImGui.cpp
)
target_include_directories(gui_${family} PRIVATE
../3rdparty/python-console
../3rdparty/python-console/modified
)
endif()
aux_source_directory(. GUI_SOURCE_FILES)
aux_source_directory(${family}/ GUI_SOURCE_FILES)
set(_RESOURCES base.qrc ${family}/nextpnr.qrc)
qt5_add_resources(GUI_RESOURCE_FILES ${_RESOURCES})
set(GUI_LIBRARY_FILES_${ufamily} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} QtPropertyBrowser PARENT_SCOPE)
add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${PYTHON_CONSOLE_SRC} ${GUI_RESOURCE_FILES})
include(${family}/family.cmake)
target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src ../3rdparty/imgui ../3rdparty/qtimgui/)
target_include_directories(gui_${family} PRIVATE ${CMAKE_BINARY_DIR}/generated)
if (BUILD_PYTHON)
target_include_directories(gui_${family} PRIVATE ../3rdparty/python-console ../3rdparty/python-console/modified)
endif()
target_compile_definitions(gui_${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} QT_NO_KEYWORDS)
target_link_libraries(gui_${family} Qt5::Widgets)
foreach (lib_dep ${EXTRA_LIB_DEPS})
target_link_libraries(gui_${family} ${lib_dep})
endforeach()

View File

View File

View File

View File

@ -1 +0,0 @@
target_include_directories(gui_mistral PRIVATE ${MISTRAL_ROOT}/libmistral ${CMAKE_BINARY_DIR}/libmistral ${CMAKE_BINARY_DIR}/tools)

View File

8
json/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
add_library(nextpnr_json INTERFACE)
target_include_directories(nextpnr_json INTERFACE .)
target_sources(nextpnr_json PUBLIC
jsonwrite.cc
jsonwrite.h
)

10
rust/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
add_library(nextpnr_rust INTERFACE)
target_include_directories(nextpnr_rust INTERFACE .)
if (USE_RUST)
target_sources(nextpnr_rust PUBLIC
rust.cc
rust.h
)
endif()