diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 00000000..5f735016 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,90 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: windows-latest + c_compiler: gcc + cpp_compiler: g++ + - os: windows-latest + c_compiler: clang + cpp_compiler: clang++ + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + - os: macos-latest + c_compiler: clang + cpp_compiler: clang++ + - os: macos-latest + c_compiler: gcc + cpp_compiler: g++ + exclude: + - os: ubuntu-latest + c_compiler: cl + - os: macos-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v3 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - uses: seanmiddleditch/gha-setup-ninja@master + + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -G Ninja + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # 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 }} diff --git a/.github/workflows/ros_workspace.yml b/.github/workflows/colcon-workspace.yml similarity index 68% rename from .github/workflows/ros_workspace.yml rename to .github/workflows/colcon-workspace.yml index b889a9c8..ca37f4b3 100644 --- a/.github/workflows/ros_workspace.yml +++ b/.github/workflows/colcon-workspace.yml @@ -1,19 +1,18 @@ -name: ROS workspace CI +name: colcon workspace on: [push, pull_request] jobs: # build on Ubuntu docker images build_linux: - name: Ubuntu (${{ matrix.ros_distribution }}, ${{ matrix.config }}) + name: "Ubuntu (${{ matrix.ros_distribution }}, shared: ${{ matrix.cmake_shared_libs }})" runs-on: ubuntu-latest strategy: matrix: ros_distribution: [noetic, humble] - config: ["default"] # nice name - cmake_args: ['[ ]'] # empty list of options + cmake_shared_libs: ['ON', 'OFF'] include: - docker_image: ubuntu:20.04 ros_distribution: noetic @@ -23,13 +22,6 @@ jobs: ros_distribution: humble ros_version: 2 - # build static library, this will set BUILD_PYTHON_WRAPPER to OFF - - docker_image: ubuntu:22.04 - ros_distribution: humble - ros_version: 2 - config: "static" - cmake_args: '[ "-DBUILD_SHARED_LIBS=OFF" ]' - container: image: ${{ matrix.docker_image }} @@ -40,8 +32,6 @@ jobs: apt install --no-install-recommends -y git ca-certificates - uses: actions/checkout@v4 - #with: - # submodules: recursive - name: Setup ROS environment uses: ros-tooling/setup-ros@v0.7 @@ -52,6 +42,7 @@ jobs: with: package-name: apriltag target-ros1-distro: ${{ matrix.ros_distribution }} + extra-cmake-args: "-DBUILD_SHARED_LIBS=${{ matrix.cmake_shared_libs }}" - name: ROS 2 CI Action if: ${{ matrix.ros_version == 2 }} @@ -59,23 +50,19 @@ jobs: with: package-name: apriltag target-ros2-distro: ${{ matrix.ros_distribution }} - colcon-defaults: | - { - "build": { - "cmake-args": ${{ matrix.cmake_args }} - } - } + extra-cmake-args: "-DBUILD_SHARED_LIBS=${{ matrix.cmake_shared_libs }}" # build on Windows native build_windows: - name: Windows (${{ matrix.ros_distribution }}) + name: "Windows (${{ matrix.ros_distribution }}, shared: ${{ matrix.cmake_shared_libs }})" runs-on: windows-2019 strategy: matrix: ros_distribution: [noetic, humble] + cmake_shared_libs: ['OFF'] include: - ros_distribution: noetic ros_version: 1 @@ -97,12 +84,7 @@ jobs: with: package-name: apriltag target-ros1-distro: ${{ matrix.ros_distribution }} - colcon-defaults: | - { - "build": { - "cmake-args": [ "-DBUILD_SHARED_LIBS=OFF" ] - } - } + extra-cmake-args: "-DBUILD_SHARED_LIBS=${{ matrix.cmake_shared_libs }}" - name: ROS 2 CI Action if: ${{ matrix.ros_version == 2 }} @@ -110,23 +92,18 @@ jobs: with: package-name: apriltag target-ros2-distro: ${{ matrix.ros_distribution }} - colcon-defaults: | - { - "build": { - "cmake-args": [ "-DBUILD_SHARED_LIBS=OFF" ] - } - } + extra-cmake-args: "-DBUILD_SHARED_LIBS=${{ matrix.cmake_shared_libs }}" # build on macOS native build_macos: - name: macOS (${{ matrix.ros_distribution }}) + name: "macOS (${{ matrix.ros_distribution }}, shared: ${{ matrix.cmake_shared_libs }})" runs-on: macos-latest strategy: - fail-fast: false matrix: ros_distribution: [humble] + cmake_shared_libs: ['OFF'] steps: - uses: actions/checkout@v4 @@ -145,9 +122,4 @@ jobs: with: package-name: apriltag target-ros2-distro: ${{ matrix.ros_distribution }} - colcon-defaults: | - { - "build": { - "cmake-args": [ "-DBUILD_PYTHON_WRAPPER=OFF" ] - } - } + extra-cmake-args: "-DBUILD_SHARED_LIBS=${{ matrix.cmake_shared_libs }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 555cb14f..a42d460e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,8 @@ endif() # Set a default build type if none was specified set(default_build_type "Release") +SET(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${default_build_type}' as none was specified.") set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) @@ -43,9 +45,19 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Werror) # add_compile_options(-Wpedantic) add_compile_options(-Wno-shift-negative-value) - if(NOT CMAKE_C_COMPILER_ID MATCHES "AppleClang") - add_link_options("-Wl,-z,relro,-z,now,-z,defs") - endif() +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_C_COMPILER_ID MATCHES "AppleClang" AND NOT CMAKE_C_SIMULATE_ID MATCHES "MSVC") + add_link_options("-Wl,-z,relro,-z,now,-z,defs") +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_SIMULATE_ID MATCHES "MSVC") + # error: 'strdup' is deprecated: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _strdup. + # "strdup" is standard since C23 + add_definitions(-D _CRT_NONSTDC_NO_DEPRECATE) + # ignore "'fopen' is deprecated" and "'strncpy' is deprecated" warnings + # TODO: replace by fopen_s and strncpy_s in C11 + add_definitions(-D _CRT_SECURE_NO_WARNINGS) endif() aux_source_directory(common COMMON_SRC) diff --git a/apriltag.c b/apriltag.c index 3aea694b..ec4f0224 100644 --- a/apriltag.c +++ b/apriltag.c @@ -32,6 +32,7 @@ either expressed or implied, of the Regents of The University of Michigan. #include "apriltag.h" +#define _USE_MATH_DEFINES #include #include #include @@ -53,10 +54,6 @@ either expressed or implied, of the Regents of The University of Michigan. #include "common/postscript_utils.h" -#ifndef M_PI -# define M_PI 3.141592653589793238462643383279502884196 -#endif - #ifdef _WIN32 static inline void srandom(unsigned int seed) { diff --git a/apriltag_quad_thresh.c b/apriltag_quad_thresh.c index 56aece84..589a8b44 100644 --- a/apriltag_quad_thresh.c +++ b/apriltag_quad_thresh.c @@ -28,6 +28,7 @@ either expressed or implied, of the Regents of The University of Michigan. // limitation: image size must be <32768 in width and height. This is // because we use a fixed-point 16 bit integer representation with one // fractional bit. +#define _USE_MATH_DEFINES #include #include #include @@ -62,10 +63,6 @@ struct uint64_zarray_entry struct uint64_zarray_entry *next; }; -#ifndef M_PI -# define M_PI 3.141592653589793238462643383279502884196 -#endif - struct pt { // Note: these represent 2*actual value. diff --git a/common/g2d.c b/common/g2d.c index 4645f206..91bbcabc 100644 --- a/common/g2d.c +++ b/common/g2d.c @@ -33,13 +33,6 @@ either expressed or implied, of the Regents of The University of Michigan. #include "g2d.h" #include "common/math_util.h" -#ifdef _WIN32 -static inline long int random(void) -{ - return rand(); -} -#endif - double g2d_distance(const double a[2], const double b[2]) { return sqrtf(sq(a[0]-b[0]) + sq(a[1]-b[1])); @@ -725,6 +718,13 @@ int g2d_polygon_rasterize(const zarray_t *poly, double y, double *x) */ #if 0 +#ifdef _WIN32 +static inline long int random(void) +{ + return rand(); +} +#endif + #include "timeprofile.h" int main(int argc, char *argv[]) diff --git a/common/getopt.c b/common/getopt.c index 413bc1eb..21ec6fb1 100644 --- a/common/getopt.c +++ b/common/getopt.c @@ -86,6 +86,11 @@ void getopt_option_destroy(getopt_option_t *goo) free(goo); } +void getopt_option_destroy_void(void *goo) +{ + getopt_option_destroy((getopt_option_t *)goo); +} + void getopt_destroy(getopt_t *gopt) { // free the extra arguments and container @@ -94,7 +99,7 @@ void getopt_destroy(getopt_t *gopt) // deep free of the getopt_option structs. Also frees key/values, so // after this loop, hash tables will no longer work - zarray_vmap(gopt->options, getopt_option_destroy); + zarray_vmap(gopt->options, getopt_option_destroy_void); zarray_destroy(gopt->options); // free tables @@ -506,10 +511,10 @@ char * getopt_get_usage(getopt_t *gopt) if (goo->spacer) continue; - longwidth = max(longwidth, (int) strlen(goo->lname)); + longwidth = imax(longwidth, (int) strlen(goo->lname)); if (goo->type == GOO_STRING_TYPE) - valuewidth = max(valuewidth, (int) strlen(goo->svalue)); + valuewidth = imax(valuewidth, (int) strlen(goo->svalue)); } for (int i = 0; i < zarray_size(gopt->options); i++) { diff --git a/common/getopt.h b/common/getopt.h index 69dbb05c..1f036628 100644 --- a/common/getopt.h +++ b/common/getopt.h @@ -38,6 +38,7 @@ typedef struct getopt getopt_t; getopt_t *getopt_create(); void getopt_destroy(getopt_t *gopt); +void getopt_option_destroy_void(void *goo); // Parse args. Returns 1 on success int getopt_parse(getopt_t *gopt, int argc, char *argv[], int showErrors); diff --git a/common/math_util.h b/common/math_util.h index 9271a018..aa088836 100644 --- a/common/math_util.h +++ b/common/math_util.h @@ -27,31 +27,19 @@ either expressed or implied, of the Regents of The University of Michigan. #pragma once +#define _USE_MATH_DEFINES #include -#include #include #include #include -#include // memcpy #ifdef __cplusplus extern "C" { #endif -#ifndef M_TWOPI -# define M_TWOPI 6.2831853071795862319959 /* 2*pi */ -#endif - -#ifndef M_PI -# define M_PI 3.141592653589793238462643383279502884196 -#endif - #define to_radians(x) ( (x) * (M_PI / 180.0 )) #define to_degrees(x) ( (x) * (180.0 / M_PI )) -#define max(A, B) (A < B ? B : A) -#define min(A, B) (A < B ? A : B) - /* DEPRECATE, threshold meaningless without context. static inline int dequals(double a, double b) { @@ -111,7 +99,7 @@ static inline int irand(int bound) /** Map vin to [0, 2*PI) **/ static inline double mod2pi_positive(double vin) { - return vin - M_TWOPI * floor(vin / M_TWOPI); + return vin - M_2_PI * floor(vin / M_2_PI); } /** Map vin to [-PI, PI) **/ @@ -145,7 +133,7 @@ static inline int mod_positive(int vin, int mod) { static inline int theta_to_int(double theta, int max) { theta = mod2pi_ref(M_PI, theta); - int v = (int) (theta / M_TWOPI * max); + int v = (int) (theta / M_2_PI * max); if (v == max) v = 0; diff --git a/common/pjpeg-idct.c b/common/pjpeg-idct.c index ddbdde50..099c68c7 100644 --- a/common/pjpeg-idct.c +++ b/common/pjpeg-idct.c @@ -25,13 +25,10 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the Regents of The University of Michigan. */ +#define _USE_MATH_DEFINES #include #include -#ifndef M_PI -# define M_PI 3.141592653589793238462643383279502884196 -#endif - // 8 bits of fixed-point output // // This implementation has a worst-case complexity of 22 multiplies diff --git a/common/pthreads_cross.cpp b/common/pthreads_cross.cpp index f7721912..3403863f 100644 --- a/common/pthreads_cross.cpp +++ b/common/pthreads_cross.cpp @@ -21,7 +21,6 @@ SOFTWARE. */ #include "common/pthreads_cross.h" -#include #ifdef _WIN32 @@ -45,7 +44,10 @@ int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routin if (thread == NULL || start_routine == NULL) return 1; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" *thread = (HANDLE) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL); +#pragma GCC diagnostic pop if (*thread == NULL) return 1; return 0; @@ -230,15 +232,10 @@ void ms_to_timespec(struct timespec *ts, unsigned int ms) unsigned int timespec_to_ms(const struct timespec *abstime) { - DWORD t; - if (abstime == NULL) return INFINITE; - t = ((abstime->tv_sec - time(NULL)) * 1000) + (abstime->tv_nsec / 1000000); - if (t < 0) - t = 1; - return t; + return ((abstime->tv_sec - time(NULL)) * 1000) + (abstime->tv_nsec / 1000000); } unsigned int pcthread_get_num_procs() diff --git a/common/pthreads_cross.h b/common/pthreads_cross.h index 897a3335..5970c679 100644 --- a/common/pthreads_cross.h +++ b/common/pthreads_cross.h @@ -30,6 +30,7 @@ SOFTWARE. #include #include #endif +#include #ifdef _WIN32 diff --git a/common/time_util.h b/common/time_util.h index 207e9583..c1840495 100644 --- a/common/time_util.h +++ b/common/time_util.h @@ -40,6 +40,8 @@ typedef long long suseconds_t; inline int gettimeofday(struct timeval* tp, void* tzp) { + (void)tzp; + unsigned long t; t = time(NULL); tp->tv_sec = t / 1000; diff --git a/common/workerpool.c b/common/workerpool.c index a0170ef8..359abfef 100644 --- a/common/workerpool.c +++ b/common/workerpool.c @@ -66,26 +66,19 @@ void *worker_thread(void *p) { workerpool_t *wp = (workerpool_t*) p; - int cnt = 0; - while (1) { struct task *task; pthread_mutex_lock(&wp->mutex); while (wp->taskspos == zarray_size(wp->tasks)) { wp->end_count++; -// printf("%"PRId64" thread %d did %d\n", utime_now(), pthread_self(), cnt); pthread_cond_broadcast(&wp->endcond); pthread_cond_wait(&wp->startcond, &wp->mutex); - cnt = 0; -// printf("%"PRId64" thread %d awake\n", utime_now(), pthread_self()); } zarray_get_volatile(wp->tasks, wp->taskspos, &task); wp->taskspos++; - cnt++; pthread_mutex_unlock(&wp->mutex); -// pthread_yield(); sched_yield(); // we've been asked to exit. diff --git a/common/zarray.c b/common/zarray.c index 43e6a7e0..fa1f8a25 100644 --- a/common/zarray.c +++ b/common/zarray.c @@ -41,7 +41,7 @@ int zstrcmp(const void * a_pp, const void * b_pp) return strcmp(a,b); } -void zarray_vmap(zarray_t *za, void (*f)()) +void zarray_vmap(zarray_t *za, void (*f)(void*)) { assert(za != NULL); assert(f != NULL); diff --git a/common/zarray.h b/common/zarray.h index 22b4c2bb..1c9da4a6 100644 --- a/common/zarray.h +++ b/common/zarray.h @@ -355,7 +355,7 @@ static inline void zarray_map(zarray_t *za, void (*f)(void*)) * * void map_function(element_type *element) */ - void zarray_vmap(zarray_t *za, void (*f)()); + void zarray_vmap(zarray_t *za, void (*f)(void *)); /** * Removes all elements from the array and sets its size to zero. Pointers to diff --git a/common/zhash.c b/common/zhash.c index faf52233..b4a2ec09 100644 --- a/common/zhash.c +++ b/common/zhash.c @@ -352,7 +352,7 @@ void zhash_iterator_remove(zhash_iterator_t *zit) zit->last_entry--; } -void zhash_map_keys(zhash_t *zh, void (*f)()) +void zhash_map_keys(zhash_t *zh, void (*f)(void*)) { assert(zh != NULL); if (f == NULL) @@ -368,7 +368,7 @@ void zhash_map_keys(zhash_t *zh, void (*f)()) } } -void zhash_vmap_keys(zhash_t * zh, void (*f)()) +void zhash_vmap_keys(zhash_t * zh, void (*f)(void*)) { assert(zh != NULL); if (f == NULL) @@ -385,7 +385,7 @@ void zhash_vmap_keys(zhash_t * zh, void (*f)()) } } -void zhash_map_values(zhash_t * zh, void (*f)()) +void zhash_map_values(zhash_t * zh, void (*f)(void*)) { assert(zh != NULL); if (f == NULL) @@ -400,7 +400,7 @@ void zhash_map_values(zhash_t * zh, void (*f)()) } } -void zhash_vmap_values(zhash_t * zh, void (*f)()) +void zhash_vmap_values(zhash_t * zh, void (*f)(void*)) { assert(zh != NULL); if (f == NULL) diff --git a/common/zhash.h b/common/zhash.h index f3dee1aa..9993a66e 100644 --- a/common/zhash.h +++ b/common/zhash.h @@ -259,7 +259,7 @@ void zhash_iterator_remove(zhash_iterator_t *zit); * for the key, which the caller should not modify, as the hash table will not be * re-indexed. The function may be NULL, in which case no action is taken. */ -void zhash_map_keys(zhash_t *zh, void (*f)()); +void zhash_map_keys(zhash_t *zh, void (*f)(void *)); /** * Calls the supplied function with a pointer to every value in the hash table in @@ -267,7 +267,7 @@ void zhash_map_keys(zhash_t *zh, void (*f)()); * for the value, which the caller may safely modify. The function may be NULL, * in which case no action is taken. */ -void zhash_map_values(zhash_t *zh, void (*f)()); +void zhash_map_values(zhash_t *zh, void (*f)(void *)); /** * Calls the supplied function with a copy of every key in the hash table in @@ -280,7 +280,7 @@ void zhash_map_values(zhash_t *zh, void (*f)()); * Use with non-pointer keys (i.e. integer, double, etc.) will likely cause a * segmentation fault. */ -void zhash_vmap_keys(zhash_t *vh, void (*f)()); +void zhash_vmap_keys(zhash_t *vh, void (*f)(void *)); /** * Calls the supplied function with a copy of every value in the hash table in @@ -293,7 +293,7 @@ void zhash_vmap_keys(zhash_t *vh, void (*f)()); * Use with non-pointer values (i.e. integer, double, etc.) will likely cause a * segmentation fault. */ -void zhash_vmap_values(zhash_t *vh, void (*f)()); +void zhash_vmap_values(zhash_t *vh, void (*f)(void *)); /** * Returns an array which contains copies of all of the hash table's keys, in no diff --git a/common/zmaxheap.c b/common/zmaxheap.c index 7479f053..3ca30af7 100644 --- a/common/zmaxheap.c +++ b/common/zmaxheap.c @@ -167,7 +167,7 @@ void zmaxheap_add(zmaxheap_t *heap, void *p, float v) } } -void zmaxheap_vmap(zmaxheap_t *heap, void (*f)()) +void zmaxheap_vmap(zmaxheap_t *heap, void (*f)(void*)) { assert(heap != NULL); assert(f != NULL); diff --git a/common/zmaxheap.h b/common/zmaxheap.h index f0020f92..2f4a90d6 100644 --- a/common/zmaxheap.h +++ b/common/zmaxheap.h @@ -39,7 +39,7 @@ struct zmaxheap_iterator { zmaxheap_t *zmaxheap_create(size_t el_sz); -void zmaxheap_vmap(zmaxheap_t *heap, void (*f)()); +void zmaxheap_vmap(zmaxheap_t *heap, void (*f)(void *)); void zmaxheap_destroy(zmaxheap_t *heap);