diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 00000000..04bff1a1 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,69 @@ +name: Publish wheels to PyPI + +on: + # allows running workflows manually + workflow_dispatch: + + release: + types: [published] + +jobs: + main: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + arch: manylinux_x86_64 + - os: macos-13 + arch: macosx_x86_64 + - os: macos-14 + arch: macosx_arm64 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.23.0 + env: + CIBW_BUILD: "cp*-${{ matrix.arch }}" + CIBW_ARCHS_LINUX: "x86_64" + CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux_2_34_x86_64 + CIBW_ARCHS_MACOS: "native" + CIBW_ENVIRONMENT_MACOS: > + MACOSX_DEPLOYMENT_TARGET=${{ matrix.os == 'macos-14' && '14.0' || '13.0' }} + DYLD_LIBRARY_PATH=/usr/local/opt/gcc/lib/gcc/current/:$DYLD_LIBRARY_PATH + FC=gfortran-14 + CIBW_BUILD_FRONTEND: "build" + with: + package-dir: . + output-dir: wheelhouse + config-file: "{package}/pyproject.toml" + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: vmecpp-wheels-${{ matrix.os }} + path: ./wheelhouse/*.whl + + publish: + name: Publish wheels to PyPI + if: github.event_name == 'release' + needs: main + runs-on: ubuntu-latest + environment: + name: pypi + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + path: wheelhouse + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 81631c69..82eb92db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ -/bin -/build +__pycache__ +*.egg-info +*.egg +*.pyc +build +dist diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f00aa44..cc3d17bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,249 +1,338 @@ +cmake_minimum_required(VERSION 3.15) -cmake_minimum_required( VERSION 3.15 ) +project(NJOY LANGUAGES Fortran) +string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_PROJECT_NAME_LOWER) # For install paths like "njoy" -find_package( Python3 3.5 REQUIRED ) +# Include standard modules +include(GNUInstallDirs) +include(FeatureSummary) +include(cmake/GenerateScript.cmake) -set( CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Supported configuration types" FORCE ) +set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Supported configuration types" FORCE) -project( njoy LANGUAGES Fortran ) -get_directory_property( is_subproject PARENT_DIRECTORY ) -include( CMakeDependentOption REQUIRED ) +# Determine if this is being built as a subproject or the main project +if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_SOURCE_DIR}) + set(NJOY_IS_SUBPROJECT FALSE) # This is the main/top-level project +else() + set(NJOY_IS_SUBPROJECT TRUE) # This is being included as a subproject +endif() +add_feature_info("Building as Subproject" NJOY_IS_SUBPROJECT "NJOY is being built as part of a larger CMake project.") -set( njoy_GNU_minimum_version 5.1 ) +# Compiler Version Check +set(NJOY_GNU_MINIMUM_VERSION 5.1) -if( njoy_${CMAKE_Fortran_COMPILER_ID}_minimum_version ) - if( CMAKE_Fortran_COMPILER_VERSION AND - CMAKE_Fortran_COMPILER_VERSION VERSION_LESS - ${njoy_${CMAKE_Fortran_COMPILER_ID}_minimum_version} ) - message( FATAL_ERROR "${CMAKE_Fortran_COMPILER_ID} version must be greater than ${njoy_${CMAKE_Fortran_COMPILER_ID}_minimum_version}" ) +if(CMAKE_Fortran_COMPILER_ID AND NJOY_${CMAKE_Fortran_COMPILER_ID}_MINIMUM_VERSION) + if(CMAKE_Fortran_COMPILER_VERSION AND + CMAKE_Fortran_COMPILER_VERSION VERSION_LESS NJOY_${CMAKE_Fortran_COMPILER_ID}_MINIMUM_VERSION) + message(FATAL_ERROR + "${CMAKE_Fortran_COMPILER_ID} version ${CMAKE_Fortran_COMPILER_VERSION} " + "is less than required minimum ${NJOY_${CMAKE_Fortran_COMPILER_ID}_MINIMUM_VERSION}") endif() endif() -# general properties -option( njoy_strict "Compile time warnings are converted to errors" OFF ) - -# binary instrumentation -option( coverage "Enable binary instrumentation to collect test coverage information in the DEBUG configuration" ) -option( profile_generate "Enable binary instrumentation to generation execution profiles in the RELEASE configuration which may be used to guide later optimization" ) - -# additional optimizations -option( link_time_optimization "Enable link time optimization in the RELEASE configuration" ) -option( profile_use "In the RELEASE configuration, leverage previously generated exeution profile to inform optimization decisions" ) -option( nonportable_optimization "Enable optimizations which compromise portability of resulting binary in the RELEASE configuration" ) +# Build Options +option(NJOY_STRICT "Compile time warnings are converted to errors" OFF) +option(NJOY_COVERAGE "Enable binary instrumentation for test coverage (DEBUG configuration)" OFF) +option(NJOY_PROFILE_GENERATE "Enable binary instrumentation for execution profiling (RELEASE configuration)" OFF) +option(NJOY_PROFILE_USE "Leverage previously generated execution profile for optimization (RELEASE configuration)" OFF) +option(NJOY_ENABLE_LTO "Enable Link Time Optimization (IPO)" ON) +option(NJOY_NONPORTABLE_OPTIMIZATION "Enable optimizations compromising binary portability (RELEASE configuration)" OFF) +option(NJOY_LINK_EXECUTABLE_STATICALLY "Attempt to link the NJOY executable statically" OFF) -# libraries and linking -option( static "Statically link component and environment libraries" OFF ) -if ( static AND ( "${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin" ) ) - message( FATAL_ERROR "Static binaries not supported on OSX" ) +# Standard CMake option to control library type (SHARED vs STATIC) +# Default to ON (shared libraries) if not set, to match previous default behavior +# where NJOY_STATIC_SELF_LIBRARY was OFF by default. +if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries") endif() -CMAKE_DEPENDENT_OPTION( static_libraries "Statically link component libraries" OFF "NOT static" ON ) -CMAKE_DEPENDENT_OPTION( static_njoy "Statically link the njoy component library" OFF "NOT static;NOT static_libraries" ON ) +if(NJOY_PROFILE_GENERATE AND NJOY_PROFILE_USE) + message(FATAL_ERROR "Cannot both generate (NJOY_PROFILE_GENERATE) and use (NJOY_PROFILE_USE) execution profiles in the same configuration.") +endif() -if ( profile_generate AND profile_use ) - message( FATAL_ERROR "Cannot both generate and use execution profile in the same configuration" ) +if(NJOY_PROFILE_GENERATE OR NJOY_PROFILE_USE) + file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/profiling") endif() -if ( profile_generate ) - file( MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/profiling" ) +# Interprocedural Optimization (LTO) +if(NJOY_ENABLE_LTO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +else() + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) endif() -set( CMAKE_Fortran_MODULE_DIRECTORY "${CMAKE_BINARY_DIR}/fortran_modules" CACHE PATH "directory for fortran modules" ) -file( MAKE_DIRECTORY "${CMAKE_Fortran_MODULE_DIRECTORY}" ) - -set( njoy_GNU_Linux_common_flags "-Wall" "-Wextra" ) -set( njoy_GNU_Linux_DEBUG_flags "-O0" "-g" "-gdwarf-3" "-frounding-math" "-fsignaling-nans" "-fcheck=all" "-ffpe-trap=invalid,zero,overflow" ) -set( njoy_GNU_Linux_RELEASE_flags "-O3" "-DNDEBUG" ) -set( njoy_GNU_Linux_strict_flags "-Werror" ) -set( njoy_GNU_Linux_coverage_flags "--coverage" ) -set( njoy_GNU_Linux_subproject_flags "-Wno-maybe-uninitialized" "-Wno-conversion" "-Wno-unused-parameter" "-Wno-compare-reals" "-Wno-unused-variable" "-Wno-intrinsic-shadow" "-Wno-unused-dummy-argument" "-Wno-uninitialized" "-Wno-unused-function" "-Wno-unused-label" "-Wno-character-truncation" ) -set( njoy_GNU_Linux_base_project_flags ) -set( njoy_GNU_Linux_profile_generate_flags "-fprofile-generate='${CMAKE_BINARY_DIR}/profiling'" ) -set( njoy_GNU_Linux_link_time_optimization_flags "-flto" ) -set( njoy_GNU_Linux_profile_use_flags "-fprofile-use='${CMAKE_BINARY_DIR}/profiling'" ) -set( njoy_GNU_Linux_nonportable_optimization_flags "-march=native" ) -set( njoy_GNU_Linux_static_flags "-static" ) -set( njoy_GNU_Windows_common_flags "-Wall" "-Wextra" ) -set( njoy_GNU_Windows_DEBUG_flags "-O0" "-g" "-gdwarf-3" "-frounding-math" "-fsignaling-nans" "-fcheck=all" "-ffpe-trap=invalid,zero,overflow" ) -set( njoy_GNU_Windows_RELEASE_flags "-O3" "-DNDEBUG" ) -set( njoy_GNU_Windows_strict_flags "-Werror" ) -set( njoy_GNU_Windows_coverage_flags "--coverage" ) -set( njoy_GNU_Windows_subproject_flags "-Wno-maybe-uninitialized" "-Wno-conversion" "-Wno-unused-parameter" "-Wno-compare-reals" "-Wno-unused-variable" "-Wno-intrinsic-shadow" "-Wno-unused-dummy-argument" "-Wno-uninitialized" "-Wno-unused-function" "-Wno-unused-label" "-Wno-character-truncation" ) -set( njoy_GNU_Windows_base_project_flags ) -set( njoy_GNU_Windows_profile_generate_flags "-fprofile-generate='${CMAKE_BINARY_DIR}/profiling'" ) -set( njoy_GNU_Windows_link_time_optimization_flags "-flto" ) -set( njoy_GNU_Windows_profile_use_flags "-fprofile-use='${CMAKE_BINARY_DIR}/profiling'" ) -set( njoy_GNU_Windows_nonportable_optimization_flags "-march=native" ) -set( njoy_GNU_Windows_static_flags "-static" ) -set( njoy_GNU_Darwin_common_flags "-Wall" "-Wextra" ) -set( njoy_GNU_Darwin_DEBUG_flags "-O0" "-g" "-gdwarf-3" "-frounding-math" "-fsignaling-nans" "-fcheck=all" "-ffpe-trap=invalid,zero,overflow" ) -set( njoy_GNU_Darwin_RELEASE_flags "-O3" "-DNDEBUG" ) -set( njoy_GNU_Darwin_strict_flags "-Werror" ) -set( njoy_GNU_Darwin_coverage_flags "--coverage" ) -set( njoy_GNU_Darwin_subproject_flags "-Wno-maybe-uninitialized" "-Wno-conversion" "-Wno-unused-parameter" "-Wno-compare-reals" "-Wno-unused-variable" "-Wno-intrinsic-shadow" "-Wno-unused-dummy-argument" "-Wno-uninitialized" "-Wno-unused-function" "-Wno-unused-label" "-Wno-character-truncation" ) -set( njoy_GNU_Darwin_base_project_flags ) -set( njoy_GNU_Darwin_profile_generate_flags "-fprofile-generate='${CMAKE_BINARY_DIR}/profiling'" ) -set( njoy_GNU_Darwin_link_time_optimization_flags "-flto" ) -set( njoy_GNU_Darwin_profile_use_flags "-fprofile-use='${CMAKE_BINARY_DIR}/profiling'" ) -set( njoy_GNU_Darwin_nonportable_optimization_flags "-march=native" ) -set( njoy_GNU_Darwin_static_flags "-static" ) - -if ( static_njoy ) - set( njoy_library_linkage STATIC ) -else () - set( njoy_library_linkage SHARED ) -endif () - -set( CMAKE_SKIP_BUILD_RPATH FALSE ) -set( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE ) -if ( CMAKE_SYSTEM_NAME STREQUAL "Darwin" ) - set( rpath_prefix "@loader_path" ) +# Use PROJECT_BINARY_DIR for the module directory path. +set(CMAKE_Fortran_MODULE_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME_LOWER}_fortran_modules" CACHE PATH "Directory for NJOY Fortran modules") +file(MAKE_DIRECTORY "${CMAKE_Fortran_MODULE_DIRECTORY}") + +set(NJOY_APPENDED_COMPILE_FLAGS "" CACHE STRING "User-specified compile flags appended to NJOY targets") +set(NJOY_APPENDED_LINK_FLAGS "" CACHE STRING "User-specified link flags appended to NJOY targets") + +# Compiler Specific Flags +if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + set(NJOY_GNU_COMMON_FLAGS "-Wall;-Wextra") + set(NJOY_GNU_DEBUG_FLAGS "-O0;-g;-gdwarf-3;-frounding-math;-fsignaling-nans;-fcheck=all;-ffpe-trap=invalid,zero,overflow") + set(NJOY_GNU_RELEASE_FLAGS "-O3;-DNDEBUG") # CMake's IPO will add -flto if NJOY_ENABLE_LTO is ON + set(NJOY_GNU_STRICT_FLAGS "-Werror") + set(NJOY_GNU_COVERAGE_FLAGS "--coverage") + set(NJOY_GNU_PROFILE_GENERATE_FLAGS "-fprofile-generate=${PROJECT_BINARY_DIR}/profiling") + set(NJOY_GNU_PROFILE_USE_FLAGS "-fprofile-use=${PROJECT_BINARY_DIR}/profiling") + set(NJOY_GNU_NONPORTABLE_OPTIMIZATION_FLAGS "-march=native") + set(NJOY_GNU_STATIC_LINK_FLAGS "-static") # For njoy_executable if NJOY_LINK_EXECUTABLE_STATICALLY is ON + set(NJOY_GNU_SUBPROJECT_COMPILE_FLAGS "-Wno-maybe-uninitialized;-Wno-conversion;-Wno-unused-parameter;-Wno-compare-reals;-Wno-unused-variable;-Wno-intrinsic-shadow;-Wno-unused-dummy-argument;-Wno-uninitialized;-Wno-unused-function;-Wno-unused-label;-Wno-character-truncation") + set(NJOY_GNU_BASE_PROJECT_COMPILE_FLAGS "") +elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") + message(WARNING "Intel Fortran compiler flags are placeholders. Please verify and complete them for ${CMAKE_Fortran_COMPILER_ID}.") + set(NJOY_INTEL_COMMON_FLAGS "-Wall") + set(NJOY_INTEL_DEBUG_FLAGS "-O0;-g;-debug;-check all;-fpe0;-traceback") + set(NJOY_INTEL_RELEASE_FLAGS "-O3;-DNDEBUG") # CMake's IPO will add -ipo if NJOY_ENABLE_LTO is ON + set(NJOY_INTEL_STRICT_FLAGS "-warn errors") + set(NJOY_INTEL_COVERAGE_FLAGS "-prof-gen=srcpos") + set(NJOY_INTEL_PROFILE_GENERATE_FLAGS "-prof-gen=${PROJECT_BINARY_DIR}/profiling") + set(NJOY_INTEL_PROFILE_USE_FLAGS "-prof-use=${PROJECT_BINARY_DIR}/profiling") + set(NJOY_INTEL_NONPORTABLE_OPTIMIZATION_FLAGS "-xHost") + set(NJOY_INTEL_STATIC_LINK_FLAGS "-static-intel") # For njoy_executable if NJOY_LINK_EXECUTABLE_STATICALLY is ON + set(NJOY_INTEL_SUBPROJECT_COMPILE_FLAGS "-diag-disable remark") + set(NJOY_INTEL_BASE_PROJECT_COMPILE_FLAGS "") else() - set( rpath_prefix "\\$ORIGIN" ) + message(WARNING "Compiler ${CMAKE_Fortran_COMPILER_ID} is not explicitly supported with optimized flags. Using default CMake flags.") endif() -list( INSERT 0 CMAKE_INSTALL_RPATH "${rpath_prefix}/../lib" ) -set( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE ) -if ( NOT GIT_EXECUTABLE ) - find_package( Git ) - if ( NOT GIT_FOUND ) - message( FATAL_ERROR "git installation was not found." ) +function(njoy_apply_build_options TARGET_NAME) + set(COMPILE_OPTIONS_LIST "") + set(LINK_OPTIONS_LIST "") + set(COMPILER_FLAGS_PREFIX "NJOY_${CMAKE_Fortran_COMPILER_ID}") + + if(DEFINED ${COMPILER_FLAGS_PREFIX}_COMMON_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST ${${COMPILER_FLAGS_PREFIX}_COMMON_FLAGS}) + endif() + if(NJOY_STRICT AND DEFINED ${COMPILER_FLAGS_PREFIX}_STRICT_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST ${${COMPILER_FLAGS_PREFIX}_STRICT_FLAGS}) + endif() + + if(NJOY_IS_SUBPROJECT) + if(DEFINED ${COMPILER_FLAGS_PREFIX}_SUBPROJECT_COMPILE_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST ${${COMPILER_FLAGS_PREFIX}_SUBPROJECT_COMPILE_FLAGS}) + endif() + else() # Base project + if(DEFINED ${COMPILER_FLAGS_PREFIX}_BASE_PROJECT_COMPILE_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST ${${COMPILER_FLAGS_PREFIX}_BASE_PROJECT_COMPILE_FLAGS}) + endif() + endif() + + # Debug specific flags + if(DEFINED ${COMPILER_FLAGS_PREFIX}_DEBUG_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_DEBUG_FLAGS}>) + endif() + if(NJOY_COVERAGE AND DEFINED ${COMPILER_FLAGS_PREFIX}_COVERAGE_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_COVERAGE_FLAGS}>) + list(APPEND LINK_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_COVERAGE_FLAGS}>) + endif() + + # Release specific flags + if(DEFINED ${COMPILER_FLAGS_PREFIX}_RELEASE_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_RELEASE_FLAGS}>) + endif() + if(NJOY_PROFILE_GENERATE AND DEFINED ${COMPILER_FLAGS_PREFIX}_PROFILE_GENERATE_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_PROFILE_GENERATE_FLAGS}>) + list(APPEND LINK_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_PROFILE_GENERATE_FLAGS}>) + endif() + if(NJOY_PROFILE_USE AND DEFINED ${COMPILER_FLAGS_PREFIX}_PROFILE_USE_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_PROFILE_USE_FLAGS}>) + list(APPEND LINK_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_PROFILE_USE_FLAGS}>) + endif() + + # Non-portable optimization flags + if(NJOY_NONPORTABLE_OPTIMIZATION AND DEFINED ${COMPILER_FLAGS_PREFIX}_NONPORTABLE_OPTIMIZATION_FLAGS) + list(APPEND COMPILE_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_NONPORTABLE_OPTIMIZATION_FLAGS}>) + if(NJOY_ENABLE_LTO) # If LTO is enabled, -march=native might also be needed at final link time + list(APPEND LINK_OPTIONS_LIST $<$:${${COMPILER_FLAGS_PREFIX}_NONPORTABLE_OPTIMIZATION_FLAGS}>) + endif() + endif() + + if(NJOY_APPENDED_COMPILE_FLAGS) + string(REPLACE " " ";" _temp_flags "${NJOY_APPENDED_COMPILE_FLAGS}") + list(APPEND COMPILE_OPTIONS_LIST ${_temp_flags}) + endif() + if(NJOY_APPENDED_LINK_FLAGS) + string(REPLACE " " ";" _temp_flags "${NJOY_APPENDED_LINK_FLAGS}") + list(APPEND LINK_OPTIONS_LIST ${_temp_flags}) + endif() + + target_compile_options(${TARGET_NAME} PRIVATE ${COMPILE_OPTIONS_LIST}) + if(LINK_OPTIONS_LIST) # Only call if list is not empty + target_link_options(${TARGET_NAME} PRIVATE ${LINK_OPTIONS_LIST}) endif() +endfunction() + + +if(SKBUILD) + # By default, scikit-build will install everything to ${SKBUILD_PLATLIB_DIR}/njoy + # Install include and bin directories to ${SKBUILD_PLATLIB_DIR}/njoy/SKBUILD_SUBDIR + set(SKBUILD_SUBDIR core/) + set(CMAKE_INSTALL_BINDIR ${SKBUILD_SUBDIR}${CMAKE_INSTALL_BINDIR}) + set(CMAKE_INSTALL_INCLUDEDIR ${SKBUILD_SUBDIR}${CMAKE_INSTALL_INCLUDEDIR}) + set(CMAKE_INSTALL_LIBDIR ${SKBUILD_SUBDIR}${CMAKE_INSTALL_LIBDIR}) + set(CMAKE_INSTALL_DATADIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_DATADIR}) + set(CMAKE_INSTALL_DOCDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_DOCDIR}) + set(CMAKE_INSTALL_INFODIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_INFODIR}) + set(CMAKE_INSTALL_MANDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_MANDIR}) + set(CMAKE_INSTALL_LOCALEDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_LOCALEDIR}) + set(CMAKE_INSTALL_LOCALSTATEDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_LOCALSTATEDIR}) + set(CMAKE_INSTALL_RUNSTATEDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_RUNSTATEDIR}) + + # Set RPATH + if(APPLE) + set(CMAKE_MACOSX_RPATH ON) + set(NJOY_LIBRARY_RPATH "@loader_path") + set(NJOY_BINARY_RPATH "@loader_path/../../${CMAKE_INSTALL_LIBDIR}") + elseif(UNIX) + set(NJOY_LIBRARY_RPATH "$ORIGIN") + set(NJOY_BINARY_RPATH "$ORIGIN/../../${CMAKE_INSTALL_LIBDIR}") + endif() +else() + # use, i.e. don't skip the full RPATH for the build tree + set(CMAKE_SKIP_BUILD_RPATH FALSE) + + # when building, don't use the install RPATH already + # (but later on when installing) + set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) + + # the RPATH to be used when installing, but only if it's not a system directory + list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemDir) + if("${isSystemDir}" STREQUAL "-1") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") + endif() endif() -execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_BRANCH - OUTPUT_STRIP_TRAILING_WHITESPACE -) -execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse HEAD - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE +# add the automatically determined parts of the RPATH +# which point to directories outside the build tree to the install RPATH +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +# Feature Summary Setup for NJOY Options +add_feature_info("Strict Warnings As Errors" NJOY_STRICT "Compile time warnings are converted to errors.") +add_feature_info("Test Coverage (Debug)" NJOY_COVERAGE "Enable binary instrumentation for test coverage.") +add_feature_info("Profiling Generation (Release)" NJOY_PROFILE_GENERATE "Enable binary instrumentation for execution profiling.") +add_feature_info("Profiling Use (Release)" NJOY_PROFILE_USE "Leverage previously generated execution profile for optimization.") +add_feature_info("CMake LTO/IPO (Release)" NJOY_ENABLE_LTO "Enable Link Time Optimization via CMake's INTERPROCEDURAL_OPTIMIZATION property (Release config).") +add_feature_info("Non-Portable Optimizations (Release)" NJOY_NONPORTABLE_OPTIMIZATION "Enable optimizations compromising binary portability (e.g., -march=native).") +add_feature_info("Build NJOY as Shared Library" BUILD_SHARED_LIBS "Build NJOY as a shared library.") +add_feature_info("Link Executable Statically" NJOY_LINK_EXECUTABLE_STATICALLY "Attempt to link the NJOY executable statically (e.g., using -static).") + +# Determine NJOY Library type for messaging +if(BUILD_SHARED_LIBS) + set(NJOY_LIBRARY_TYPE_MESSAGE "SHARED (BUILD_SHARED_LIBS=ON)") +else() + set(NJOY_LIBRARY_TYPE_MESSAGE "STATIC (BUILD_SHARED_LIBS=OFF)") +endif() + +# Configuration Summary +message(STATUS "") +message(STATUS "-----------------------------------------------------------") +message(STATUS " ${CMAKE_PROJECT_NAME} Project Configuration") +message(STATUS "-----------------------------------------------------------") +message(STATUS " Project Source Directory: ${PROJECT_SOURCE_DIR}") +message(STATUS " Project Binary Directory: ${PROJECT_BINARY_DIR}") +message(STATUS " Installation Prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS " Install Binaries to: ${CMAKE_INSTALL_FULL_BINDIR}") +message(STATUS " Install Libraries to: ${CMAKE_INSTALL_FULL_LIBDIR}") +message(STATUS " Install Modules/Includes to: ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${CMAKE_PROJECT_NAME_LOWER}") +message(STATUS " Fortran Compiler: ${CMAKE_Fortran_COMPILER_ID} ${CMAKE_Fortran_COMPILER_VERSION}") +message(STATUS " Build types: ${CMAKE_CONFIGURATION_TYPES}") +message(STATUS " NJOY Library Type: ${NJOY_LIBRARY_TYPE_MESSAGE}") +message(STATUS " NJOY LTO/IPO Setting: ${NJOY_ENABLE_LTO}") +message(STATUS "-----------------------------------------------------------") +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) +message(STATUS "-----------------------------------------------------------") + +set(NJOY_LIBRARY_SOURCES + "src/acecm.f90" "src/acedo.f90" "src/acefc.f90" "src/acepa.f90" "src/acepn.f90" + "src/acer.f90" "src/aceth.f90" "src/broadr.f90" "src/ccccr.f90" "src/covr.f90" + "src/dtfr.f90" "src/endf.f90" "src/errorr.f90" "src/gaminr.f90" "src/gaspr.f90" + "src/graph.f90" "src/groupr.f90" "src/heatr.f90" "src/leapr.f90" "src/locale.f90" + "src/mainio.f90" "src/mathm.f90" "src/matxsr.f90" "src/mixr.f90" "src/moder.f90" + "src/phys.f90" "src/plotr.f90" "src/powr.f90" "src/purr.f90" "src/reconr.f90" + "src/resxsr.f90" "src/samm.f90" "src/thermr.f90" "src/unresr.f90" "src/util.f90" + "src/vers.f90" "src/viewr.f90" "src/wimsr.f90" ) -message( STATUS "" ) -message( STATUS "-----------------------------------------------------------" ) -message( STATUS "" ) -message( STATUS "njoy" ) -message( STATUS "Git current branch: ${GIT_BRANCH}" ) -message( STATUS "Git commit hash: ${GIT_HASH}" ) -message( STATUS "" ) -message( STATUS "-----------------------------------------------------------" ) - -add_library( njoy ${njoy_library_linkage} - "${CMAKE_CURRENT_SOURCE_DIR}/src/acecm.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/acedo.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/acefc.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/acepa.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/acepn.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/acer.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/aceth.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/broadr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/ccccr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/covr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/dtfr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/endf.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/errorr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/gaminr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/gaspr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/graph.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/groupr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/heatr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/leapr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/locale.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/main.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/mainio.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/mathm.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/matxsr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/mixr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/moder.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/phys.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/plotr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/powr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/purr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/reconr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/resxsr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/samm.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/thermr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/unresr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/util.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/vers.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/viewr.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/src/wimsr.f90" ) - -target_include_directories( njoy PUBLIC "${CMAKE_Fortran_MODULE_DIRECTORY}" ) - -set( PREFIX njoy_${CMAKE_Fortran_COMPILER_ID}_${CMAKE_SYSTEM_NAME} ) - -target_compile_options( njoy PRIVATE -${${PREFIX}_common_flags} -$<$:${${PREFIX}_njoy_strict_flags}> -$<$:${${PREFIX}_static_flags}> -$<$:${${PREFIX}_subproject_flags}> -$<$>:${${PREFIX}_base_project_flags}> -$<$: -${${PREFIX}_DEBUG_flags} -$<$:${${PREFIX}_coverage_flags}>> -$<$: -${${PREFIX}_RELEASE_flags} -$<$:${${PREFIX}_profile_generate_flags}> -$<$:${${PREFIX}_profile_use_flags}> -$<$:${${PREFIX}_link_time_optimization_flags}> -$<$:${${PREFIX}_nonportable_optimization_flags}>> -${Fortran_appended_flags} ${njoy_appended_flags} ) - -target_link_libraries( njoy PUBLIC "$<$,$>:${${PREFIX}_RELEASE_flags};${${PREFIX}_link_time_optimization_flags}$<$:${${PREFIX}_profile_generate_flags};>$<$:${${PREFIX}_profile_use_flags};>$<$:${${PREFIX}_nonportable_optimization_flags};>>$<$:$<$:${${PREFIX}_coverage_flags};>>$<$:${Fortran_appended_flags};>$<$:${njoy_appended_flags};>" ) - -if ( NOT is_subproject ) - add_executable( njoy_executable src/main.f90 ) - set_target_properties( njoy_executable PROPERTIES OUTPUT_NAME njoy ) - target_compile_options( njoy_executable PRIVATE - ${${PREFIX}_common_flags} - $<$:${${PREFIX}_njoy_strict_flags}> - $<$:${${PREFIX}_static_flags}> - $<$:${${PREFIX}_subproject_flags}> - $<$>:${${PREFIX}_base_project_flags}> - $<$: - ${${PREFIX}_DEBUG_flags} - $<$:${${PREFIX}_coverage_flags}>> - $<$: - ${${PREFIX}_RELEASE_flags} - $<$:${${PREFIX}_profile_generate_flags}> - $<$:${${PREFIX}_profile_use_flags}> - $<$:${${PREFIX}_link_time_optimization_flags}> - $<$:${${PREFIX}_nonportable_optimization_flags}>> - ${Fortran_appended_flags} ${njoy_appended_flags} ) - target_link_libraries( njoy_executable PUBLIC njoy ) +# Add NJOY library. Type (SHARED/STATIC) is controlled by BUILD_SHARED_LIBS. +add_library(${CMAKE_PROJECT_NAME} ${NJOY_LIBRARY_SOURCES}) +set_target_properties(${CMAKE_PROJECT_NAME} + PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_Fortran_MODULE_DIRECTORY}" + ) + +# Set RPATH for the library +if(NJOY_LIBRARY_RPATH) + set_target_properties(${CMAKE_PROJECT_NAME} + PROPERTIES + INSTALL_RPATH "${NJOY_LIBRARY_RPATH}" + ) endif() -if( NOT is_subproject ) +target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC + $ # For build tree consumption + $ # For installed consumption +) +njoy_apply_build_options(${CMAKE_PROJECT_NAME}) + +if(NOT NJOY_IS_SUBPROJECT) + add_executable(${CMAKE_PROJECT_NAME}_executable src/main.f90) + set_target_properties(${CMAKE_PROJECT_NAME}_executable + PROPERTIES + OUTPUT_NAME ${CMAKE_PROJECT_NAME_LOWER} + ) + + # Set RPATH for the executable + if(NJOY_BINARY_RPATH) + set_target_properties(${CMAKE_PROJECT_NAME}_executable + PROPERTIES + INSTALL_RPATH "${NJOY_BINARY_RPATH}" + ) + endif() + target_link_libraries(${CMAKE_PROJECT_NAME}_executable PRIVATE ${CMAKE_PROJECT_NAME}) + njoy_apply_build_options(${CMAKE_PROJECT_NAME}_executable) + + # Generate and install the Python script for the executable + generate_and_install_python_script(${CMAKE_PROJECT_NAME_LOWER}) + + if(NJOY_LINK_EXECUTABLE_STATICALLY) + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + message(WARNING "NJOY_LINK_EXECUTABLE_STATICALLY=ON: Fully static executables are not well-supported on macOS and may lead to issues or failures. Prefer dynamic linking or linking a static NJOY library to a dynamically linked executable.") + endif() + + set(_compiler_static_link_flags_var "NJOY_${CMAKE_Fortran_COMPILER_ID}_STATIC_LINK_FLAGS") + if(DEFINED ${_compiler_static_link_flags_var} AND NOT "${${_compiler_static_link_flags_var}}" STREQUAL "") + message(STATUS "Applying static link flags to executable ${CMAKE_PROJECT_NAME}_executable: ${${_compiler_static_link_flags_var}}") + target_link_options(${CMAKE_PROJECT_NAME}_executable PRIVATE ${${_compiler_static_link_flags_var}}) + else() + message(WARNING "NJOY_LINK_EXECUTABLE_STATICALLY is ON, but no specific static link flags (NJOY_${CMAKE_Fortran_COMPILER_ID}_STATIC_LINK_FLAGS) are defined for compiler ${CMAKE_Fortran_COMPILER_ID}. The executable might not be fully static.") + endif() + endif() + enable_testing() - add_subdirectory( tests ) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/tests") + add_subdirectory(tests) + else() + message(WARNING "Tests directory not found at ${CMAKE_CURRENT_SOURCE_DIR}/tests. Skipping tests setup.") + endif() endif() -set( installation_targets njoy ) -if ( NOT is_subproject ) - list( APPEND installation_targets njoy_executable ) +# Installation +set(NJOY_INSTALLATION_TARGETS ${CMAKE_PROJECT_NAME}) +if(NOT NJOY_IS_SUBPROJECT) + list(APPEND NJOY_INSTALLATION_TARGETS ${CMAKE_PROJECT_NAME}_executable) endif() -install( TARGETS ${installation_targets} - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ - WORLD_EXECUTE WORLD_READ ) - -file( RELATIVE_PATH relative_fortran_module_files_path - "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_Fortran_MODULE_DIRECTORY}" ) -file( GLOB fortran_module_files - RELATIVE "${relative_fortran_module_files_path}" - *.mod ) -install( FILES ${fortran_module_files} - DESTINATION include - PERMISSIONS OWNER_READ OWNER_WRITE - GROUP_READ - WORLD_READ ) +install(TARGETS ${NJOY_INSTALLATION_TARGETS} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ +) +install(DIRECTORY "${CMAKE_Fortran_MODULE_DIRECTORY}/" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME_LOWER} + FILES_MATCHING PATTERN "*.mod" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ +) \ No newline at end of file diff --git a/cmake/GenerateScript.cmake b/cmake/GenerateScript.cmake new file mode 100644 index 00000000..355aea4f --- /dev/null +++ b/cmake/GenerateScript.cmake @@ -0,0 +1,33 @@ +# Function to generate and install a Python script +# This function creates a Python script with a specified name, writes content to it, +# and installs it to a specified location. +# +# Arguments: +# SCRIPT_NAME - The name of the script to be generated (e.g., 'my_script'). + +function(generate_and_install_python_script SCRIPT_NAME) + if(SKBUILD) + # Strip any leading/trailing whitespace from the script name + string(STRIP "${SCRIPT_NAME}" CLEAN_SCRIPT_NAME) + + # Define the output path for the generated script using the cleaned name + set(GENERATED_SCRIPT "${CMAKE_BINARY_DIR}/scripts/${CLEAN_SCRIPT_NAME}") + + # Generate the script content directly + file(WRITE "${GENERATED_SCRIPT}" "#!/usr/bin/env python3\n\n") + file(APPEND "${GENERATED_SCRIPT}" "import os\n") + file(APPEND "${GENERATED_SCRIPT}" "import sys\n") + file(APPEND "${GENERATED_SCRIPT}" "import sysconfig\n\n") + file(APPEND "${GENERATED_SCRIPT}" "if __name__ == '__main__':\n") + file(APPEND "${GENERATED_SCRIPT}" " os.execv(\n") + file(APPEND "${GENERATED_SCRIPT}" " os.path.join(sysconfig.get_path('platlib'), '${SKBUILD_PROJECT_NAME}', '${CMAKE_INSTALL_BINDIR}', '${CLEAN_SCRIPT_NAME}'),\n") + file(APPEND "${GENERATED_SCRIPT}" " sys.argv,\n") + file(APPEND "${GENERATED_SCRIPT}" " )\n") + + # Install the generated script + install( + PROGRAMS "${GENERATED_SCRIPT}" + DESTINATION "${SKBUILD_SCRIPTS_DIR}" + ) + endif() +endfunction() diff --git a/njoy/__init__.py b/njoy/__init__.py new file mode 100644 index 00000000..f7b20046 --- /dev/null +++ b/njoy/__init__.py @@ -0,0 +1,7 @@ +from importlib.metadata import version, PackageNotFoundError +from .paths import * + +try: + __version__ = version('njoy') +except PackageNotFoundError: + __version__ = "unknown" \ No newline at end of file diff --git a/njoy/paths.py b/njoy/paths.py new file mode 100644 index 00000000..d8961184 --- /dev/null +++ b/njoy/paths.py @@ -0,0 +1,61 @@ +import os +import sys +import glob +import warnings +from . import __path__ + +CORE_BASE_PATH = os.path.join(__path__[0], "core") + +if not os.path.exists(CORE_BASE_PATH): + import sysconfig + CORE_BASE_PATH = os.path.join(sysconfig.get_path("platlib"), "njoy", "core") + if not os.path.exists(CORE_BASE_PATH): + raise ImportError("NJOY is not installed. Please run 'pip install njoy'.") + if os.environ.get("NJOY_DEV_MODE", "0") != "1": + warnings.warn( + "It seems that NJOY is being run from its source directory. " + "This setup is not recommended as it may lead to unexpected behavior, " + "such as conflicts between source and installed versions. " + "Please run your script from outside the NJOY source tree.", + RuntimeWarning + ) + +def get_paths(subdir, pattern="*", recursive=False): + """ + Helper function to return paths that match a given pattern within a subdirectory. + + Args: + subdir (str): The subdirectory within the 'core' directory. + pattern (str): The pattern to match files or directories. + recursive (bool): Whether to search recursively in subdirectories. + + Returns: + list: A list of matched paths. + """ + search_pattern = os.path.join(CORE_BASE_PATH, subdir, "**", pattern) if recursive else os.path.join(CORE_BASE_PATH, subdir, pattern) + return glob.glob(search_pattern, recursive=recursive) + +def get_include_path(): + """Return includes and include path for NJOY headers.""" + include = get_paths("include", "*", recursive=True) + include_path = get_paths("include", "", recursive=False) + return include, include_path + +def get_core_libraries(): + """Return libraries and library paths for NJOY.""" + lib = [lib_file for lib in ["lib", "lib64"] for lib_file in get_paths(lib, "libNJOY*", recursive=True)] + lib_path = [lib_file for lib in ["lib", "lib64"] for lib_file in get_paths(lib, "", recursive=False)] + return lib, lib_path + +def get_extra_libraries(): + """Return the extra libraries installed by auditwheel or delocate.""" + libs_path = os.path.join(__path__[0], ".dylibs") if sys.platform == "darwin" else os.path.normpath(os.path.join(__path__[0], "..", "njoy.libs")) + return (glob.glob(os.path.join(libs_path, "*")), libs_path) if os.path.exists(libs_path) else ([], []) + +# Setup variables +include, include_path = get_include_path() +lib, lib_path = get_core_libraries() +extra_lib, extra_lib_path = get_extra_libraries() + +# Export variables for easy access +__all__ = ["include", "include_path", "lib", "lib_path", "extra_lib", "extra_lib_path"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f00fd11e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[build-system] +requires = ["scikit-build-core", "setuptools-scm"] +build-backend = "scikit_build_core.build" + +[project] +name = "njoy" +description = "NJOY is a modular code that processes ENDF nuclear data into libraries for applications, with independent modules communicating via files." +authors = [{ name = "The NJOY Development Team", email = "njoy@lanl.gov" }] +license = { file = "LICENSE" } +readme = "README.md" +requires-python = ">=3.8" +dynamic = ["version"] + +keywords = [ + "Nuclear Data", + "ENDF", + "Nuclear Engineering", + "Nuclear Physics", + "Monte Carlo", + "Neutron Transport", +] + +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD 3-Clause License", + "Natural Language :: English", + "Topic :: Scientific/Engineering", + "Programming Language :: Fortran", + "Programming Language :: Python :: 3", +] + +[project.urls] +Homepage = "https://www.njoy21.io/NJOY2016/" +Documentation = "https://www.njoy21.io/NJOY2016/" +Repository = "https://github.com/njoy/NJOY2016" +Issues = "https://github.com/njoy/NJOY2016/issues" + +[tool.scikit-build] +build.verbose = true +logging.level = "INFO" +wheel.packages = ["njoy"] +wheel.install-dir = "njoy" +metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" + +[tool.setuptools_scm]