Skip to content
Merged
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
41 changes: 41 additions & 0 deletions .github/workflows/build_wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Build

on: [push, pull_request]

jobs:
build_wheels:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we can add an if: clause, to check if the repository is jamesaboyle/SANA-FE (my local mirror)

name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
# macos-13 is an intel runner, macos-14 is apple silicon
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-latest]

steps:
- uses: actions/checkout@v4

- name: Install flex and bison (platform-specific)
shell: bash
run: |
if [[ "$RUNNER_OS" == "macOS" ]]; then
brew install bison flex
echo 'PATH="/opt/homebrew/opt/bison/bin:/opt/homebrew/opt/flex/bin:$PATH"' >> $GITHUB_ENV
elif [[ "$RUNNER_OS" == "Windows" ]]; then
choco install winflexbison3
echo 'C:\ProgramData\chocolatey\bin' >> $GITHUB_PATH
fi

- name: Build wheels
uses: pypa/cibuildwheel@v3.0.0
# env:
# CIBW_SOME_OPTION: value
# ...
with:
# package-dir: .
# output-dir: wheelhouse
config-file: "{package}/pyproject.toml"

- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ build/
CMakeFiles/
Makefile
compile_commands.json
test_venv/
*.so
41 changes: 39 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++")
endif()
Expand All @@ -27,11 +30,16 @@ if (NOT DEFINED PYTHON_BUILD_ENABLED)
set(PYTHON_BUILD_ENABLED ON)
endif()


if (NOT DEFINED ENABLE_TESTING)
set(ENABLE_TESTING OFF)
endif()

# Ensure that at least one build target is enabled
if(NOT STANDALONE_BUILD_ENABLED AND NOT PYTHON_BUILD_ENABLED)
if(NOT STANDALONE_BUILD_ENABLED AND NOT PYTHON_BUILD_ENABLED AND NOT ENABLE_TESTING)
message(
FATAL_ERROR
"No build target enabled: Either STANDALONE_BUILD_ENABLED or PYTHON_BUILD_ENABLED must be ON."
"No build target enabled: Either STANDALONE_BUILD_ENABLED or PYTHON_BUILD_ENABLED or ENABLE_TESTING must be ON."
)
endif()

Expand Down Expand Up @@ -183,6 +191,8 @@ FetchContent_MakeAvailable(ryml)
set(RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS ON)
##### END OF rapid-yaml

message(STATUS "got ryml")

############## booksim2
project(booksim2-quickstart LANGUAGES CXX)
set(BOOKSIM_VERSION "110ad1b80e493241f6e57587bc11354ac84f91f8")
Expand Down Expand Up @@ -246,6 +256,9 @@ if(PYTHON_BUILD_ENABLED)
${SOURCE_FILES}
)

set_target_properties(sanafecpp PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/sanafe
)
target_link_libraries(sanafecpp PRIVATE ${PYTHON_LIBRARIES})
target_link_libraries(sanafecpp PRIVATE pybind11::pybind11)
target_link_libraries(sanafecpp PUBLIC ryml::ryml)
Expand All @@ -263,6 +276,8 @@ if(PYTHON_BUILD_ENABLED)
target_compile_definitions(sanafecpp PRIVATE ENABLE_SOURCE_INFO)
endif()
endif()

install(TARGETS sanafecpp DESTINATION sanafe)
endif()

if(STANDALONE_BUILD_ENABLED)
Expand All @@ -286,3 +301,25 @@ if(STANDALONE_BUILD_ENABLED)
endif()
endif()
endif()

if(ENABLE_TESTING)
list(FILTER SOURCE_FILES EXCLUDE REGEX "pymodule.cpp")
add_library(sanafe SHARED ${SOURCE_FILES} ${HEADER_FILES})
target_link_libraries(sanafe PUBLIC ryml::ryml)
target_link_libraries(sanafe PUBLIC booksim)
target_link_libraries(sanafe PRIVATE ${CMAKE_DL_LIBS})
if (OpenMP_CXX_FOUND AND ENABLE_OPENMP)
target_link_libraries(sanafe PRIVATE OpenMP::OpenMP_CXX)
target_compile_definitions(sanafe PRIVATE HAVE_OPENMP)
endif()
target_link_libraries(sanafe PRIVATE Threads::Threads)
target_include_directories(sanafe PUBLIC
${CMAKE_SOURCE_DIR}/src
)
message(STATUS "Testing enabled")
include(CTest)
add_subdirectory(tests)

