cmake_minimum_required(VERSION 3.18) project(pyngspice VERSION 43.0.0 LANGUAGES C CXX) # ============================================================================ # Options # ============================================================================ option(BUILD_PYTHON_BINDINGS "Build Python bindings" ON) option(ENABLE_XSPICE "Enable XSPICE code models" ON) option(ENABLE_OSDI "Enable OSDI (Verilog-A) support" ON) option(ENABLE_CIDER "Enable CIDER numerical device models" OFF) # Disabled - needs special KLU setup option(ENABLE_KLU "Enable KLU sparse matrix solver" OFF) # Disabled - needs complex build setup # ============================================================================ # Compiler settings # ============================================================================ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD 11) set(CMAKE_C_EXTENSIONS ON) # Use gnu11 instead of c11 for better complex support set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Suppress warnings if(MSVC) add_compile_options(/W3 /wd4996 /wd4267 /wd4244 /wd4018 /wd4090 /wd4101 /wd4146) add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS) add_definitions(-DWIN32 -D_WINDOWS) else() add_compile_options(-Wall -Wno-unused-variable -Wno-unused-function -Wno-sign-compare -Wno-format) if(CMAKE_C_COMPILER_ID MATCHES "GNU") add_compile_options(-Wno-maybe-uninitialized -Wno-unused-but-set-variable) endif() endif() # ============================================================================ # Platform detection # ============================================================================ if(WIN32) set(PLATFORM_WINDOWS TRUE) elseif(APPLE) set(PLATFORM_MACOS TRUE) elseif(UNIX) set(PLATFORM_LINUX TRUE) endif() # ============================================================================ # Find dependencies # ============================================================================ find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module NumPy) # Find pybind11 — try system install first, then fall back to pip-installed find_package(pybind11 CONFIG QUIET) if(NOT pybind11_FOUND) # pybind11 is a build dependency in pyproject.toml, so pip has already # installed it into the build environment. Find it via the Python package. execute_process( COMMAND ${Python3_EXECUTABLE} -c "import pybind11; print(pybind11.get_cmake_dir())" OUTPUT_VARIABLE PYBIND11_CMAKE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE PYBIND11_RESULT ) if(PYBIND11_RESULT EQUAL 0) list(APPEND CMAKE_PREFIX_PATH ${PYBIND11_CMAKE_DIR}) find_package(pybind11 CONFIG REQUIRED) else() message(FATAL_ERROR "pybind11 not found. Install it with: pip install pybind11>=2.11") endif() endif() # Math library on Unix if(UNIX AND NOT APPLE) find_library(M_LIBRARY m REQUIRED) endif() find_package(Threads REQUIRED) # ============================================================================ # Source directories # ============================================================================ set(NGSPICE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) # ============================================================================ # Include directories # ============================================================================ set(NGSPICE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/visualc/src/include # Windows config.h ${NGSPICE_SRC} ${NGSPICE_SRC}/include ${NGSPICE_SRC}/include/cppduals # For HICUM2 duals library ${NGSPICE_SRC}/include/ngspice ${NGSPICE_SRC}/spicelib/devices ${NGSPICE_SRC}/spicelib/analysis ${NGSPICE_SRC}/spicelib/parser ${NGSPICE_SRC}/maths/dense ${NGSPICE_SRC}/maths/sparse ${NGSPICE_SRC}/maths/ni ${NGSPICE_SRC}/maths/deriv ${NGSPICE_SRC}/maths/cmaths ${NGSPICE_SRC}/maths/misc ${NGSPICE_SRC}/maths/poly ${NGSPICE_SRC}/maths/fft ${NGSPICE_SRC}/frontend ${NGSPICE_SRC}/frontend/plotting ${NGSPICE_SRC}/frontend/parser ${NGSPICE_SRC}/frontend/help ${NGSPICE_SRC}/frontend/numparam ${NGSPICE_SRC}/frontend/trannoise ${NGSPICE_SRC}/misc ${NGSPICE_SRC}/ciderlib/input ${NGSPICE_SRC}/ciderlib/support ${NGSPICE_SRC}/ciderlib/oned ${NGSPICE_SRC}/ciderlib/twod ${NGSPICE_SRC}/xspice/cm ${NGSPICE_SRC}/xspice/cmpp ${NGSPICE_SRC}/xspice/evt ${NGSPICE_SRC}/xspice/enh ${NGSPICE_SRC}/xspice/ipc ${NGSPICE_SRC}/xspice/idn ${NGSPICE_SRC}/xspice/mif ${NGSPICE_SRC}/xspice/icm ${NGSPICE_SRC}/osdi ) # ============================================================================ # Build date/version info # ============================================================================ string(TIMESTAMP NGSPICE_BUILD_DATE "%Y-%m-%d") # ============================================================================ # Compile definitions # ============================================================================ set(NGSPICE_DEFINITIONS -DNGSPICEDLL -DSHARED_MODULE -DSIMULATOR -DHAS_PROGREP -DNG_SHARED_BUILD -DNGSPICEBUILDDATE="${NGSPICE_BUILD_DATE}" ) # MinGW-specific: nothing special needed now since we patched cmath1.c if(ENABLE_XSPICE) list(APPEND NGSPICE_DEFINITIONS -DXSPICE) endif() if(ENABLE_OSDI) list(APPEND NGSPICE_DEFINITIONS -DOSDI) endif() if(ENABLE_CIDER) list(APPEND NGSPICE_DEFINITIONS -DCIDER) endif() if(ENABLE_KLU) list(APPEND NGSPICE_DEFINITIONS -DKLU) endif() # ============================================================================ # Collect ngspice source files # ============================================================================ # Frontend sources file(GLOB FRONTEND_SOURCES ${NGSPICE_SRC}/frontend/*.c ) # Frontend subdirectories file(GLOB FRONTEND_PARSER_SOURCES ${NGSPICE_SRC}/frontend/parser/*.c) file(GLOB FRONTEND_NUMPARAM_SOURCES ${NGSPICE_SRC}/frontend/numparam/*.c) file(GLOB FRONTEND_TRANNOISE_SOURCES ${NGSPICE_SRC}/frontend/trannoise/*.c) file(GLOB FRONTEND_HELP_SOURCES ${NGSPICE_SRC}/frontend/help/*.c) file(GLOB FRONTEND_PLOTTING_SOURCES ${NGSPICE_SRC}/frontend/plotting/*.c) # Math sources file(GLOB MATHS_SPARSE_SOURCES ${NGSPICE_SRC}/maths/sparse/*.c) file(GLOB MATHS_DENSE_SOURCES ${NGSPICE_SRC}/maths/dense/*.c) file(GLOB MATHS_NI_SOURCES ${NGSPICE_SRC}/maths/ni/*.c) file(GLOB MATHS_DERIV_SOURCES ${NGSPICE_SRC}/maths/deriv/*.c) file(GLOB MATHS_CMATHS_SOURCES ${NGSPICE_SRC}/maths/cmaths/*.c) file(GLOB MATHS_MISC_SOURCES ${NGSPICE_SRC}/maths/misc/*.c) file(GLOB MATHS_POLY_SOURCES ${NGSPICE_SRC}/maths/poly/*.c) file(GLOB MATHS_FFT_SOURCES ${NGSPICE_SRC}/maths/fft/*.c) # Spicelib sources file(GLOB SPICELIB_ANALYSIS_SOURCES ${NGSPICE_SRC}/spicelib/analysis/*.c) file(GLOB SPICELIB_PARSER_SOURCES ${NGSPICE_SRC}/spicelib/parser/*.c) # Device sources - collect all device model subdirectories file(GLOB_RECURSE SPICELIB_DEVICES_C_SOURCES ${NGSPICE_SRC}/spicelib/devices/*.c) # HICUM2 .cpp files use the "duals" library for automatic differentiation (header-only, included in ngspice) file(GLOB_RECURSE SPICELIB_DEVICES_CPP_SOURCES ${NGSPICE_SRC}/spicelib/devices/*.cpp) set(SPICELIB_DEVICES_SOURCES ${SPICELIB_DEVICES_C_SOURCES} ${SPICELIB_DEVICES_CPP_SOURCES}) # Exclude KLU-specific binding files when KLU is disabled if(NOT ENABLE_KLU) list(FILTER SPICELIB_DEVICES_SOURCES EXCLUDE REGEX "bindCSC\\.c$") endif() # Misc sources file(GLOB MISC_SOURCES ${NGSPICE_SRC}/misc/*.c) # CIDER sources (if enabled) if(ENABLE_CIDER) file(GLOB CIDER_INPUT_SOURCES ${NGSPICE_SRC}/ciderlib/input/*.c) file(GLOB CIDER_SUPPORT_SOURCES ${NGSPICE_SRC}/ciderlib/support/*.c) file(GLOB CIDER_ONED_SOURCES ${NGSPICE_SRC}/ciderlib/oned/*.c) file(GLOB CIDER_TWOD_SOURCES ${NGSPICE_SRC}/ciderlib/twod/*.c) set(CIDER_SOURCES ${CIDER_INPUT_SOURCES} ${CIDER_SUPPORT_SOURCES} ${CIDER_ONED_SOURCES} ${CIDER_TWOD_SOURCES} ) endif() # XSPICE sources (if enabled) if(ENABLE_XSPICE) file(GLOB XSPICE_CM_SOURCES ${NGSPICE_SRC}/xspice/cm/*.c) file(GLOB XSPICE_EVT_SOURCES ${NGSPICE_SRC}/xspice/evt/*.c) file(GLOB XSPICE_ENH_SOURCES ${NGSPICE_SRC}/xspice/enh/*.c) file(GLOB XSPICE_IPC_SOURCES ${NGSPICE_SRC}/xspice/ipc/*.c) file(GLOB XSPICE_IDN_SOURCES ${NGSPICE_SRC}/xspice/idn/*.c) file(GLOB XSPICE_MIF_SOURCES ${NGSPICE_SRC}/xspice/mif/*.c) # ICM (code models) built separately set(XSPICE_SOURCES ${XSPICE_CM_SOURCES} ${XSPICE_EVT_SOURCES} ${XSPICE_ENH_SOURCES} ${XSPICE_IPC_SOURCES} ${XSPICE_IDN_SOURCES} ${XSPICE_MIF_SOURCES} ) # Exclude KLU-specific binding files when KLU is disabled if(NOT ENABLE_KLU) list(FILTER XSPICE_SOURCES EXCLUDE REGEX "bindCSC\\.c$") endif() endif() # OSDI sources (if enabled) if(ENABLE_OSDI) file(GLOB OSDI_SOURCES ${NGSPICE_SRC}/osdi/*.c) endif() # KLU sources (if enabled) if(ENABLE_KLU) # KLU sources are in src/maths/KLU directory file(GLOB KLU_SOURCES ${NGSPICE_SRC}/maths/KLU/*.c) list(APPEND NGSPICE_INCLUDE_DIRS ${NGSPICE_SRC}/maths/KLU) endif() # Main ngspice files set(NGSPICE_MAIN_SOURCES ${NGSPICE_SRC}/sharedspice.c ${NGSPICE_SRC}/conf.c ${NGSPICE_SRC}/ngspice.c # Contains SIMinfo definition ) # MSVC compatibility if(MSVC AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/visualc/src/msvc-compat.c) list(APPEND NGSPICE_MAIN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/visualc/src/msvc-compat.c ) endif() # ============================================================================ # Combine all ngspice sources # ============================================================================ set(ALL_NGSPICE_SOURCES ${NGSPICE_MAIN_SOURCES} ${FRONTEND_SOURCES} ${FRONTEND_PARSER_SOURCES} ${FRONTEND_NUMPARAM_SOURCES} ${FRONTEND_TRANNOISE_SOURCES} ${FRONTEND_HELP_SOURCES} ${FRONTEND_PLOTTING_SOURCES} ${MATHS_SPARSE_SOURCES} ${MATHS_DENSE_SOURCES} ${MATHS_NI_SOURCES} ${MATHS_DERIV_SOURCES} ${MATHS_CMATHS_SOURCES} ${MATHS_MISC_SOURCES} ${MATHS_POLY_SOURCES} ${MATHS_FFT_SOURCES} ${SPICELIB_ANALYSIS_SOURCES} ${SPICELIB_PARSER_SOURCES} ${SPICELIB_DEVICES_SOURCES} ${MISC_SOURCES} ) if(ENABLE_CIDER) list(APPEND ALL_NGSPICE_SOURCES ${CIDER_SOURCES}) endif() if(ENABLE_XSPICE) list(APPEND ALL_NGSPICE_SOURCES ${XSPICE_SOURCES}) endif() if(ENABLE_OSDI) list(APPEND ALL_NGSPICE_SOURCES ${OSDI_SOURCES}) endif() if(ENABLE_KLU AND DEFINED KLU_SOURCES) list(APPEND ALL_NGSPICE_SOURCES ${KLU_SOURCES}) endif() # Remove files that cause conflicts or aren't needed for shared library list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "main\\.c$") # Note: ngspice.c is INCLUDED - it contains SIMinfo but no main() list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "winmain\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "tclspice\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngnutmeg\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "nghelp\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngsconvert\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngmultidec\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngproc2mod\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "makeidx\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "hist_info\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "testcommands\\.c$") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "test_accuracy\\.c$") # Exclude ndev (network device) - uses Unix socket headers list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/ndev/") # Exclude CIDER-dependent numerical device models when CIDER is disabled if(NOT ENABLE_CIDER) list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/nbjt/") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/nbjt2/") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/numd/") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/numd2/") list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/numos/") endif() # ============================================================================ # Create ngspice static library # ============================================================================ add_library(ngspice_core STATIC ${ALL_NGSPICE_SOURCES}) target_include_directories(ngspice_core PUBLIC ${NGSPICE_INCLUDE_DIRS}) target_compile_definitions(ngspice_core PUBLIC ${NGSPICE_DEFINITIONS}) if(UNIX AND NOT APPLE) target_link_libraries(ngspice_core PUBLIC ${M_LIBRARY}) endif() target_link_libraries(ngspice_core PUBLIC Threads::Threads) # Windows-specific libraries if(WIN32) target_link_libraries(ngspice_core PUBLIC shlwapi) endif() # ============================================================================ # Python extension sources # ============================================================================ set(PYTHON_EXT_SOURCES ${NGSPICE_SRC}/bindings/module.cpp ${NGSPICE_SRC}/cpp/simulator.cpp ${NGSPICE_SRC}/cpp/callbacks.cpp ${NGSPICE_SRC}/cpp/sim_runner.cpp ${NGSPICE_SRC}/cpp/raw_read.cpp ${NGSPICE_SRC}/cpp/trace.cpp ) # ============================================================================ # Create Python module # ============================================================================ pybind11_add_module(_pyngspice MODULE ${PYTHON_EXT_SOURCES}) target_include_directories(_pyngspice PRIVATE ${NGSPICE_INCLUDE_DIRS} ${NGSPICE_SRC}/cpp ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS} ) target_compile_definitions(_pyngspice PRIVATE NGSPICE_PYTHON_MODULE VERSION_STR="${PROJECT_VERSION}" ${NGSPICE_DEFINITIONS} ) # Link against ngspice core and Python target_link_libraries(_pyngspice PRIVATE ngspice_core Python3::NumPy ) if(APPLE) target_link_libraries(_pyngspice PRIVATE "-framework Accelerate") endif() # Output .pyd directly into pyngspice/ for inplace editable install set_target_properties(_pyngspice PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/pyngspice ) # MinGW: statically link all runtime DLLs (ship zero DLLs) if(MINGW) target_link_options(_pyngspice PRIVATE -static-libgcc -static-libstdc++) target_link_libraries(_pyngspice PRIVATE -Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive,-Bdynamic ) endif() # ============================================================================ # Install (for wheel builds) # ============================================================================ install(TARGETS _pyngspice LIBRARY DESTINATION pyngspice RUNTIME DESTINATION pyngspice ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/pyngspice/__init__.py ${CMAKE_CURRENT_SOURCE_DIR}/pyngspice/runner.py ${CMAKE_CURRENT_SOURCE_DIR}/pyngspice/netlist.py DESTINATION pyngspice ) # ============================================================================ # Summary # ============================================================================ message(STATUS "") message(STATUS "========================================") message(STATUS "pyngspice Configuration") message(STATUS "========================================") message(STATUS " Version: ${PROJECT_VERSION}") message(STATUS " Python: ${Python3_EXECUTABLE}") message(STATUS " NumPy: ${Python3_NumPy_VERSION}") message(STATUS " pybind11: ${pybind11_VERSION}") message(STATUS " XSPICE: ${ENABLE_XSPICE}") message(STATUS " OSDI: ${ENABLE_OSDI}") message(STATUS " CIDER: ${ENABLE_CIDER}") message(STATUS " KLU: ${ENABLE_KLU}") message(STATUS " Source files: ${CMAKE_CURRENT_LIST_DIR}") message(STATUS "========================================") message(STATUS "")