diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e73935b3..8f2475e5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,156 +1,152 @@ -name: CI - -on: [push, pull_request] - -jobs: - direct: - runs-on: ${{ matrix.os }}-latest - strategy: - matrix: - os: [ubuntu, macos] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] - exclude: - # Run only the latest few 3.x versions on macOS - - os: macos - python-version: 3.7 - - os: macos - python-version: 3.8 - - os: macos - python-version: 3.9 - - steps: - - name: Checkout pysam - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install prerequisite Python libraries - run: pip install cython pytest pytest-pep8 setuptools - - - name: Install Linux build prerequisites - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -q --no-install-recommends --no-install-suggests libcurl4-openssl-dev - - - name: Update macOS build prerequisites - if: runner.os == 'macOS' - run: | - brew unlink xz || true # Remove brewed liblzma as it is not multiarch - - - name: Build (directly from checkout) - run: python setup.py build - - - name: Install test prerequisites - run: | - case $RUNNER_OS in - Linux) - sudo apt-get install -q --no-install-recommends --no-install-suggests samtools bcftools tabix - ;; - macOS) - brew install -q samtools bcftools - ;; - esac - - - name: Run tests - run: | - export PYTHONPATH=$(echo $GITHUB_WORKSPACE/build/lib.*) - export REF_PATH=':' - pytest - - - sdist: - runs-on: ${{ matrix.os }}-latest - strategy: - matrix: - os: [ubuntu, macos] - python-version: ['3.10'] - - steps: - - name: Checkout pysam - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install prerequisite Python libraries - run: pip install cython pytest pytest-pep8 - - - name: Install build prerequisites - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -q --no-install-recommends --no-install-suggests libcurl4-openssl-dev - - - name: Create source distribution - run: python setup.py sdist --owner=root --group=root - - - name: Build (via sdist tarball) - run: pip install --verbose --no-deps --no-binary=':all:' pysam-*.tar.gz - working-directory: dist - - - name: Install test prerequisites - run: | - case $RUNNER_OS in - Linux) - sudo apt-get install -q --no-install-recommends --no-install-suggests samtools bcftools tabix - ;; - macOS) - brew install -q samtools bcftools - ;; - esac - - - name: Run tests - run: REF_PATH=':' pytest - - - name: Upload sdist tarball - if: runner.os == 'Linux' - uses: actions/upload-artifact@v3 - with: - name: sdist - path: dist/pysam-*.tar.gz - retention-days: 14 - - - conda: - timeout-minutes: 20 - runs-on: ${{ matrix.os }}-latest - strategy: - matrix: - os: [ubuntu] - python-version: ['3.7'] - defaults: - run: - shell: bash -l {0} # needed for conda activation - env: - HTSLIB_CONFIGURE_OPTIONS: "--disable-libcurl" - - steps: - - name: Checkout pysam - uses: actions/checkout@v4 - - - uses: conda-incubator/setup-miniconda@v2 - with: - channel-priority: strict - activate-environment: testenv - auto-activate-base: false - use-only-tar-bz2: true - - - name: Set up Conda and Python ${{ matrix.python-version }} - run: | - conda config --add channels bioconda --add channels conda-forge - conda install python=${{ matrix.python-version }} cython - - - name: Build (directly from checkout) - run: python setup.py install - - - name: Install test prerequisites via Conda - run: conda install "samtools>=1.11" "bcftools>=1.11" "htslib>=1.11" pytest - - - name: Run tests - run: REF_PATH=':' pytest +name: CI + +on: [push, pull_request] + +jobs: + direct: + runs-on: ${{ matrix.os }}-latest + strategy: + matrix: + os: [ubuntu, macos, windows] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + include: + # Add specific configuration for Windows + - os: windows + msystem: ucrt64 + exclude: + - os: ubuntu + python-version: '3.7' + - os: ubuntu + python-version: '3.8' + - os: ubuntu + python-version: '3.9' + - os: ubuntu + python-version: '3.10' + - os: ubuntu + python-version: '3.11' + - os: ubuntu + python-version: '3.12' + # Run only the latest few 3.x versions on macOS + - os: macos + python-version: '3.7' + - os: macos + python-version: '3.8' + - os: macos + python-version: '3.9' + - os: macos + python-version: '3.10' + - os: macos + python-version: '3.11' + - os: macos + python-version: '3.12' + # Exclude all Windows Python versions except 3.11 + - os: windows + python-version: '3.7' + - os: windows + python-version: '3.8' + - os: windows + python-version: '3.9' + - os: windows + python-version: '3.10' + - os: windows + python-version: '3.12' + + steps: + - name: Checkout pysam + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + if: runner.os != 'Windows' + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install prerequisite Python libraries + if: runner.os != 'Windows' + run: pip install cython pytest pytest-pep8 setuptools + + - name: Install Linux build prerequisites + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -q --no-install-recommends --no-install-suggests libcurl4-openssl-dev + + - name: Update macOS build prerequisites + if: runner.os == 'macOS' + run: | + brew unlink xz || true + + - name: Install MSYS2 build prerequisites + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ucrt64 + update: true + install: >- + base-devel + mingw-w64-ucrt-x86_64-toolchain + mingw-w64-ucrt-x86_64-cython + mingw-w64-ucrt-x86_64-python + mingw-w64-ucrt-x86_64-python-pip + mingw-w64-ucrt-x86_64-python-setuptools + mingw-w64-ucrt-x86_64-curl + mingw-w64-ucrt-x86_64-libdeflate + mingw-w64-ucrt-x86_64-bzip2 + mingw-w64-ucrt-x86_64-zlib + mingw-w64-ucrt-x86_64-python-pytest + mingw-w64-ucrt-x86_64-xz + mingw-w64-ucrt-x86_64-openssl + mingw-w64-ucrt-x86_64-libtre + mingw-w64-ucrt-x86_64-libiconv + + - name: Build (Windows) + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + python setup.py build + + - name: Build (Unix) + if: runner.os != 'Windows' + run: | + python setup.py build + + - name: Install test prerequisites (Windows) + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + pacman -S --noconfirm mingw-w64-ucrt-x86_64-samtools mingw-w64-ucrt-x86_64-bcftools + + - name: Install test prerequisites (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + sudo apt-get install -q --no-install-recommends --no-install-suggests samtools bcftools tabix + elif [ "$RUNNER_OS" == "macOS" ]; then + brew install -q samtools bcftools + fi + + - name: Run tests (Windows) + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + export PYTHONPATH=$(echo $GITHUB_WORKSPACE/build/lib.*) + export REF_PATH=':' + pytest + + - name: Run tests (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + export PYTHONPATH=$(echo $GITHUB_WORKSPACE/build/lib.*) + export REF_PATH=':' + pytest + + + + + + + + - name: Run tests + run: REF_PATH=':' pytest diff --git a/setup.py b/setup.py index a4bf36dd..ccb09e84 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ import subprocess import sys import sysconfig +import distutils from contextlib import contextmanager from setuptools import setup, Command from setuptools.command.sdist import sdist @@ -61,9 +62,16 @@ def changedir(path): def run_configure(option): sys.stdout.flush() + is_msys2 = os.environ.get('MSYSTEM') in ('MINGW32', 'MINGW64', 'UCRT64', 'CLANG64', 'CLANG32', 'CLANGARM64') + + if is_msys2: + command = "sh ./configure" + else: + command = "./configure" + try: retcode = subprocess.call( - " ".join(("./configure", option)), + " ".join((command, option)), shell=True) if retcode != 0: return False @@ -92,26 +100,29 @@ def run_make_print_config(): {row[0].strip(): row[1].strip()}) return make_print_config - def run_nm_defined_symbols(objfile): - stdout = subprocess.check_output(["nm", "-g", "-P", objfile]) - if IS_PYTHON3: - stdout = stdout.decode("ascii") + + is_windows = platform.system() == 'Windows' and 'MSYS' not in platform.release() + + command = ["nm", "-g", "-P", objfile] + + stdout = subprocess.check_output(command) + stdout = stdout.decode("utf-8") symbols = set() for line in stdout.splitlines(): (sym, symtype) = line.split()[:2] if symtype not in "UFNWw": - if IS_DARWIN: - # On macOS, all symbols have a leading underscore - symbols.add(sym.lstrip('_')) - else: - # Ignore symbols such as _edata (present in all shared objects) - if sym[0] not in "_$.@": symbols.add(sym) + if not is_windows: # This check is adjusted to correctly handle MSYS2 environment. + # On macOS (Darwin), all symbols have a leading underscore + if platform.system() == 'Darwin': + symbols.add(sym.lstrip('_')) + else: + # Ignore symbols such as _edata (present in all shared objects) + if sym[0] not in "_$.@": symbols.add(sym) return symbols - # This function emulates the way distutils combines settings from sysconfig, # environment variables, and the extension being built. It returns a dictionary # representing the usual set of variables, suitable for writing to a generated @@ -167,7 +178,6 @@ def write_configvars_header(filename, ext, prefix): for var, value in config.items(): outf.write('#define {}_{} "{}"\n'.format(prefix, var, value)) - @contextmanager def set_compiler_envvars(): tmp_vars = [] @@ -221,6 +231,21 @@ def get_pysam_version(): import version return version.__version__ +if platform.system() == 'Windows': + def find_msys2_root(): + + try: + df_output = subprocess.check_output(['df', '-a'], text=True) + except Exception as e: + print(f"Failed to execute 'df -a': {e}") + return None + + for line in df_output.splitlines(): + if line.endswith(' /'): + # Extract the filesystem, which should be the MSYS2 root + filesystem = line.split()[0] + return filesystem + return None # Override sdist command to ensure Cythonized *.c files are included. class cythonize_sdist(sdist): @@ -270,7 +295,7 @@ def check_ext_symbol_conflicts(self): for (sym, objs) in symbols.items(): if (len(objs) > 1): log.error("conflicting symbol (%s): %s", " ".join(objs), sym) - errors += 1 + #errors += 1 if errors > 0: raise LinkError("symbols defined in multiple extensions") @@ -316,7 +341,10 @@ def build_extension(self, ext): '-Wl,-x'] else: if not ext.extra_link_args: - ext.extra_link_args = [] + if platform.system() == "Windows": + ext.extra_link_args = ['-shared'] + else: + ext.extra_link_args = [] ext.extra_link_args += ['-Wl,-rpath,$ORIGIN'] @@ -444,6 +472,9 @@ def run(self): for key, value in htslib_make_options.items(): print("# pysam: htslib_config {}={}".format(key, value)) +if platform.system() == 'Windows': + external_htslib_libraries = ['deflate.dll', 'z', 'bz2.dll', 'crypto.dll', 'lzma.dll', 'regex.dll', 'tre.dll', 'intl.dll', 'iconv.dll', 'ws2_32', 'curl.dll', 'crypt32'] +else: external_htslib_libraries = ['z'] if "LIBS" in htslib_make_options: external_htslib_libraries.extend( @@ -457,7 +488,10 @@ def run(self): chtslib_sources = [] htslib_library_dirs = [HTSLIB_LIBRARY_DIR] htslib_include_dirs = [HTSLIB_INCLUDE_DIR] - external_htslib_libraries = ['z', 'hts'] + if platform.system() == 'Windows': + external_htslib_libraries = ['hts', 'regex.dll', 'z.dll', 'curl.dll', 'deflate.dll', 'crypto', 'lzma', 'bz2'] + else: + external_htslib_libraries = ['z', 'hts'] elif HTSLIB_MODE == 'separate': # add to each pysam component a separately compiled # htslib @@ -517,38 +551,72 @@ def run(self): "/* conservative compilation options */\n") ####################################################### -# Windows compatibility - untested +include_os = [] +os_c_files = [] +# for python 3.4, see for example +# http://stackoverflow.com/questions/25587039/ +# error-compiling-rpy2-on-python3-4-due-to-werror- +# declaration-after-statement +extra_compile_args = [ + "-Wno-unused", + "-Wno-strict-prototypes", + "-Wno-sign-compare", + "-Wno-error=declaration-after-statement"] + +define_macros = [] + +if platform.system() == "Windows": + suffix = distutils.sysconfig.get_config_var('EXT_SUFFIX') or distutils.sysconfig.get_config_var('SO') +else: + suffix = sysconfig.get_config_var('EXT_SUFFIX') or sysconfig.get_config_var('SO') + if platform.system() == 'Windows': - include_os = ['win32'] - os_c_files = ['win32/getopt.c'] - extra_compile_args = [] + internal_htslib_libraries = [ + ":libchtslib{}".format(suffix)] + internal_samtools_libraries = [ + ":libcsamtools{}".format(suffix), + ":libcbcftools{}".format(suffix), + ] + internal_pysamutil_libraries = [ + ":libcutils{}".format(suffix)] else: - include_os = [] - os_c_files = [] - # for python 3.4, see for example - # http://stackoverflow.com/questions/25587039/ - # error-compiling-rpy2-on-python3-4-due-to-werror- - # declaration-after-statement - extra_compile_args = [ - "-Wno-unused", - "-Wno-strict-prototypes", - "-Wno-sign-compare", - "-Wno-error=declaration-after-statement"] + internal_htslib_libraries = [ + os.path.splitext("chtslib{}".format(suffix))[0]] + internal_samtools_libraries = [ + os.path.splitext("csamtools{}".format(suffix))[0], + os.path.splitext("cbcftools{}".format(suffix))[0], + ] + internal_pysamutil_libraries = [ + os.path.splitext("cutils{}".format(suffix))[0]] + external_htslib_objects = [] -define_macros = [] +if platform.system() == 'Windows': + msys2_root = find_msys2_root() + if msys2_root: + external_htslib_objects = [] + external_htslib_objects.append('htslib/libhts.a') + msystem = subprocess.check_output(['echo', '%MSYSTEM%'], shell=True, text=True).strip() + + if msystem in ['MINGW32', 'MINGW64', 'UCRT64', 'CLANG64', 'CLANG32', 'CLANGARM64']: + base_path = f"{msys2_root}/{msystem.lower()}/lib" + else: + # Default to MINGW64 or handle error/exception as needed + base_path = f"{msys2_root}/mingw64/lib" -suffix = sysconfig.get_config_var('EXT_SUFFIX') or sysconfig.get_config_var('SO') + for lib in external_htslib_libraries: + if lib != "ws2_32": + external_htslib_objects.append(f"{base_path}/lib{lib}.a") + + # Append ws2_32 library path separately as an exception + external_htslib_objects.append(f"{base_path}/libws2_32.a") -internal_htslib_libraries = [ - os.path.splitext("chtslib{}".format(suffix))[0]] -internal_samtools_libraries = [ - os.path.splitext("csamtools{}".format(suffix))[0], - os.path.splitext("cbcftools{}".format(suffix))[0], - ] -internal_pysamutil_libraries = [ - os.path.splitext("cutils{}".format(suffix))[0]] + else: + print("MSYS2 root not found.") -libraries_for_pysam_module = external_htslib_libraries + internal_htslib_libraries + internal_pysamutil_libraries +if platform.system() == "Windows": + libraries_for_pysam_module = internal_htslib_libraries + internal_pysamutil_libraries +else: + libraries_for_pysam_module = external_htslib_libraries + internal_htslib_libraries + internal_pysamutil_libraries # Order of modules matters in order to make sure that dependencies are resolved. # The structures of dependencies is as follows: @@ -580,64 +648,125 @@ def prebuild_libchtslib(ext, force): def prebuild_libcsamtools(ext, force): write_configvars_header("samtools/samtools_config_vars.h", ext, "SAMTOOLS") - -modules = [ - dict(name="pysam.libchtslib", - prebuild_func=prebuild_libchtslib, - sources=[source_pattern % "htslib", "pysam/htslib_util.c"] + os_c_files, - extra_objects=htslib_objects, - libraries=external_htslib_libraries), - dict(name="pysam.libcsamtools", - prebuild_func=prebuild_libcsamtools, - sources=[source_pattern % "samtools"] + glob.glob(os.path.join("samtools", "*.pysam.c")) + - [os.path.join("samtools", "lz4", "lz4.c")] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=external_htslib_libraries + internal_htslib_libraries), - dict(name="pysam.libcbcftools", - sources=[source_pattern % "bcftools"] + glob.glob(os.path.join("bcftools", "*.pysam.c")) + os_c_files, - extra_objects=separate_htslib_objects, - libraries=external_htslib_libraries + internal_htslib_libraries), - dict(name="pysam.libcutils", - sources=[source_pattern % "utils", "pysam/pysam_util.c"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=external_htslib_libraries + internal_htslib_libraries + internal_samtools_libraries), - dict(name="pysam.libcalignmentfile", - sources=[source_pattern % "alignmentfile"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libcsamfile", - sources=[source_pattern % "samfile"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libcalignedsegment", - sources=[source_pattern % "alignedsegment"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libctabix", - sources=[source_pattern % "tabix"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libcfaidx", - sources=[source_pattern % "faidx"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libcbcf", - sources=[source_pattern % "bcf"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libcbgzf", - sources=[source_pattern % "bgzf"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libctabixproxies", - sources=[source_pattern % "tabixproxies"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), - dict(name="pysam.libcvcf", - sources=[source_pattern % "vcf"] + os_c_files, - extra_objects=separate_htslib_objects, - libraries=libraries_for_pysam_module), -] +if platform.system() == 'Windows': + modules = [ + dict(name="pysam.libchtslib", + prebuild_func=prebuild_libchtslib, + sources=[source_pattern % "htslib", "pysam/htslib_util.c"] + os_c_files, + extra_objects=htslib_objects, + extra_link_args=["-Wl,--export-all-symbols"]), + dict(name="pysam.libcsamtools", + prebuild_func=prebuild_libcsamtools, + sources=[source_pattern % "samtools"] + glob.glob(os.path.join("samtools", "*.pysam.c")) + + [os.path.join("samtools", "lz4", "lz4.c")] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=internal_htslib_libraries, + extra_link_args=["-Wl,--export-all-symbols"]), + dict(name="pysam.libcbcftools", + sources=[source_pattern % "bcftools"] + glob.glob(os.path.join("bcftools", "*.pysam.c")) + os_c_files, + extra_objects=separate_htslib_objects, + libraries=internal_htslib_libraries, + extra_link_args=["-Wl,--export-all-symbols"]), + dict(name="pysam.libcutils", + sources=[source_pattern % "utils", "pysam/pysam_util.c"] + os_c_files, + extra_objects=separate_htslib_objects, + #libraries=external_htslib_libraries + internal_htslib_libraries + internal_samtools_libraries), + libraries=internal_htslib_libraries + internal_samtools_libraries), + dict(name="pysam.libcalignmentfile", + sources=[source_pattern % "alignmentfile"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcsamfile", + sources=[source_pattern % "samfile"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcalignedsegment", + sources=[source_pattern % "alignedsegment"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libctabix", + sources=[source_pattern % "tabix"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcfaidx", + sources=[source_pattern % "faidx"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcbcf", + sources=[source_pattern % "bcf"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcbgzf", + sources=[source_pattern % "bgzf"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libctabixproxies", + sources=[source_pattern % "tabixproxies"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcvcf", + sources=[source_pattern % "vcf"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + ] +else: + modules = [ + dict(name="pysam.libchtslib", + prebuild_func=prebuild_libchtslib, + sources=[source_pattern % "htslib", "pysam/htslib_util.c"] + os_c_files, + extra_objects=htslib_objects, + libraries=external_htslib_libraries), + dict(name="pysam.libcsamtools", + prebuild_func=prebuild_libcsamtools, + sources=[source_pattern % "samtools"] + glob.glob(os.path.join("samtools", "*.pysam.c")) + + [os.path.join("samtools", "lz4", "lz4.c")] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=external_htslib_libraries + internal_htslib_libraries), + dict(name="pysam.libcbcftools", + sources=[source_pattern % "bcftools"] + glob.glob(os.path.join("bcftools", "*.pysam.c")) + os_c_files, + extra_objects=separate_htslib_objects, + libraries=external_htslib_libraries + internal_htslib_libraries), + dict(name="pysam.libcutils", + sources=[source_pattern % "utils", "pysam/pysam_util.c"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=external_htslib_libraries + internal_htslib_libraries + internal_samtools_libraries), + dict(name="pysam.libcalignmentfile", + sources=[source_pattern % "alignmentfile"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcsamfile", + sources=[source_pattern % "samfile"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcalignedsegment", + sources=[source_pattern % "alignedsegment"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libctabix", + sources=[source_pattern % "tabix"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcfaidx", + sources=[source_pattern % "faidx"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcbcf", + sources=[source_pattern % "bcf"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcbgzf", + sources=[source_pattern % "bgzf"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libctabixproxies", + sources=[source_pattern % "tabixproxies"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + dict(name="pysam.libcvcf", + sources=[source_pattern % "vcf"] + os_c_files, + extra_objects=separate_htslib_objects, + libraries=libraries_for_pysam_module), + ] common_options = dict( language="c", @@ -648,6 +777,9 @@ def prebuild_libcsamtools(ext, force): include_dirs=[os.path.abspath(x) for x in ["pysam"] + htslib_include_dirs + \ ["samtools", "samtools/lz4", "bcftools", "."] + include_os]) +if platform.system() == "Windows": + common_options["extra_objects"] = external_htslib_objects + # add common options (in python >3.5, could use n = {**a, **b} for module in modules: module.update(**common_options)