Skip to content

Commit

Permalink
Add GitHub Actions (#122)
Browse files Browse the repository at this point in the history
* GitHub Action: AVR build & unit test

* Only run benchmark test on Release builds.

* Switch to GitHub actions

* Add manual run

* Use workflow_call instead of workflow_run
workflow_run will "only trigger a workflow run if the workflow file is on the default branch."

* Use the ClangCL toolset on Windows+CLang

* Lookup the C compiler & force build tests on

* Simplify matrix, force tests on & clang fuzzing

* Turn off clang fuzzers -  compilation errors

* Split Windows builds into separate jobs
There is quite a big difference in the compiler toolchains.

* Use marketplace cmake action

* Windows: need to use MSVC command prompt
So compilers are in the path.

* Use MSVC compile options for clang-cl

* Windows: specify generators

* clang-cl: disable __umulh/__mulh intrinsics prior to VS2022

* Sanitize: use target_compile_options & target_link_options

* Build on multiple ubuntu versions

* Speed up canary build

* Launch AVR build only on canary build success

* Reinstate appveyor support

* Update readme Build Status badge
  • Loading branch information
adbancroft authored Oct 17, 2024
1 parent e4d0295 commit e72d827
Show file tree
Hide file tree
Showing 14 changed files with 425 additions and 81 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/avr_build_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: AVR Build & Unit Tests

on:
# This job takes a long time to run, so only run it when the canary build passes
# Ideally we'd use the workflow_run trigger and have GitHub launch this action, but the docs state:
# This event will only trigger a workflow run if the workflow file is on the default branch.
# ...which leads to some interesting behaviors
workflow_call:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
AVRSimulator-Unit-Test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install PlatformIO
working-directory: ./test/avr
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
pip install wheel
platformio upgrade
platformio pkg update
- name: Build All Environments
shell: pwsh
working-directory: ./test/avr
run: Get-Content -Path .\platformio.ini | Where-Object { $_.StartsWith("[env:") } | ForEach-Object { & pio run -e $_.SubString(5, $_.Length-6) }

- name: Run Simulator Unit Tests
working-directory: ./test/avr
run: platformio test -v -e megaatmega2560_sim_unittest
48 changes: 48 additions & 0 deletions .github/workflows/canary_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Canary Build and Test

on:
push:
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

env:
build_type: Release
cpp_compiler: g++
output_folder: ${{github.workspace}}/build/

jobs:
Canary-Build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Configure CMake & Build
env:
# Map from C++ compiler to equivalent C compiler
g++: gcc
clang++: clang
uses: threeal/cmake-action@v2.0.0
with:
build-dir: ${{ env.output_folder }}
c-compiler: ${{ env[env.cpp_compiler] }}
cxx-compiler: ${{ env.cpp_compiler }}
build-args: -t tester
options: |
LIBDIVIDE_BUILD_TESTS=ON
CMAKE_BUILD_TYPE=${{ env.build_type }}
- name: Test
working-directory: ${{env.output_folder}}
run: ctest --build-config ${{env.build_type}} --verbose --tests-regex tester

# Kick off the full build if everything above succeeded
Full-Build:
needs: Canary-Build
uses: ./.github/workflows/full_build.yml

# Kick off the full build if everything above succeeded
AVR-Build:
needs: Canary-Build
uses: ./.github/workflows/avr_build_test.yml
121 changes: 121 additions & 0 deletions .github/workflows/full_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Full Build and Test

on:
# This job takes a long time to run, so only run it when the canary build passes
# Ideally we'd use the workflow_run trigger and have GitHub launch this action, but the docs state:
# This event will only trigger a workflow run if the workflow file is on the default branch.
# ...which leads to some interesting behaviors
workflow_call:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
Full-Build-Ubuntu:
runs-on: ${{ matrix.os }}

strategy:
# We want to test all matrix combinations regardless of failure
fail-fast: false

matrix:
os:
- ubuntu-24.04
- ubuntu-20.04
build_type:
- Release
- Sanitize
cpp_compiler:
- g++
- clang++

env:
output_folder: ${{github.workspace}}/build/

steps:
- uses: actions/checkout@v4

- name: Configure CMake & Build
env:
# Map from C++ compiler to equivalent C compiler
g++: gcc
clang++: clang
uses: threeal/cmake-action@v2.0.0
with:
build-dir: ${{ env.output_folder }}
c-compiler: ${{ env[matrix.cpp_compiler] }}
cxx-compiler: ${{ matrix.cpp_compiler }}
options: |
LIBDIVIDE_BUILD_TESTS=ON
CMAKE_BUILD_TYPE=${{ matrix.build_type }}
build-args: --config ${{ matrix.build_type }}

- name: Test
working-directory: ${{ env.output_folder }}
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest --build-config ${{ matrix.build_type }} --verbose

Full-Build-Windows:
runs-on: ${{ matrix.os }}

strategy:
# We want to test all matrix combinations regardless of failure
fail-fast: false
# This makes development easier
# max-parallel: 0
matrix:
os:
- windows-2022
- windows-2019
build_type:
- Release
- Sanitize
cpp_compiler:
- clang-cl.exe
- cl.exe
include:
# Set generator per os
# Note: These *expand* the existing matrix configurations, it doesn't create new matrix configurations
# See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
- os: windows-2022
generator: Visual Studio 17 2022
- os: windows-2019
generator: Visual Studio 16 2019
# clang-cl requires the toolset flag be set correctly
- cpp_compiler: clang-cl.exe
toolset: ClangCL
exclude:
# MSVC Clang toolchain fails sanitization
- cpp_compiler: clang-cl.exe
build_type: Sanitize

env:
output_folder: ${{github.workspace}}/build/

steps:
- uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1

- name: Configure CMake & Build
env:
# Map from C++ compiler to equivalent C compiler
cl.exe: cl.exe
clang-cl.exe: clang-cl.exe
toolset: ${{ matrix.toolset && format('-T {0}', matrix.toolset) || ''}}
uses: threeal/cmake-action@v2.0.0
with:
build-dir: ${{ env.output_folder }}
c-compiler: ${{ env[matrix.cpp_compiler] }}
cxx-compiler: ${{ matrix.cpp_compiler }}
generator: ${{ matrix.generator }}
args: ${{ env.toolset }}
options: |
LIBDIVIDE_BUILD_TESTS=ON
CMAKE_BUILD_TYPE=${{ matrix.build_type }}
build-args: --config ${{ matrix.build_type }}

- name: Test
working-directory: ${{ env.output_folder }}
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest --build-config ${{ matrix.build_type }} --verbose
39 changes: 28 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ include(CheckCXXSourceRuns)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
include(CMakePushCheckState)
include(CMakeSanitize)

# Compile options ################################################

# Maximum warnings level & warnings as error
add_compile_options(
"$<$<CXX_COMPILER_ID:MSVC>:/W4;/WX>"
"$<$<CXX_COMPILER_ID:GNU>:-Wall;-Wextra;-pedantic;-Werror>"
"$<$<CXX_COMPILER_ID:Clang>:-Wall;-Wextra;-pedantic;-Werror>"
"$<$<CXX_COMPILER_ID:AppleClang>:-Wall;-Wextra;-pedantic;-Werror>"
)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # clang-cl
add_compile_options("/W4;/WX;")
else() # clang native
add_compile_options("-Wall;-Wextra;-pedantic;-Werror")
endif()
else()
add_compile_options(
"$<$<CXX_COMPILER_ID:MSVC>:/W4;/WX>"
"$<$<CXX_COMPILER_ID:GNU>:-Wall;-Wextra;-pedantic;-Werror>"
"$<$<CXX_COMPILER_ID:AppleClang>:-Wall;-Wextra;-pedantic;-Werror>"
)
endif()

# Build options ################################################

Expand Down Expand Up @@ -232,6 +238,8 @@ endif()
add_library(libdivide INTERFACE)
add_library(libdivide::libdivide ALIAS libdivide)

include(CMakeSanitize)

target_compile_features(libdivide INTERFACE cxx_alias_templates)

target_include_directories(libdivide INTERFACE
Expand Down Expand Up @@ -303,13 +311,22 @@ endif()
if (LIBDIVIDE_BUILD_TESTS)
include(CTest)
enable_testing()
add_test(tester tester)
add_test(benchmark_branchfree benchmark_branchfree)

add_test(tester tester)
# cmake won't actually build the tests before it tries to run them
add_test(build_tester "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target tester)
add_test(build_benchmark_branchfree "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target benchmark_branchfree)
set_tests_properties(tester benchmark_branchfree PROPERTIES DEPENDS "build_tester;build_benchmark_branchfree")
set_tests_properties(tester PROPERTIES DEPENDS "build_tester")

add_test(test_c99 test_c99)
add_test(build_test_c99 "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target test_c99)
set_tests_properties(test_c99 PROPERTIES DEPENDS "build_test_c99")

# Only benchmark in release builds.
if("${BUILD_TYPE}" MATCHES RELEASE)
add_test(benchmark_branchfree benchmark_branchfree)
add_test(build_benchmark_branchfree "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target benchmark_branchfree)
set_tests_properties(benchmark_branchfree PROPERTIES DEPENDS "build_benchmark_branchfree")
endif()
endif()

# Build the fuzzers (requires clang) ###########################
Expand Down
27 changes: 16 additions & 11 deletions CMakeSanitize
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,6 @@ if(CMAKE_CONFIGURATION_TYPES)
FORCE)
endif()

if (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
set(COMPILER_FLAGS "/fsanitize=address /MTd")
set(LINKER_FLAGS "/INCREMENTAL:NO")
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(COMPILER_FLAGS "-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -U_DLL -U_MT -DDEBUG -O1 -g")
set(LINKER_FLAGS "-fsanitize=address")
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(COMPILER_FLAGS "-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -DDEBUG -O1 -g")
set(LINKER_FLAGS "-fsanitize=address -fsanitize=undefined")
endif()

set("CMAKE_CXX_FLAGS_${BUILD_NAME_U}" "${CMAKE_CXX_FLAGS_${BASE_BUILD_NAME_U}} ${COMPILER_FLAGS}" CACHE STRING
"Flags used by the C++ compiler during ${BUILD_NAME} builds."
FORCE)
Expand All @@ -46,3 +35,19 @@ mark_as_advanced(
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ${BUILD_NAME}."
FORCE)

if (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
target_compile_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:/fsanitize=address;/MTd>")
target_link_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:/INCREMENTAL:NO>")
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # clang-cl
target_compile_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:/fsanitize=address>")
target_link_libraries(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:clang_rt.asan_dynamic-x86_64.lib;clang_rt.asan_dynamic_runtime_thunk-x86_64.lib>")
else() # clang native
target_compile_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:-fsanitize=address;-fsanitize=undefined;-fno-sanitize-recover=all;-fno-omit-frame-pointer;-U_DLL;-U_MT;-DDEBUG;-O1;-g>")
target_link_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:-fsanitize=address>")
endif()
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:-fsanitize=address;-fsanitize=undefined;-fno-sanitize-recover=all;-fno-omit-frame-pointer;-DDEBUG;-O1;-g>")
target_link_options(libdivide INTERFACE "$<$<CONFIG:${BUILD_NAME}>:-fsanitize=address;-fsanitize=undefined>")
endif()
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# libdivide
[![Build Status](https://ci.appveyor.com/api/projects/status/github/ridiculousfish/libdivide?branch=master&svg=true)](https://ci.appveyor.com/project/kimwalisch/libdivide)
[![Build Status](https://github.com/ridiculousfish/libdivide/actions/workflows/canary_build.yml/badge.svg)](https://github.com/ridiculousfish/libdivide/actions/workflows/canary_build.yml)
[![Github Releases](https://img.shields.io/github/release/ridiculousfish/libdivide.svg)](https://github.com/ridiculousfish/libdivide/releases)

```libdivide.h``` is a header-only C/C++ library for optimizing integer division.
Expand Down
11 changes: 9 additions & 2 deletions libdivide.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,15 @@
#include <arm_neon.h>
#endif

// Clang-cl prior to Visual Studio 2022 doesn't include __umulh/__mulh intrinsics
#if defined(_MSC_VER) && defined(LIBDIVIDE_X86_64) && (!defined(__clang__) || _MSC_VER>1930)
#define LIBDIVIDE_X64_INTRINSICS
#endif

#if defined(_MSC_VER)
#if defined(LIBDIVIDE_X64_INTRINSICS)
#include <intrin.h>
#endif
#pragma warning(push)
// disable warning C4146: unary minus operator applied
// to unsigned type, result still unsigned
Expand Down Expand Up @@ -325,7 +332,7 @@ static LIBDIVIDE_INLINE int32_t libdivide_mullhi_s32(int32_t x, int32_t y) {
}

static LIBDIVIDE_INLINE uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) {
#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
#if defined(LIBDIVIDE_X64_INTRINSICS)
return __umulh(x, y);
#elif defined(HAS_INT128_T)
__uint128_t xl = x, yl = y;
Expand All @@ -351,7 +358,7 @@ static LIBDIVIDE_INLINE uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) {
}

static LIBDIVIDE_INLINE int64_t libdivide_mullhi_s64(int64_t x, int64_t y) {
#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
#if defined(LIBDIVIDE_X64_INTRINSICS)
return __mulh(x, y);
#elif defined(HAS_INT128_T)
__int128_t xl = x, yl = y;
Expand Down
6 changes: 3 additions & 3 deletions test/DivideTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class DivideTest {
PRINT_ERROR(F(", but got "));
PRINT_ERROR(result);
PRINT_ERROR(F("\n"));
exit(1);
TEST_FAIL();
}
}

Expand Down Expand Up @@ -162,7 +162,7 @@ class DivideTest {
PRINT_ERROR(F(", but got "));
PRINT_ERROR(result);
PRINT_ERROR(F("\n"));
exit(1);
TEST_FAIL();
} else {
#if 0
std::cout << "vec" << (CHAR_BIT * sizeof(VecType)) << " success for: " << numer << " / " << denom << " = " << result << std::endl;
Expand Down Expand Up @@ -325,7 +325,7 @@ class DivideTest {
PRINT_ERROR(F(", but got "));
PRINT_ERROR(recovered);
PRINT_ERROR(F("\n"));
exit(1);
TEST_FAIL();
}

test_edgecase_numerators(denom, the_divider);
Expand Down
Loading

0 comments on commit e72d827

Please sign in to comment.