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
10 changes: 9 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ contains(matrix.os, 'windows') && 'msys2 {0}' || 'bash -e {0}' }}
steps:
- uses: actions/checkout@v7
- uses: msys2/setup-msys2@v2
if: runner.os == 'Windows'
with:
msystem: CLANG64
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-dlfcn mingw-w64-clang-x86_64-uv # clang for scons, dlopen for the filters
- run: ./test.sh
12 changes: 11 additions & 1 deletion SConstruct
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import os
import platform
import subprocess
import sys
import sysconfig
import numpy as np
import eigen

WINDOWS = platform.system() == "Windows"
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()

common = ''
Expand Down Expand Up @@ -38,8 +40,12 @@ env = Environment(
CXXFLAGS="-std=c++1z",
CPPPATH=cpppath,
REDNOSE_ROOT=Dir("#").abspath,
tools=["default", "cython", "rednose_filter"],
tools=["mingw" if WINDOWS else "default", "cython", "rednose_filter"], # the default tool picks MSVC on Windows
)
if WINDOWS:
env["CC"], env["CXX"] = "clang", "clang++" # the mingw tool assumes gcc
env["SHLIBPREFIX"] = "lib" # the mingw tool drops the prefix ekf_load expects
env.Append(LINKFLAGS=["-static"]) # libc++ into the DLLs so they load outside the MSYS2 shell

# Cython build enviroment
envCython = env.Clone()
Expand All @@ -48,6 +54,10 @@ envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-depr
envCython["LIBS"] = []
if platform.system() == "Darwin":
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"]
elif WINDOWS:
envCython["LINKFLAGS"] = ["-shared", "-static"]
envCython["LIBPATH"] += [os.path.join(sys.base_prefix, "libs")]
envCython["LIBS"] = [f"python{sys.version_info.major}{sys.version_info.minor}"]
elif arch == "aarch64":
envCython["LINKFLAGS"] = ["-shared"]
envCython["LIBS"] = [os.path.basename(python_path)]
Expand Down
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,13 @@ flake8-implicit-str-concat.allow-multiline = false
[tool.ty.rules]
unresolved-import = "ignore" # Cython-compiled modules (.pyx) and dynamic SCons exports
unresolved-attribute = "ignore" # Cython-backed filter attributes

# --- TODO REMOVE AFTER DEPENDENCY PR MERGES (commaai/dependencies#107): fork-only index of the comma-deps Windows wheels until PyPI has them; not for upstream ---
[[tool.uv.index]]
name = "comma-deps-windows"
url = "https://github.com/AmyJeanes/commaai-dependencies/releases/download/windows-post116/index.html"
format = "flat"
explicit = true

[tool.uv.sources]
comma-deps-eigen = [{ index = "comma-deps-windows", marker = "sys_platform == 'win32'" }]
2 changes: 1 addition & 1 deletion rednose/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def write_code(folder, name, code, header):


def load_code(folder, name):
shared_ext = "dylib" if platform.system() == "Darwin" else "so"
shared_ext = "dylib" if platform.system() == "Darwin" else "dll" if platform.system() == "Windows" else "so"
shared_fn = os.path.join(folder, f"lib{name}.{shared_ext}")
header_fn = os.path.join(folder, f"{name}.h")

Expand Down
2 changes: 2 additions & 0 deletions rednose/helpers/ekf_load.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ void ekf_load_and_register(const std::string& ekf_directory, const std::string&

#ifdef __APPLE__
std::string dylib_ext = ".dylib";
#elif defined(_WIN32)
std::string dylib_ext = ".dll";
#else
std::string dylib_ext = ".so";
#endif
Expand Down
6 changes: 6 additions & 0 deletions site_scons/site_tools/cython.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,11 @@ def generate(env):

create_builder(env)

# Python imports extension modules as .pyd on Windows; the SConscripts name them .so
if env["PLATFORM"] == "win32":
def pyd_emitter(target, source, env):
return [env.File(str(t)[:-3] + ".pyd") if str(t).endswith(".so") else t for t in target], source
env.Append(PROGEMITTER=[pyd_emitter])

def exists(env):
return True
4 changes: 3 additions & 1 deletion site_scons/site_tools/rednose_filter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import platform
import sys

import eigen
from SCons.Script import Dir, File
Expand All @@ -9,8 +10,9 @@ def compile_single_filter(env, target, filter_gen_script, output_dir, extra_gen_
extra_generated_files = [File(f'{output_dir}/{x}') for x in extra_gen_artifacts]
generator_file = File(filter_gen_script)

# the interpreter running SCons has the deps; cmd.exe cannot start a script by its shebang
env.Command(generated_src_files + extra_generated_files,
[generator_file] + script_deps, f"{File(generator_file).relpath} {target} {Dir(output_dir).relpath}")
[generator_file] + script_deps, f'"{sys.executable}" {File(generator_file).relpath} {target} {Dir(output_dir).relpath}')

generated_cc_file = File(generated_src_files[:1])

Expand Down