From e0a05bbd1a8cb93942e6c0d2507dba8b18885ab2 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Mon, 1 Dec 2025 13:28:25 -0500 Subject: [PATCH] build(cross): macOS cross compilation macos cross compilation now works. macos binaries can be compiled on linux with osxcross installed and built --- .gitignore | 2 + build-config/fourdst/meson.build | 2 +- build-config/meson.build | 3 +- build-config/python/meson.build | 16 +++ build-config/sundials/cvode/meson.build | 66 ++++++++---- build-config/sundials/kinsol/meson.build | 33 ++++-- build-config/sundials/meson.build | 2 +- build-python/meson.build | 34 ++++-- cross/macos_arm64.ini | 7 +- meson.build | 6 ++ meson_options.txt | 1 + src/meson.build | 1 + tests/graphnet_sandbox/main.cpp | 1 - tests/graphnet_sandbox/meson.build | 2 +- utils/python/fetch_header_files.sh | 132 +++++++++++++++++++++++ 15 files changed, 264 insertions(+), 44 deletions(-) create mode 100644 build-config/python/meson.build create mode 100755 utils/python/fetch_header_files.sh diff --git a/.gitignore b/.gitignore index 1027882b..c20d7ec2 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,5 @@ meson-boost-test/ *.json *.xml *_pynucastro_network.py + +cross/python_includes diff --git a/build-config/fourdst/meson.build b/build-config/fourdst/meson.build index 04a160c2..c6696210 100644 --- a/build-config/fourdst/meson.build +++ b/build-config/fourdst/meson.build @@ -30,4 +30,4 @@ liblogging = fourdst_sp.get_variable('liblogging') if not get_option('unity-safe') libplugin = fourdst_sp.get_variable('libplugin') -endif \ No newline at end of file +endif diff --git a/build-config/meson.build b/build-config/meson.build index 8c41081a..bbc36e13 100644 --- a/build-config/meson.build +++ b/build-config/meson.build @@ -1,7 +1,8 @@ cmake = import('cmake') -subdir('fourdst') +subdir('python') +subdir('fourdst') subdir('sundials') subdir('cppad') diff --git a/build-config/python/meson.build b/build-config/python/meson.build new file mode 100644 index 00000000..b1afd178 --- /dev/null +++ b/build-config/python/meson.build @@ -0,0 +1,16 @@ +py_installation = import('python').find_installation('python3', pure: false) + + +if meson.is_cross_build() and host_machine.system() == 'darwin' + py_ver = get_option('python-target-version') + message('Cross build on Darwin, using python version ' + py_ver) + py_inc_dir = include_directories('../../cross/python_includes/python-' + py_ver + '/include/python' + py_ver) + py_dep = declare_dependency(include_directories: py_inc_dir) + py_module_prefix = '' + py_module_suffic = 'so' + meson.override_dependency('python3', py_dep) +else + py_dep = py_installation.dependency() + py_module_prefix = '' + py_module_suffic = 'so' +endif diff --git a/build-config/sundials/cvode/meson.build b/build-config/sundials/cvode/meson.build index b46397f6..1d976b94 100644 --- a/build-config/sundials/cvode/meson.build +++ b/build-config/sundials/cvode/meson.build @@ -6,9 +6,8 @@ cvode_cmake_options.add_cmake_defines({ 'CMAKE_C_FLAGS' : '-Wno-deprecated-declarations', 'BUILD_SHARED_LIBS' : 'OFF', 'BUILD_STATIC_LIBS' : 'ON', - 'CMAKE_BUILD_WITH_INSTALL_RPATH': 'ON', 'EXAMPLES_ENABLE_C': 'OFF', - 'CMAKE_POSITION_INDEPENDENT_CODE': 'ON' + 'CMAKE_POSITION_INDEPENDENT_CODE': true }) @@ -22,29 +21,56 @@ cvode_sp = cmake.subproject( options: cvode_cmake_options, ) -# For the core SUNDIALS library (SUNContext, etc.) -sundials_core_dep = cvode_sp.dependency('sundials_core_static') +sundials_core_tgt = cvode_sp.target('sundials_core_static') +sundials_cvode_tgt = cvode_sp.target('sundials_cvode_static') +sundials_nvecserial_tgt = cvode_sp.target('sundials_nvecserial_static') +sundials_sunmatrixdense_tgt = cvode_sp.target('sundials_sunmatrixdense_static') +sundials_sunlinsoldense_tgt = cvode_sp.target('sundials_sunlinsoldense_static') -# For the CVODE integrator library -sundials_cvode_dep = cvode_sp.dependency('sundials_cvode_static') +cvode_objs = [ + sundials_core_tgt.extract_all_objects(recursive: true), + sundials_cvode_tgt.extract_all_objects(recursive: true), + sundials_nvecserial_tgt.extract_all_objects(recursive: true), + sundials_sunmatrixdense_tgt.extract_all_objects(recursive: true), + sundials_sunlinsoldense_tgt.extract_all_objects(recursive: true), +] -# For the serial NVector library -sundials_nvecserial_dep = cvode_sp.dependency('sundials_nvecserial_static') +sundials_core_includes = cvode_sp.include_directories('sundials_core_static') +sundials_cvode_includes = cvode_sp.include_directories('sundials_cvode_static') +sundials_nvecserial_includes = cvode_sp.include_directories('sundials_nvecserial_static') +sundials_sunmatrixdense_includes = cvode_sp.include_directories('sundials_sunmatrixdense_static') +sundials_sunlinsoldense_includes = cvode_sp.include_directories('sundials_sunlinsoldense_static') -# For the dense matrix library -sundials_sunmatrixdense_dep = cvode_sp.dependency('sundials_sunmatrixdense_static') +cvode_includes = [ + sundials_core_includes, + sundials_cvode_includes, + sundials_nvecserial_includes, + sundials_sunmatrixdense_includes, + sundials_sunlinsoldense_includes +] -# For the dense linear solver library -sundials_sunlinsoldense_dep = cvode_sp.dependency('sundials_sunlinsoldense_static') -cvode_dep = declare_dependency( - dependencies: [ - sundials_core_dep, - sundials_cvode_dep, - sundials_nvecserial_dep, - sundials_sunmatrixdense_dep, - sundials_sunlinsoldense_dep, - ], +empty_cvode_file = configure_file( + output: 'cvode_dummy_ar.cpp', + command: ['echo'], + capture: true + ) + + + +libcvode_static = static_library( + 'cvode-static', + empty_cvode_file, + objects: cvode_objs, + include_directories: cvode_includes, + pic: true, + install: false +) + + +cvode_dep = declare_dependency( + link_with: libcvode_static, + include_directories: cvode_includes, ) diff --git a/build-config/sundials/kinsol/meson.build b/build-config/sundials/kinsol/meson.build index 9014a5f6..b14cbe74 100644 --- a/build-config/sundials/kinsol/meson.build +++ b/build-config/sundials/kinsol/meson.build @@ -7,9 +7,8 @@ kinsol_cmake_options.add_cmake_defines({ 'CMAKE_C_FLAGS' : '-Wno-deprecated-declarations', 'BUILD_SHARED_LIBS' : 'OFF', 'BUILD_STATIC_LIBS' : 'ON', - 'CMAKE_BUILD_WITH_INSTALL_RPATH': 'ON', 'EXAMPLES_ENABLE_C' : 'OFF', - 'CMAKE_POSITION_INDEPENDENT_CODE': 'ON' + 'CMAKE_POSITION_INDEPENDENT_CODE': true }) kinsol_cmake_options.add_cmake_defines({ @@ -22,11 +21,31 @@ kinsol_sp = cmake.subproject( options: kinsol_cmake_options, ) -sundials_kinsol_shared = kinsol_sp.dependency('sundials_kinsol_static') +sundials_kinsol_static_tgt = kinsol_sp.target('sundials_kinsol_obj_static') +kinsol_includes = kinsol_sp.include_directories('sundials_kinsol_obj_static') -kinsol_dep = declare_dependency( - dependencies: [ - sundials_kinsol_shared, - ] +kinsol_objs = [sundials_kinsol_static_tgt.extract_all_objects(recursive: false)] + +empty_kinsol_file = configure_file( + output: 'kinsol_dummy_ar.cpp', + command: ['echo'], + capture: true + ) + + +libkinsol_static = static_library( + 'kinsol_static', + empty_kinsol_file, + objects: kinsol_objs, + include_directories: kinsol_includes, + pic: true, + install: false ) + +kinsol_dep = declare_dependency( + link_with: libkinsol_static, + include_directories: kinsol_includes +) + + diff --git a/build-config/sundials/meson.build b/build-config/sundials/meson.build index e5cb6298..bf738c13 100644 --- a/build-config/sundials/meson.build +++ b/build-config/sundials/meson.build @@ -6,4 +6,4 @@ sundials_dep = declare_dependency( cvode_dep, kinsol_dep, ], -) \ No newline at end of file +) diff --git a/build-python/meson.build b/build-python/meson.build index d28b7862..88d67746 100644 --- a/build-python/meson.build +++ b/build-python/meson.build @@ -1,7 +1,6 @@ -# --- Python Extension Setup --- -py_installation = import('python').find_installation('python3', pure: false) gridfire_py_deps = [ + py_dep, pybind11_dep, const_dep, config_dep, @@ -9,9 +8,7 @@ gridfire_py_deps = [ gridfire_dep ] -py_mod = py_installation.extension_module( - '_gridfire', # Name of the generated .so/.pyd file (without extension) - sources: [ +py_sources = [ meson.project_source_root() + '/src/python/bindings.cpp', meson.project_source_root() + '/src/python/types/bindings.cpp', meson.project_source_root() + '/src/python/partition/bindings.cpp', @@ -29,11 +26,28 @@ py_mod = py_installation.extension_module( meson.project_source_root() + '/src/python/policy/bindings.cpp', meson.project_source_root() + '/src/python/policy/trampoline/py_policy.cpp', meson.project_source_root() + '/src/python/utils/bindings.cpp', - ], - dependencies : gridfire_py_deps, - install : true, - subdir: 'gridfire', -) + ] + + +if meson.is_cross_build() and host_machine.system() == 'darwin' + py_mod = shared_module( + '_gridfire', + sources: py_sources, + dependencies: gridfire_py_deps, + name_prefix: '', + name_suffix: 'so', + install: true, + install_dir: py_installation.get_install_dir() + '/gridfire' + ) +else + py_mod = py_installation.extension_module( + '_gridfire', # Name of the generated .so/.pyd file (without extension) + sources: py_sources, + dependencies : gridfire_py_deps, + install : true, + subdir: 'gridfire', + ) +endif py_installation.install_sources( diff --git a/cross/macos_arm64.ini b/cross/macos_arm64.ini index 33ea5f99..74dd72af 100644 --- a/cross/macos_arm64.ini +++ b/cross/macos_arm64.ini @@ -4,13 +4,16 @@ cpp = 'arm64-apple-darwin25-clang++' ar = 'arm64-apple-darwin25-ar' strip = 'arm64-apple-darwin25-strip' pkg-config = 'pkg-config' +ranlib = '/usr/bin/true' -[host-machine] +[host_machine] system = 'darwin' cpu_family = 'aarch64' -cpi = 'arm64' +cpu = 'arm64' endian = 'little' [built-in options] c_args = ['-mmacosx-version-min=15.0'] cpp_args = ['-mmacos-version-min=15.0'] +c_link_args = ['-mmacosx-version-min=15.0'] +cpp_link_args = ['-mmacos-version-min=15.0'] diff --git a/meson.build b/meson.build index f68c40c5..37ccfa3b 100644 --- a/meson.build +++ b/meson.build @@ -30,6 +30,7 @@ message('Found CXX compiler: ' + meson.get_compiler('cpp').get_id()) message('C++ standard set to: ' + get_option('cpp_std')) cppc = meson.get_compiler('cpp') +cc = meson.get_compiler('c') if cppc.get_id() == 'clang' @@ -72,6 +73,11 @@ if not cppc.has_header('format') endif +ignore_unused_args = '-Wno-unused-command-line-argument' + +add_global_arguments(ignore_unused_args, language: 'cpp') +add_global_arguments(ignore_unused_args, language: 'c') + # For Eigen diff --git a/meson_options.txt b/meson_options.txt index ec80a866..77cff5db 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,3 +5,4 @@ option('build-tests', type: 'boolean', value: true, description: 'build the test option('build-fortran', type: 'boolean', value: false, description: 'build fortran module support') option('unsafe-fortran', type: 'boolean', value: false, description: 'Allow untested fortran compilers (compilers other than gfortran)') option('unity-safe', type: 'boolean', value: false, description: 'Enable safe unity builds for better compatibility across different compilers and platforms') +option('python-target-version', type: 'string', value: '3.13', description: 'Target version for python compilation, only used for cross compilation') diff --git a/src/meson.build b/src/meson.build index aad8ffa2..ed52f2dc 100644 --- a/src/meson.build +++ b/src/meson.build @@ -51,6 +51,7 @@ libgridfire = library('gridfire', gridfire_sources, include_directories: include_directories('include'), dependencies: gridfire_build_dependencies, + objects: [cvode_objs, kinsol_objs], install : true) gridfire_dep = declare_dependency( diff --git a/tests/graphnet_sandbox/main.cpp b/tests/graphnet_sandbox/main.cpp index 217cfe38..f42ffa67 100644 --- a/tests/graphnet_sandbox/main.cpp +++ b/tests/graphnet_sandbox/main.cpp @@ -4,7 +4,6 @@ #include "gridfire/gridfire.h" #include "fourdst/composition/composition.h" -#include "fourdst/plugin/bundle/bundle.h" #include "fourdst/logging/logging.h" #include "fourdst/atomic/species.h" #include "fourdst/composition/utils.h" diff --git a/tests/graphnet_sandbox/meson.build b/tests/graphnet_sandbox/meson.build index 935eb66a..41e4300c 100644 --- a/tests/graphnet_sandbox/meson.build +++ b/tests/graphnet_sandbox/meson.build @@ -1,5 +1,5 @@ executable( 'graphnet_sandbox', 'main.cpp', - dependencies: [gridfire_dep, composition_dep, plugin_dep, cli11_dep], + dependencies: [gridfire_dep, composition_dep, cli11_dep], ) diff --git a/utils/python/fetch_header_files.sh b/utils/python/fetch_header_files.sh new file mode 100755 index 00000000..62aa5c69 --- /dev/null +++ b/utils/python/fetch_header_files.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +# --- Configuration --- +PYTHON_VERSIONS=("3.8.10" "3.9.13" "3.10.11" "3.11.9" "3.12.3" "3.13.0" "3.14.0") + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BASE_OUTPUT_DIR="$SCRIPT_DIR/../../cross/python_includes" + +# --- OS Detection --- +OS="$(uname -s)" +echo "Detected OS: $OS" + +# --- Dependency Check --- +check_dependencies() { + if [ "$OS" == "Linux" ]; then + if ! command -v 7z &> /dev/null; then + echo "Error: '7z' (p7zip-full) is required on Linux." + exit 1 + fi + if ! command -v cpio &> /dev/null; then + echo "Error: 'cpio' is required." + exit 1 + fi + fi +} + +# --- Extraction Logic (OS Specific) --- +extract_pkg() { + local pkg_file="$1" + local extract_root="$2" + local major_ver="$3" # e.g., 3.11 + + echo " -> Extracting..." + + if [ "$OS" == "Darwin" ]; then + pkgutil --expand "$pkg_file" "$extract_root/expanded" + local payload_path="$extract_root/expanded/Python_Framework.pkg/Payload" + + if [ ! -f "$payload_path" ]; then + echo " -> Error: Could not find Payload in package." + return 1 + fi + mkdir -p "$extract_root/root" + pushd "$extract_root/root" > /dev/null + cat "$payload_path" | gunzip | cpio -id "*include/python${major_ver}/*" 2>/dev/null + popd > /dev/null + + else + 7z x "$pkg_file" -o"$extract_root/expanded" -y > /dev/null + local payload_path="$extract_root/expanded/Python_Framework.pkg/Payload" + + if [ ! -f "$payload_path" ]; then + echo " -> Error: Could not find Payload in package." + return 1 + fi + + mkdir -p "$extract_root/root" + pushd "$extract_root/root" > /dev/null + cat "$payload_path" | gunzip | cpio -id "*include/python${major_ver}/*" 2>/dev/null + popd > /dev/null + fi +} + +check_dependencies + +mkdir -p "$BASE_OUTPUT_DIR" + +for FULL_VER in "${PYTHON_VERSIONS[@]}"; do + MAJOR_VER=$(echo "$FULL_VER" | cut -d. -f1,2) + + TARGET_DIR="$BASE_OUTPUT_DIR/python-$MAJOR_VER" + TEMP_DIR="$BASE_OUTPUT_DIR/tmp_$FULL_VER" + PKG_NAME="python-${FULL_VER}-macos11.pkg" + + if [[ "$MAJOR_VER" == "3.8" ]]; then + PKG_NAME="python-${FULL_VER}-macosx10.9.pkg" + fi + + DOWNLOAD_URL="https://www.python.org/ftp/python/${FULL_VER}/$PKG_NAME" + + echo "Processing Python $FULL_VER..." + + if [ -d "$TARGET_DIR" ] && [ "$(ls -A $TARGET_DIR)" ]; then + echo " -> Headers already exist in $TARGET_DIR. Skipping." + continue + fi + + mkdir -p "$TEMP_DIR" + + echo " -> Downloading from $DOWNLOAD_URL" + curl -L -s -o "$TEMP_DIR/python.pkg" "$DOWNLOAD_URL" + + if [ $? -ne 0 ]; then + echo " -> Download failed! Check version number or internet connection." + rm -rf "$TEMP_DIR" + continue + fi + + # 2. Extract + extract_pkg "$TEMP_DIR/python.pkg" "$TEMP_DIR" "$MAJOR_VER" + + # 3. Move Headers to Final Location + # The cpio extraction usually results in: ./Versions/X.Y/include/pythonX.Y + # We want to move that specific include folder to our target dir + + FOUND_HEADERS=$(find "$TEMP_DIR/root" -type d -path "*/include/python${MAJOR_VER}" | head -n 1) + + if [ -n "$FOUND_HEADERS" ]; then + echo " -> Found headers at: $FOUND_HEADERS" + + # Move the content to the final destination + # We want the folder to be .../python-3.11/include/python3.11 + mkdir -p "$TARGET_DIR/include" + mv "$FOUND_HEADERS" "$TARGET_DIR/include/" + + # Verify pyconfig.h exists (sanity check) + if [ -f "$TARGET_DIR/include/python${MAJOR_VER}/pyconfig.h" ]; then + echo " -> Success: Headers installed to $TARGET_DIR" + else + echo " -> Warning: Header move seemed successful, but pyconfig.h is missing." + fi + else + echo " -> Error: Could not locate header files after extraction." + fi + + # 4. Cleanup + rm -rf "$TEMP_DIR" + echo "---------------------------------------------------" +done + +echo "Done. All headers stored in $BASE_OUTPUT_DIR" +