Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build-and-publish-docs-on-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ jobs:
python_version: 0

docs:
needs: get-python-version
uses: scikit-package/release-scripts/.github/workflows/_release-docs.yml@v0
with:
project: diffpy.srreal
submodules: recursive
c_extension: true
headless: false
python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }}
63 changes: 51 additions & 12 deletions .github/workflows/build-wheel-release-upload.yml
Original file line number Diff line number Diff line change
@@ -1,38 +1,77 @@
name: Build Wheel and Release

# Trigger on tag push or manual dispatch.
# Tag and release privilege are verified inside the reusable workflow.
# Tag and release privilege are verified by the check-* jobs below.
on:
workflow_dispatch:
push:
tags:
- "*"

# ── Release modality ──────────────────────────────────────────────────────────
# Three options are provided below. Only ONE job should be active at a time.
# To switch: comment out the active job and uncomment your preferred option,
# Three options are provided below. Only ONE option should be active at a time.
# To switch: comment out the active jobs and uncomment your preferred option,
# then commit the change to main before tagging a release.
# ─────────────────────────────────────────────────────────────────────────────

jobs:
# Option 1 (default): Release to GitHub, publish to PyPI, and deploy docs.
#
# The wheel is uploaded to PyPI so users can install with `pip install`.
# A GitHub release is created with the changelog as the release body, and
# the Sphinx documentation is rebuilt and deployed to GitHub Pages.
# The sdist and wheels are uploaded to PyPI so users can install with
# `pip install`. A GitHub release is created with the changelog as the
# release body, and the Sphinx documentation is rebuilt and deployed to
# GitHub Pages.
#
# Choose this for open-source packages distributed via PyPI and/or
# conda-forge where broad public availability is the goal.
build-release:
uses: scikit-package/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0
# The wheels come from build-wheels.yml rather than the shared
# scikit-package workflow: they link Boost and GSL statically, so they
# work without a conda environment.
check-tag-privilege:
uses: scikit-package/release-scripts/.github/workflows/_check-tag-privilege.yml@v0
with:
project: diffpy.srreal
c_extension: true
maintainer_github_username: sbillinge

check-pat-token-validity:
uses: scikit-package/release-scripts/.github/workflows/_check-pat-token-validity.yml@v0
secrets:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}

check-pypi-token-validity:
uses: scikit-package/release-scripts/.github/workflows/_check-pypi-token-validity.yml@v0
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}

get-python-version:
needs: [check-tag-privilege, check-pat-token-validity, check-pypi-token-validity]
uses: scikit-package/release-scripts/.github/workflows/_get-python-version-latest.yml@v0

build-wheels:
needs: [check-tag-privilege, check-pat-token-validity, check-pypi-token-validity]
uses: ./.github/workflows/build-wheels.yml

release-github:
needs: [build-wheels]
uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0
secrets:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}

# Uploads every artifact of this run: the sdist and all wheels.
release-pypi:
needs: [release-github, get-python-version]
uses: scikit-package/release-scripts/.github/workflows/_release-pypi.yml@v0
with:
python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }}
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}

docs-release:
needs: [release-github, get-python-version]
uses: scikit-package/release-scripts/.github/workflows/_release-docs.yml@v0
with:
submodules: recursive
project: diffpy.srreal
c_extension: true
python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }}

# Option 2: Release to GitHub and deploy docs, without publishing to PyPI.
#
# A GitHub release is created and the Sphinx docs are deployed, but the
Expand Down
60 changes: 60 additions & 0 deletions .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Build Wheels

# Builds the sdist and self-contained wheels (Boost and GSL linked
# statically, see [tool.cibuildwheel] in pyproject.toml). Called by the
# release workflow; run it manually to check the wheels before tagging.
on:
workflow_call:
workflow_dispatch:

jobs:
build-sdist:
name: Build sdist
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"

- name: Build sdist
run: |
python -m pip install --upgrade build
python -m build --sdist

- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz

build-wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-15-intel
- macos-latest
- windows-latest
steps:
- name: Check out
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0

- name: Build and test wheels
uses: pypa/cibuildwheel@v4.3.5

- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}
path: wheelhouse/*.whl
7 changes: 1 addition & 6 deletions .github/workflows/matrix-and-codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,8 @@ jobs:
uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0
with:
project: diffpy.srreal
submodules: recursive
c_extension: true
headless: false
run: |
set -e
if [[ $(uname -s) == Linux ]]; then
python -m pip install git+https://github.com/diffpy/pyobjcryst.git@nanobind_migration_test
python -c 'import pyobjcryst._pyobjcryst'
fi
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
7 changes: 1 addition & 6 deletions .github/workflows/tests-on-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ jobs:
uses: scikit-package/release-scripts/.github/workflows/_tests-on-pr.yml@v0
with:
project: diffpy.srreal
submodules: recursive
c_extension: true
headless: false
run: |
set -e
if [[ $(uname -s) == Linux ]]; then
python -m pip install git+https://github.com/diffpy/pyobjcryst.git@nanobind_migration_test
python -c 'import pyobjcryst._pyobjcryst'
fi
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "extern/libdiffpy"]
path = extern/libdiffpy
url = https://github.com/diffpy/libdiffpy.git
88 changes: 84 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,58 @@
cmake_minimum_required(VERSION 3.20)
project(diffpy_srreal LANGUAGES CXX)
project(diffpy_srreal LANGUAGES NONE)

# Keep this aligned with the pinned libdiffpy's src/SConscript.
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

set(LIBDIFFPY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/extern/libdiffpy")
if(NOT EXISTS "${LIBDIFFPY_SOURCE_DIR}/src/diffpy/version.tpl")
message(FATAL_ERROR
"Missing libdiffpy sources. Initialize the submodule with "
"'git submodule update --init --recursive', or use a source distribution "
"that includes extern/libdiffpy."
)
endif()

# Read version information from the submodule; sdists carry the generated header.
set(LIBDIFFPY_VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/include/diffpy/version.hpp")
set(LIBDIFFPY_SDIST_VERSION_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/_build/libdiffpy-version.hpp")
if(EXISTS "${LIBDIFFPY_SOURCE_DIR}/.git")
file(STRINGS "${LIBDIFFPY_SOURCE_DIR}/site_scons/fallback_version.py"
LIBDIFFPY_FALLBACK_VERSION REGEX "^FALLBACK_VERSION =")
if(NOT LIBDIFFPY_FALLBACK_VERSION MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
message(FATAL_ERROR "Cannot read the libdiffpy base version.")
endif()
set(DIFFPY_VERSION_MAJOR "${CMAKE_MATCH_1}")
set(DIFFPY_VERSION_MINOR "${CMAKE_MATCH_2}")
set(DIFFPY_VERSION_MICRO "${CMAKE_MATCH_3}")
set(DIFFPY_VERSION_PATCH 0)
set(DIFFPY_VERSION_STR "${DIFFPY_VERSION_MAJOR}.${DIFFPY_VERSION_MINOR}.${DIFFPY_VERSION_MICRO}")
math(EXPR DIFFPY_VERSION "${DIFFPY_VERSION_MAJOR} * 1000000000 + ${DIFFPY_VERSION_MINOR} * 1000000 + ${DIFFPY_VERSION_MICRO} * 1000")
set(DIFFPY_VERSION "${DIFFPY_VERSION}LL")
find_package(Git REQUIRED)
execute_process(
COMMAND "${GIT_EXECUTABLE}" -C "${LIBDIFFPY_SOURCE_DIR}" log -1 "--format=%H;%ci"
OUTPUT_VARIABLE LIBDIFFPY_COMMIT_INFO
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY
)
list(GET LIBDIFFPY_COMMIT_INFO 0 DIFFPY_GIT_SHA)
list(GET LIBDIFFPY_COMMIT_INFO 1 DIFFPY_VERSION_DATE)
configure_file("${LIBDIFFPY_SOURCE_DIR}/src/diffpy/version.tpl"
"${LIBDIFFPY_VERSION_HEADER}")
else()
configure_file("${LIBDIFFPY_SDIST_VERSION_HEADER}"
"${LIBDIFFPY_VERSION_HEADER}" COPYONLY)
endif()
if(SKBUILD_STATE STREQUAL "sdist")
configure_file("${LIBDIFFPY_VERSION_HEADER}"
"${LIBDIFFPY_SDIST_VERSION_HEADER}" COPYONLY)
return()
endif()
enable_language(CXX)

# Conda is optional; system packages and CMAKE_PREFIX_PATH work as well.
if(DEFINED ENV{CONDA_PREFIX})
list(APPEND CMAKE_PREFIX_PATH "$ENV{CONDA_PREFIX}" "$ENV{CONDA_PREFIX}/Library")
Expand All @@ -19,12 +66,35 @@ execute_process(
COMMAND_ERROR_IS_FATAL ANY
)
find_package(nanobind 3.1...<4 CONFIG REQUIRED)

# PyPI wheels link Boost and GSL statically so they install without conda or
# system libraries. Conda and system builds keep the default shared linking.
option(SRREAL_STATIC_DEPS "Link Boost.Serialization and GSL statically" OFF)
set(SRREAL_DEPS_LICENSE_DIR "" CACHE PATH
"Third-party license files to ship in the wheel with SRREAL_STATIC_DEPS")

if(SRREAL_STATIC_DEPS)
set(Boost_USE_STATIC_LIBS ON)
endif()
find_package(Boost CONFIG REQUIRED COMPONENTS serialization)

if(SRREAL_STATIC_DEPS AND NOT WIN32)
# FindGSL has no static switch; restrict the search to static archives.
set(_srreal_saved_suffixes "${CMAKE_FIND_LIBRARY_SUFFIXES}")
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
endif()
find_package(GSL REQUIRED)
if(DEFINED _srreal_saved_suffixes)
set(CMAKE_FIND_LIBRARY_SUFFIXES "${_srreal_saved_suffixes}")
get_target_property(_srreal_boost_lib Boost::serialization LOCATION)
foreach(_srreal_lib IN ITEMS "${_srreal_boost_lib}" "${GSL_LIBRARY}" "${GSL_CBLAS_LIBRARY}")
if(NOT _srreal_lib MATCHES "\\.a$")
message(FATAL_ERROR
"SRREAL_STATIC_DEPS requires static libraries, found ${_srreal_lib}")
endif()
endforeach()
endif()

include(cmake/FetchLibdiffpy.cmake)
configure_file("${LIBDIFFPY_SOURCE_DIR}/src/diffpy/version.tpl"
"include/diffpy/version.hpp")
set(DIFFPY_HAS_OBJCRYST 0)
configure_file("${LIBDIFFPY_SOURCE_DIR}/src/diffpy/features.tpl"
"include/diffpy/features.hpp")
Expand All @@ -49,6 +119,10 @@ target_include_directories(diffpy_core PUBLIC
"${LIBDIFFPY_SOURCE_DIR}/src"
)
target_compile_definitions(diffpy_core PRIVATE DIFFPYRUNTIMERELPATH=data)
if(WIN32)
# libdiffpy and its Python wrappers are compiled into the same extension.
target_compile_definitions(diffpy_core PUBLIC BUILD_DLL _USE_MATH_DEFINES NOMINMAX)
endif()
target_link_libraries(diffpy_core PUBLIC Boost::serialization GSL::gsl GSL::gslcblas ${CMAKE_DL_LIBS})

file(GLOB SRREAL_SOURCES CONFIGURE_DEPENDS "src/extensions/*.cpp")
Expand All @@ -63,3 +137,9 @@ install(DIRECTORY "${LIBDIFFPY_SOURCE_DIR}/src/runtime/"
install(FILES "${LIBDIFFPY_SOURCE_DIR}/LICENSE.rst"
"${LIBDIFFPY_SOURCE_DIR}/LICENSE_DANSE.rst"
DESTINATION diffpy/srreal/licenses/libdiffpy)
# Statically linked Boost and GSL code ships inside the extension, so their
# licenses ship with it.
if(SRREAL_STATIC_DEPS AND SRREAL_DEPS_LICENSE_DIR)
install(DIRECTORY "${SRREAL_DEPS_LICENSE_DIR}/"
DESTINATION diffpy/srreal/licenses)
endif()
28 changes: 16 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,28 @@ To install using ``pip`` into your ``diffpy.srreal_env`` environment, type ::

pip install diffpy.srreal

If you prefer to install from sources, after installing the dependencies, obtain the source archive from
`GitHub <https://github.com/diffpy/diffpy.srreal/>`_. Once installed, ``cd`` into your ``diffpy.srreal`` directory
and run the following ::
To build from Git, install the dependencies, then clone the repository with
its libdiffpy submodule ::

git clone --recurse-submodules https://github.com/diffpy/diffpy.srreal.git
cd diffpy.srreal
pip install .

Source builds use CMake to download libdiffpy at the commit recorded in
``cmake/LibdiffpyVersion.cmake`` and verify the archive's SHA-256 checksum.
This applies to both repository checkouts and source distributions; no Git
submodule initialization is needed. A C++23 compiler, Boost.Serialization,
and GSL must be installed separately.
For an existing checkout, initialize or update the submodule before building ::

To build without downloading libdiffpy, extract the pinned archive or check
out that exact commit locally, then supply its absolute path ::
git submodule update --init --recursive

pip install . -Ccmake.define.FETCHCONTENT_SOURCE_DIR_LIBDIFFPY=/absolute/path/to/libdiffpy
The parent repository pins the libdiffpy commit in ``extern/libdiffpy``.
CMake compiles those sources directly without downloading them or updating
the submodule. Version information is read from the submodule and included
in source distributions as a generated header.

Other build dependencies must also be available for a fully offline build.
Packaged source distributions (sdists) include libdiffpy, so they can be
extracted and built with ``pip install .`` without Git or a separate
libdiffpy download. GitHub's automatically generated source archives omit
submodule contents; use a recursive clone or a packaged sdist instead.
A C++23 compiler, Boost.Serialization, GSL, and the Python build dependencies
must still be installed separately, including for an offline build.

Getting Started
---------------
Expand Down
24 changes: 0 additions & 24 deletions cmake/FetchLibdiffpy.cmake

This file was deleted.

13 changes: 0 additions & 13 deletions cmake/LibdiffpyVersion.cmake

This file was deleted.

Loading
Loading