find_program(CTEST_MEMORYCHECK_COMMAND valgrind)
set(CTEST_MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=10")
endif()
102 changes: 102 additions & 0 deletions ci/check_build.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env ruby

require 'fileutils'

log_dir = ENV["SANAFE_CI_LOG_DIR"] || "logs/commit-latest"
log_file = "#{log_dir}/build.log"
FileUtils.mkdir_p(log_dir)


#build standalone sim
def build_cpp(label:, build_dir:, compiler: nil, log_file:)
puts "[#{label}] Building standalone simulator..."

cmake_cmd = "cmake -S . -B #{build_dir}" #construct cmake config command using source and build dirs
cmake_cmd += " -DCMAKE_CXX_COMPILER=#{compiler}" if compiler #add compiler option if provided
cmake_ok = system("#{cmake_cmd} > #{log_file} 2>&1") #run cmake, direct output to log file

if cmake_ok
make_ok = system("cmake --build #{build_dir} --parallel 8 >> #{log_file} 2>&1") #if cmake works, build and append output to log
end

if cmake_ok && make_ok
puts "[#{label}] Simulator build: PASS"
true
else
puts "[#{label}] Simulator build: FAIL (see #{log_file})"
false
end
end

#build and install python .so
def build_python(label:, build_dir:, log_file:)
puts "[#{label}] Building python extension..."

FileUtils.rm_f("CMakeCache.txt")
FileUtils.mkdir_p(build_dir)
FileUtils.mkdir_p(File.dirname(log_file))

full_log_path = File.expand_path(log_file)

#create venv name
unique_id = ENV["SANAFE_CI_ID"]
venv_path = File.expand_path("#{File.dirname(log_file)}/venv")
venv_python = File.join(venv_path, "bin", "python")

FileUtils.mkdir_p("venvs")
unless system("python3 -m venv #{venv_path}")
puts "[#{label}] Failed to create virtualenv at #{venv_path}"
return false
end

install_ok = false
import_ok = false

File.open(full_log_path, "w") do |log|
Dir.chdir(build_dir) do
IO.popen("#{venv_python} -m pip install .. > /dev/null 2>&1") do |io|
io.each { |line| log.puts line }
end
install_ok = $?.success?
end
end

puts "[#{label}] Python build: #{install_ok ? 'PASS' : "FAIL (see #{log_file})"}"
install_ok
end

#set clang and gcc path
clang_path = ENV["CLANG_PATH"]
gcc_path = ENV["GCC_PATH"]

#gcc builds
gcc_sim_ok = build_cpp(
label: "GCC",
build_dir: "build_gcc",
compiler: gcc_path,
log_file: "#{log_dir}/build_gcc_sim.log"
)
gcc_py_ok = build_python(
label: "GCC",
build_dir: "build_gcc_py",
log_file: "#{log_dir}/build_gcc_py.log"
)

#clang builds
clang_sim_ok = build_cpp(
label: "Clang",
build_dir: "build_clang",
compiler: clang_path,
log_file: "#{log_dir}/build_clang_sim.log"
)
clang_py_ok = build_python(
label: "Clang",
build_dir: "build_clang_py",
log_file: "#{log_dir}/build_clang_py.log"
)

#final summary
all_ok = gcc_sim_ok && gcc_py_ok && clang_sim_ok && clang_py_ok
puts all_ok ? "All builds succeeded." : "One or more builds failed."
exit(all_ok ? 0 : 1)

Empty file added ci/check_coverity.rb
Empty file.
23 changes: 23 additions & 0 deletions ci/check_cppcheck.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env ruby

require 'fileutils'

log_dir = ENV["SANAFE_CI_LOG_DIR"] || "logs/commit-latest"
log_file = "#{log_dir}/cppcheck.log"
FileUtils.mkdir_p(log_dir)

cpp_files = Dir.glob("src/*.cpp") + Dir.glob("plugins/*.cpp")
failed = false

File.open(log_file, "w") do |log|
log.puts "Running CPPCheck..."
cpp_files.each do |file|
log.puts "----- #{file} -----"
result = `cppcheck --enable=all --inconclusive --quiet --std=c++20 --language=c++ --suppress=missingIncludeSystem #{file} 2>&1`
log.puts result
failed ||= !result.strip.empty?
end
end

puts failed ? "CPPCheck: FAIL (see #{log_file})" : "CPPCheck: PASS"
exit(failed ? 1 : 0)
78 changes: 78 additions & 0 deletions ci/check_dynamic.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env ruby

require 'fileutils'

log_dir = ENV["SANAFE_CI_LOG_DIR"] || "logs/commit-latest"
build_log_file = "#{log_dir}/dynamic_build.log"
test_log_file = "#{log_dir}/dynamic_test.log"
FileUtils.mkdir_p(log_dir)

# timeout after 30 seconds
timeout = 30

puts "Running dynamic tests..."

puts "Building the project with testing enabled..."

cmake = system("cmake -DENABLE_TESTING=ON -DPYTHON_BUILD_ENABLED=OFF -DSTANDALONE_BUILD_ENABLED=OFF -DCMAKE_BUILD_TYPE=Debug -S . -B build > #{build_log_file} 2>&1")

if !cmake
puts "CMake configuration failed. See #{build_log_file} for details."
puts "Dynamic Tests: FAIL"
exit 3
end

build = system("cmake --build build -j 10 >> #{build_log_file} 2>&1")

if !build
puts "Build failed. See #{build_log_file} for details."
puts "Dynamic Tests: FAIL"
exit 3
end

puts "Build successful. Running tests..."

test = system("ctest --memcheck --test-dir build --output-on-failure --timeout #{timeout} > #{test_log_file} 2>&1")
exitcode = $?.exitstatus

puts "Tests completed, now checking for memory leaks..."

build_dir = "build"
mem_log_dir = "#{build_dir}/Testing/Temporary"
pattern = "MemoryChecker.*\\.log$"
summary_log = File.join(mem_log_dir, "LastMemCheck.log")

unless Dir.exist?(mem_log_dir)
puts "Memory log directory #{mem_log_dir} does not exist. Something went wrong."
exit 2
end

log_files = Dir.entries(mem_log_dir).select { |f| f.match?(pattern) }
puts "Found #{log_files.size} Valgrind log(s)."

leaks_found = false

log_files.each do |filename|
path = File.join(mem_log_dir, filename)
contents = File.read(path)

if contents.include?("definitely lost")
puts "Leak detected in #{filename}"
leaks_found = true
end
end

if leaks_found
puts "Memory leaks detected in one or more Valgrind logs. See #{mem_log_dir} for details."
puts "Dynamic Tests: FAIL"
exit 2
end
if exitcode != 0
puts "One or more tests failed. See #{test_log_file} for details."
puts "Dynamic Tests: FAIL"
exit 1
else
puts "All tests passed successfully. Hooray! Check build/Testing/Temporary for memory leak reports."
puts "Dynamic Tests: PASS"
exit 0
end
39 changes: 39 additions & 0 deletions ci/check_format.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env ruby
#TODO: break format
require 'fileutils'

log_dir = ENV["SANAFE_CI_LOG_DIR"] || "logs/commit-latest"
log_file = "#{log_dir}/format.log"
FileUtils.mkdir_p(log_dir)

#find cpp files
cpp_files = Dir.glob("src/*.cpp") + Dir.glob("plugins/*.cpp")

failed_files = []

File.open(log_file, "w") do |log|
cpp_files.each do |file|
result = `clang-format --dry-run --Werror #{file} 2>&1`
if $?.exitstatus != 0
failed_files << file
log.puts "[FAIL] #{file}"
log.puts result
else
log.puts "[PASS] #{file}"
end
end

if failed_files.empty?
log.puts "\nAll files passed clang-format check."
else
log.puts "\n#{failed_files.size} file(s) failed formatting."
end
end

if failed_files.empty?
puts "Format Check: PASS"
else
puts "Format Check: FAIL (#{failed_files.size} file(s) failed, see #{log_file})"
end

exit(failed_files.empty? ? 0 : 1)
Loading
Loading