From eaf730dc73bb2c502c4c76bae36c7acc8a70575c Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Mon, 10 Nov 2025 20:16:43 +0100 Subject: [PATCH 01/40] occt: bump OCCT version to 7.9.2 --- README.md | 4 ++-- ci/occt.Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c68236a..878b37a 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ This software uses **Open CASCADE Technology** (OCCT), which is licensed under t > of or is based on facilities provided by the Open CASCADE Technology software. ### Source Code Availability -The source code for Open CASCADE version 7.4.0 can be obtained from: +The source code for Open CASCADE version 7.9.2 can be obtained from: - Repository: https://github.com/Open-Cascade-SAS/OCCT.git -- Commit: `fd47711d682be943f0e0a13d1fb54911b0499c31` +- Commit: `c5f20409c52bf8f658314d205a0e5d6f0be0969c` **This software makes use of facilities provided by Open CASCADE Technology.** diff --git a/ci/occt.Dockerfile b/ci/occt.Dockerfile index 0e32987..fde0024 100644 --- a/ci/occt.Dockerfile +++ b/ci/occt.Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y \ RUN git clone https://github.com/Open-Cascade-SAS/OCCT.git opencascade WORKDIR /opencascade RUN rm -rf /opencascade/build && mkdir build -RUN git checkout V7_4_0 -b dev-branch +RUN git checkout V7_9_2 -b OCCT-792 RUN mkdir -p build WORKDIR /opencascade/build From af9f65ab1d20e1bd5543b9c809f52e6d132d334c Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Mon, 10 Nov 2025 20:17:13 +0100 Subject: [PATCH 02/40] ci: trigger action on dev-occt branch patterns --- .github/workflows/occt-unit-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/occt-unit-test.yml b/.github/workflows/occt-unit-test.yml index 538300e..680a678 100644 --- a/.github/workflows/occt-unit-test.yml +++ b/.github/workflows/occt-unit-test.yml @@ -1,7 +1,7 @@ name: OCCT CI on: push: - branches: [master, dev-occt] + branches: [master, dev-occt**] paths: - "src/occt/**" - "src/ffi/**" From 7a903fecabfd17ad09aa10bd49673b0b89d6b413 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Tue, 11 Nov 2025 10:45:42 +0100 Subject: [PATCH 03/40] occt: add point deletion function --- src/occt/include/point.hpp | 6 ++++++ src/occt/point.cpp | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/occt/include/point.hpp b/src/occt/include/point.hpp index 8b82a03..605572a 100644 --- a/src/occt/include/point.hpp +++ b/src/occt/include/point.hpp @@ -3,9 +3,15 @@ Copyright (C) 2025 Erick Ahmed */ +#ifndef POINT_HPP +#define POINT_HPP + #pragma once #include TopoDS_Shape make_point(double x, double y, double z); void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z); +void clear_point(TopoDS_Shape& shape); + +#endif diff --git a/src/occt/point.cpp b/src/occt/point.cpp index 84cee5d..5df99f5 100644 --- a/src/occt/point.cpp +++ b/src/occt/point.cpp @@ -23,3 +23,7 @@ void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z) { *y = point.Y(); *z = point.Z(); } + +void clear_point(TopoDS_Shape& shape) { + shape.Nullify(); +} From 60d51d91f5b04b459646be7e57988896c9561c81 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Tue, 11 Nov 2025 10:47:22 +0100 Subject: [PATCH 04/40] test: add unit tests for point deletion --- test/occt/point-test.cpp | 51 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/test/occt/point-test.cpp b/test/occt/point-test.cpp index 0d14308..7279f52 100644 --- a/test/occt/point-test.cpp +++ b/test/occt/point-test.cpp @@ -8,7 +8,7 @@ #include #include "point.hpp" -TEST_CASE("make_point and coord_point behavior", "[make_point]") { +TEST_CASE("make_point and coord_point behavior", "[point]") { auto check_coords = [](const TopoDS_Shape& p, const double expected[3]) { double x, y, z; coord_point(p, &x, &y, &z); @@ -76,3 +76,52 @@ TEST_CASE("make_point and coord_point behavior", "[make_point]") { REQUIRE((std::isnan(z) || std::isinf(z))); } } + +TEST_CASE("clear_point behavior", "[point]") { + SECTION("Clear single point") { + TopoDS_Shape point = make_point(1.0, 2.0, 3.0); + + REQUIRE_FALSE(point.IsNull()); + double x, y, z; + coord_point(point, &x, &y, &z); + REQUIRE(x == Catch::Approx(1.0)); + REQUIRE(y == Catch::Approx(2.0)); + REQUIRE(z == Catch::Approx(3.0)); + + clear_point(point); + + REQUIRE(point.IsNull()); + } + + SECTION("Clear multiple points") { + const int num_points = 5; + TopoDS_Shape points[num_points]; + + for (int i = 0; i < num_points; ++i) { + points[i] = make_point(i * 1.0, i * 2.0, i * 3.0); + REQUIRE_FALSE(points[i].IsNull()); + } + + for (int i = 0; i < num_points; ++i) { + clear_point(points[i]); + REQUIRE(points[i].IsNull()); + } + } + + SECTION("Clear already null shape") { + TopoDS_Shape null_shape; + REQUIRE(null_shape.IsNull()); + + clear_point(null_shape); + REQUIRE(null_shape.IsNull()); + } + + SECTION("Memory efficiency - multiple clear calls") { + TopoDS_Shape point = make_point(1.0, 1.0, 1.0); + + for (int i = 0; i < 100; ++i) { + clear_point(point); + REQUIRE(point.IsNull()); + } + } +} From 94b6085849a4ad724ab2b2e14a6ed4cf08597d4d Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Tue, 11 Nov 2025 10:56:28 +0100 Subject: [PATCH 05/40] test: add benchmarking for point library --- test/occt/point-test.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/test/occt/point-test.cpp b/test/occt/point-test.cpp index 7279f52..06cc2a3 100644 --- a/test/occt/point-test.cpp +++ b/test/occt/point-test.cpp @@ -4,11 +4,12 @@ */ #include +#include #include #include #include "point.hpp" -TEST_CASE("make_point and coord_point behavior", "[point]") { +TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { auto check_coords = [](const TopoDS_Shape& p, const double expected[3]) { double x, y, z; coord_point(p, &x, &y, &z); @@ -75,9 +76,7 @@ TEST_CASE("make_point and coord_point behavior", "[point]") { REQUIRE((std::isnan(y) || std::isinf(y))); REQUIRE((std::isnan(z) || std::isinf(z))); } -} -TEST_CASE("clear_point behavior", "[point]") { SECTION("Clear single point") { TopoDS_Shape point = make_point(1.0, 2.0, 3.0); @@ -125,3 +124,28 @@ TEST_CASE("clear_point behavior", "[point]") { } } } + +TEST_CASE("Benchmark point operations") { + SECTION("make_point performance") { + BENCHMARK("create point") { + return make_point(1.0, 2.0, 3.0); + }; + } + + SECTION("coord_point performance") { + TopoDS_Shape point = make_point(1.0, 2.0, 3.0); + double x, y, z; + + BENCHMARK("extract coordinates") { + coord_point(point, &x, &y, &z); + }; + } + + SECTION("clear_point performance") { + BENCHMARK("create and clear point") { + TopoDS_Shape point = make_point(1.0, 2.0, 3.0); + clear_point(point); + return point.IsNull(); + }; + } +} From 0092d48a3023c314f930d010222a9a1711cb08b9 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Tue, 11 Nov 2025 11:37:15 +0100 Subject: [PATCH 06/40] general: improve README readability --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 878b37a..233e465 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This project is licensed under the GNU Affero General Public License v3.0 - see ## Third-Party Dependencies -This software uses **Open CASCADE Technology** (OCCT), which is licensed under the GNU LGPL version 2.1 with the following exception: +This software makes use of facilities provided by **Open CASCADE Technology** (OCCT), which is licensed under the GNU LGPL version 2.1 with the following exception: ### Open CASCADE Exception (version 1.0) > The object code (i.e. not a source) form of a "work that uses the Library" @@ -33,5 +33,3 @@ This software uses **Open CASCADE Technology** (OCCT), which is licensed under t The source code for Open CASCADE version 7.9.2 can be obtained from: - Repository: https://github.com/Open-Cascade-SAS/OCCT.git - Commit: `c5f20409c52bf8f658314d205a0e5d6f0be0969c` - -**This software makes use of facilities provided by Open CASCADE Technology.** From 2b42634d88d121c8a74b7fb29fa3603c78da4ac1 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Tue, 11 Nov 2025 23:32:40 +0100 Subject: [PATCH 07/40] occt: improve delete point with actual clearing from memory instead of nullification --- src/occt/point.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/occt/point.cpp b/src/occt/point.cpp index 5df99f5..ae1b122 100644 --- a/src/occt/point.cpp +++ b/src/occt/point.cpp @@ -24,6 +24,6 @@ void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z) { *z = point.Z(); } -void clear_point(TopoDS_Shape& shape) { - shape.Nullify(); +void delete_shape(TopoDS_Shape* shape) { + delete shape; } From 29a3ff419da9f73bee6e83a37bea9729e1a4a397 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 00:23:05 +0100 Subject: [PATCH 08/40] occt: refactor library for Rust FFI compatibility --- src/occt/include/point.hpp | 16 ++++++++++++---- src/occt/point.cpp | 35 ++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/occt/include/point.hpp b/src/occt/include/point.hpp index 605572a..2dc09f2 100644 --- a/src/occt/include/point.hpp +++ b/src/occt/include/point.hpp @@ -8,10 +8,18 @@ #pragma once -#include +#ifdef __cplusplus +extern "C" { +#endif -TopoDS_Shape make_point(double x, double y, double z); -void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z); -void clear_point(TopoDS_Shape& shape); +typedef struct point_shape point_shape_t; + +point_shape_t* make_point(double x, double y, double z); +void coord_point(const point_shape_t* shape, double* x, double* y, double* z); +void delete_point(point_shape_t* shape); + +#ifdef __cplusplus +} +#endif #endif diff --git a/src/occt/point.cpp b/src/occt/point.cpp index ae1b122..6b18d31 100644 --- a/src/occt/point.cpp +++ b/src/occt/point.cpp @@ -10,20 +10,29 @@ #include #include -TopoDS_Shape make_point(double x, double y, double z) { - gp_Pnt point(x, y, z); - return BRepBuilderAPI_MakeVertex(point).Vertex(); -} +struct point_shape_t { + TopoDS_Shape shape; +}; -void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z) { - TopoDS_Vertex vertex = TopoDS::Vertex(shape); - gp_Pnt point = BRep_Tool::Pnt(vertex); +extern "C" { + point_shape_t* make_point(double x, double y, double z) { + point_shape_t* result = new point_shape_t(); + gp_Pnt point(x, y, z); + result->shape = BRepBuilderAPI_MakeVertex(point).Vertex(); + return result; + } - *x = point.X(); - *y = point.Y(); - *z = point.Z(); -} + void coord_point(const point_shape_t* shape, double* x, double* y, double* z) { + if (!shape || !x || !y || !z) return; -void delete_shape(TopoDS_Shape* shape) { - delete shape; + TopoDS_Vertex vertex = TopoDS::Vertex(shape->shape); + gp_Pnt point = BRep_Tool::Pnt(vertex); + *x = point.X(); + *y = point.Y(); + *z = point.Z(); + } + + void delete_point(point_shape_t* shape) { + delete shape; + } } From ddc2fb41723c8d3d53dbef94a0fe14eabae1b743 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 00:23:20 +0100 Subject: [PATCH 09/40] occt: update unit tests with new library --- test/occt/point-test.cpp | 101 +++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/test/occt/point-test.cpp b/test/occt/point-test.cpp index 06cc2a3..5a58301 100644 --- a/test/occt/point-test.cpp +++ b/test/occt/point-test.cpp @@ -6,11 +6,10 @@ #include #include #include -#include #include "point.hpp" TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { - auto check_coords = [](const TopoDS_Shape& p, const double expected[3]) { + auto check_coords = [](const point_shape_t* p, const double expected[3]) { double x, y, z; coord_point(p, &x, &y, &z); REQUIRE(x == Catch::Approx(expected[0])); @@ -20,26 +19,30 @@ TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { SECTION("Accept integer coordinates") { double coords[3] = {3, -7, 2}; - TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]); + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); check_coords(p, coords); + delete_point(p); } SECTION("Accept float coordinates") { double coords[3] = {3.2352, 7.124662, -2.5}; - TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]); + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); check_coords(p, coords); + delete_point(p); } SECTION("XYZ Origin") { double coords[3] = {0, 0, 0}; - TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]); + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); check_coords(p, coords); + delete_point(p); } SECTION("Large magnitude values") { double coords[3] = {6.5186415e7, 9.48156654e8, -6.516515e6}; - TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]); + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); check_coords(p, coords); + delete_point(p); } SECTION("Multiple points") { @@ -52,8 +55,9 @@ TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { }; for (const auto& c : points) { - TopoDS_Shape p = make_point(c[0], c[1], c[2]); + point_shape_t* p = make_point(c[0], c[1], c[2]); check_coords(p, c); + delete_point(p); } } @@ -61,91 +65,108 @@ TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { double coords[3] = {std::numeric_limits::max(), std::numeric_limits::lowest(), std::numeric_limits::min()}; - TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]); + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); check_coords(p, coords); + delete_point(p); } SECTION("NaN and Infinity") { - double coords[3] = {std::numeric_limits::quiet_NaN(), - std::numeric_limits::infinity(), - -std::numeric_limits::infinity()}; - TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]); + double coords[3] = {std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); double x, y, z; coord_point(p, &x, &y, &z); - REQUIRE((std::isnan(x) || std::isinf(x))); - REQUIRE((std::isnan(y) || std::isinf(y))); - REQUIRE((std::isnan(z) || std::isinf(z))); + + REQUIRE(std::isnan(x)); + REQUIRE(std::isinf(y)); + REQUIRE(std::isinf(z)); + REQUIRE(y > 0); + REQUIRE(z < 0); + + delete_point(p); + } + + SECTION("Denormal values") { + double coords[3] = {std::numeric_limits::denorm_min(), + -std::numeric_limits::denorm_min(), + 0.0}; + point_shape_t* p = make_point(coords[0], coords[1], coords[2]); + check_coords(p, coords); + delete_point(p); } SECTION("Clear single point") { - TopoDS_Shape point = make_point(1.0, 2.0, 3.0); + point_shape_t* point = make_point(1.0, 2.0, 3.0); - REQUIRE_FALSE(point.IsNull()); double x, y, z; coord_point(point, &x, &y, &z); REQUIRE(x == Catch::Approx(1.0)); REQUIRE(y == Catch::Approx(2.0)); REQUIRE(z == Catch::Approx(3.0)); - clear_point(point); - - REQUIRE(point.IsNull()); + delete_point(point); } SECTION("Clear multiple points") { const int num_points = 5; - TopoDS_Shape points[num_points]; + point_shape_t* points[num_points]; for (int i = 0; i < num_points; ++i) { points[i] = make_point(i * 1.0, i * 2.0, i * 3.0); - REQUIRE_FALSE(points[i].IsNull()); } for (int i = 0; i < num_points; ++i) { - clear_point(points[i]); - REQUIRE(points[i].IsNull()); + delete_point(points[i]); } } SECTION("Clear already null shape") { - TopoDS_Shape null_shape; - REQUIRE(null_shape.IsNull()); - - clear_point(null_shape); - REQUIRE(null_shape.IsNull()); + point_shape_t* null_shape = nullptr; + REQUIRE(null_shape == nullptr); + delete_point(null_shape); } SECTION("Memory efficiency - multiple clear calls") { - TopoDS_Shape point = make_point(1.0, 1.0, 1.0); - for (int i = 0; i < 100; ++i) { - clear_point(point); - REQUIRE(point.IsNull()); + point_shape_t* temp_point = make_point(1.0, 1.0, 1.0); + delete_point(temp_point); } } + + SECTION("Pointer validity after operations") { + point_shape_t* p = make_point(1.0, 2.0, 3.0); + REQUIRE(p != nullptr); + + double x, y, z; + coord_point(p, &x, &y, &z); + + delete_point(p); + } } TEST_CASE("Benchmark point operations") { SECTION("make_point performance") { + point_shape_t* p = nullptr; BENCHMARK("create point") { - return make_point(1.0, 2.0, 3.0); + return p = make_point(1.0, 2.0, 3.0); }; + delete_point(p); } SECTION("coord_point performance") { - TopoDS_Shape point = make_point(1.0, 2.0, 3.0); + point_shape_t* point = make_point(1.0, 2.0, 3.0); double x, y, z; BENCHMARK("extract coordinates") { coord_point(point, &x, &y, &z); }; + delete_point(point); } - SECTION("clear_point performance") { - BENCHMARK("create and clear point") { - TopoDS_Shape point = make_point(1.0, 2.0, 3.0); - clear_point(point); - return point.IsNull(); + SECTION("delete_point performance") { + BENCHMARK("create and delete point") { + point_shape_t* point = make_point(1.0, 2.0, 3.0); + delete_point(point); + return point; }; } } From e9cb80797df4f70dce7338707ee836f8ea19ddc5 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 01:12:02 +0100 Subject: [PATCH 10/40] ffi: add Rust bindings for point.hpp --- src/wrappers/point.rs | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/wrappers/point.rs diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs new file mode 100644 index 0000000..5cf1d93 --- /dev/null +++ b/src/wrappers/point.rs @@ -0,0 +1,71 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + +use std::os::raw::c_double; + +// FFI bindings + +#[repr(C)] +pub struct PointShape { + _private: [u8; 0], +} + +extern "C" { + pub fn make_point(x: c_double, y: c_double, z: c_double) -> *mut PointShape; + pub fn coord_point( + shape: *const PointShape, + x: *mut c_double, + y: *mut c_double, + z: *mut c_double, + ); + pub fn delete_point(shape: *mut PointShape); +} + +// Safe wrapper + +pub struct Point { + ptr: *mut PointShape, +} + +impl Point { + pub fn new(x: f64, y: f64, z: f64) -> Self { + unsafe { + let ptr = make_point(x, y, z); + if ptr.is_null() { + panic!("Error: null pointer"); + } + Self { ptr } + } + } + + pub fn coordinates(&self) -> (f64, f64, f64) { + unsafe { + let mut x = 0.0; + let mut y = 0.0; + let mut z = 0.0; + + coord_point(self.ptr, &mut x, &mut y, &mut z); + (x, y, z) + } + } +} + +impl Drop for Point { + fn drop(&mut self) { + unsafe { + delete_point(self.ptr); + } + } +} + +impl Clone for Point { + fn clone(&self) -> Self { + let (x, y, z) = self.coordinates(); + Self::new(x, y, z) + } +} + +unsafe impl Send for Point {} +unsafe impl Sync for Point {} From 6aa5c324303a41466a6452d93362d9ef9f4b3550 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 09:55:49 +0100 Subject: [PATCH 11/40] test: improve precision in benchmarking operations --- test/occt/point-test.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/occt/point-test.cpp b/test/occt/point-test.cpp index 5a58301..3d63035 100644 --- a/test/occt/point-test.cpp +++ b/test/occt/point-test.cpp @@ -146,27 +146,27 @@ TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { TEST_CASE("Benchmark point operations") { SECTION("make_point performance") { point_shape_t* p = nullptr; - BENCHMARK("create point") { + BENCHMARK("Create point") { return p = make_point(1.0, 2.0, 3.0); }; delete_point(p); } SECTION("coord_point performance") { - point_shape_t* point = make_point(1.0, 2.0, 3.0); + point_shape_t* p = make_point(1.0, 2.0, 3.0); double x, y, z; - BENCHMARK("extract coordinates") { - coord_point(point, &x, &y, &z); + BENCHMARK("Extract coordinates") { + coord_point(p, &x, &y, &z); }; - delete_point(point); + delete_point(p); } SECTION("delete_point performance") { - BENCHMARK("create and delete point") { - point_shape_t* point = make_point(1.0, 2.0, 3.0); - delete_point(point); - return point; + point_shape_t* p = make_point(1.0, 2.0, 3.0); + BENCHMARK("Delete point") { + delete_point(p); + return p; }; } } From 422f08835edfa7b8a30486e2e6eb882629b2057c Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 09:58:44 +0100 Subject: [PATCH 12/40] general: change point variables nomenclature to same standard --- Cargo.toml | 2 +- test/occt/point-test.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a885705..3d63cd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "arc-core" -version = "0.0.1" +version = "0.0.2" edition = "2024" license = "AGPL-3.0-or-later" diff --git a/test/occt/point-test.cpp b/test/occt/point-test.cpp index 3d63035..fe54c25 100644 --- a/test/occt/point-test.cpp +++ b/test/occt/point-test.cpp @@ -98,37 +98,37 @@ TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { point_shape_t* point = make_point(1.0, 2.0, 3.0); double x, y, z; - coord_point(point, &x, &y, &z); + coord_point(p, &x, &y, &z); REQUIRE(x == Catch::Approx(1.0)); REQUIRE(y == Catch::Approx(2.0)); REQUIRE(z == Catch::Approx(3.0)); - delete_point(point); + delete_point(p); } SECTION("Clear multiple points") { const int num_points = 5; - point_shape_t* points[num_points]; + point_shape_t* p[num_points]; for (int i = 0; i < num_points; ++i) { - points[i] = make_point(i * 1.0, i * 2.0, i * 3.0); + p[i] = make_point(i * 1.0, i * 2.0, i * 3.0); } for (int i = 0; i < num_points; ++i) { - delete_point(points[i]); + delete_point(p[i]); } } SECTION("Clear already null shape") { - point_shape_t* null_shape = nullptr; - REQUIRE(null_shape == nullptr); - delete_point(null_shape); + point_shape_t* null_p = nullptr; + REQUIRE(null_p == nullptr); + delete_point(null_p); } SECTION("Memory efficiency - multiple clear calls") { for (int i = 0; i < 100; ++i) { - point_shape_t* temp_point = make_point(1.0, 1.0, 1.0); - delete_point(temp_point); + point_shape_t* tmp_p = make_point(1.0, 1.0, 1.0); + delete_point(tmp_p); } } @@ -147,7 +147,7 @@ TEST_CASE("Benchmark point operations") { SECTION("make_point performance") { point_shape_t* p = nullptr; BENCHMARK("Create point") { - return p = make_point(1.0, 2.0, 3.0); + return p = makes_point(1.0, 2.0, 3.0); }; delete_point(p); } From 2802f7659fbd273b2a62511cb257db27acc78ac8 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 15:13:48 +0100 Subject: [PATCH 13/40] ffi: make C function private --- src/wrappers/point.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs index 5cf1d93..b69f6f4 100644 --- a/src/wrappers/point.rs +++ b/src/wrappers/point.rs @@ -23,7 +23,16 @@ extern "C" { pub fn delete_point(shape: *mut PointShape); } -// Safe wrapper + extern "C" { + fn make_point(x: c_double, y: c_double, z: c_double) -> *mut PointShape; + fn coord_point( + shape: *const PointShape, + x: *mut c_double, + y: *mut c_double, + z: *mut c_double, + ); + fn delete_point(shape: *mut PointShape); + } pub struct Point { ptr: *mut PointShape, From c1488507f2dc14c998cf9c35dfc10c6921d1c39e Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 15:14:20 +0100 Subject: [PATCH 14/40] ffi: wrap unsafe FFI functions in safe wrappers --- src/wrappers/point.rs | 97 +++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 59 deletions(-) diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs index b69f6f4..60b9bd5 100644 --- a/src/wrappers/point.rs +++ b/src/wrappers/point.rs @@ -1,29 +1,12 @@ -/* - SPDX-License-Identifier: AGPL-3.0-or-later - Copyright (C) 2025 Erick Ahmed -*/ +mod ffi_point { + use std::os::raw::c_double; -use std::os::raw::c_double; + #[repr(C)] + pub(crate) struct PointShape { + _private: [u8; 0], + } -// FFI bindings - -#[repr(C)] -pub struct PointShape { - _private: [u8; 0], -} - -extern "C" { - pub fn make_point(x: c_double, y: c_double, z: c_double) -> *mut PointShape; - pub fn coord_point( - shape: *const PointShape, - x: *mut c_double, - y: *mut c_double, - z: *mut c_double, - ); - pub fn delete_point(shape: *mut PointShape); -} - - extern "C" { + unsafe extern "C" { fn make_point(x: c_double, y: c_double, z: c_double) -> *mut PointShape; fn coord_point( shape: *const PointShape, @@ -34,47 +17,43 @@ extern "C" { fn delete_point(shape: *mut PointShape); } -pub struct Point { - ptr: *mut PointShape, -} + // Safe wrapper -impl Point { - pub fn new(x: f64, y: f64, z: f64) -> Self { - unsafe { - let ptr = make_point(x, y, z); - if ptr.is_null() { - panic!("Error: null pointer"); + pub struct Point { + ptr: *mut PointShape, + } + + impl Point { + pub fn new(x: f64, y: f64, z: f64) -> Result { + unsafe { + let ptr = make_point(x, y, z); + if ptr.is_null() { + Err("Error: null pointer returned") + } else { + Ok(Point { ptr }) + } + } + } + + pub fn coordinates(&self) -> (f64, f64, f64) { + unsafe { + let mut x = 0.0; + let mut y = 0.0; + let mut z = 0.0; + coord_point(self.ptr, &mut x, &mut y, &mut z); + (x, y, z) } - Self { ptr } } } - pub fn coordinates(&self) -> (f64, f64, f64) { - unsafe { - let mut x = 0.0; - let mut y = 0.0; - let mut z = 0.0; - - coord_point(self.ptr, &mut x, &mut y, &mut z); - (x, y, z) + impl Drop for Point { + fn drop(&mut self) { + unsafe { + delete_point(self.ptr); + } } } -} -impl Drop for Point { - fn drop(&mut self) { - unsafe { - delete_point(self.ptr); - } - } + unsafe impl Send for Point {} + unsafe impl Sync for Point {} } - -impl Clone for Point { - fn clone(&self) -> Self { - let (x, y, z) = self.coordinates(); - Self::new(x, y, z) - } -} - -unsafe impl Send for Point {} -unsafe impl Sync for Point {} From f96e85416e5cff1bdd200eb669c954a2fe2ed074 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 15:43:48 +0100 Subject: [PATCH 15/40] ffi: add debug method --- src/wrappers/point.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs index 60b9bd5..9ecdbf6 100644 --- a/src/wrappers/point.rs +++ b/src/wrappers/point.rs @@ -54,6 +54,12 @@ mod ffi_point { } } - unsafe impl Send for Point {} - unsafe impl Sync for Point {} + // TODO: check if i can mark as thread-safe + + impl std::fmt::Debug for Point { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (x, y, z) = self.coordinates(); + write!(f, "Point({}, {}, {})", x, y, z) + } + } } From 612ee5da6ea25a5e59a7ae3d1294fdff6f6b7562 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 15:48:24 +0100 Subject: [PATCH 16/40] test: rename to tests/ for Rust integration test compatibility --- {test => tests}/occt/CMakeLists.txt | 0 {test => tests}/occt/point-test.cpp | 0 tests/wrappers/point-test.rs | 10 ++++++++++ 3 files changed, 10 insertions(+) rename {test => tests}/occt/CMakeLists.txt (100%) rename {test => tests}/occt/point-test.cpp (100%) create mode 100644 tests/wrappers/point-test.rs diff --git a/test/occt/CMakeLists.txt b/tests/occt/CMakeLists.txt similarity index 100% rename from test/occt/CMakeLists.txt rename to tests/occt/CMakeLists.txt diff --git a/test/occt/point-test.cpp b/tests/occt/point-test.cpp similarity index 100% rename from test/occt/point-test.cpp rename to tests/occt/point-test.cpp diff --git a/tests/wrappers/point-test.rs b/tests/wrappers/point-test.rs new file mode 100644 index 0000000..5736cdb --- /dev/null +++ b/tests/wrappers/point-test.rs @@ -0,0 +1,10 @@ +#[test] +mod tests { + use arc_core::ffi::point::Point; + + fn point_creation() { + let p = Point::new(1.0, 2.0, 3.0); + let coords = p.coordinates(); + assert_eq!(coords, (1.0, 2.0, 3.0)); + } +} From 022bc4e9e282ec22d92e6e73150dbb5f11bd310f Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:17:10 +0100 Subject: [PATCH 17/40] ffi: export pub API --- src/wrappers/point.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs index 9ecdbf6..3589a43 100644 --- a/src/wrappers/point.rs +++ b/src/wrappers/point.rs @@ -18,7 +18,6 @@ mod ffi_point { } // Safe wrapper - pub struct Point { ptr: *mut PointShape, } @@ -63,3 +62,5 @@ mod ffi_point { } } } + +pub use ffi_point::Point; From 13c8dae325232e6f1d2933f9c4857beb3013a8df Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:17:45 +0100 Subject: [PATCH 18/40] general: add header --- src/wrappers/point.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs index 3589a43..c01917f 100644 --- a/src/wrappers/point.rs +++ b/src/wrappers/point.rs @@ -1,3 +1,8 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + mod ffi_point { use std::os::raw::c_double; From ced3c4d22b3c303098677d3c1f7ca0799868942b Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:18:42 +0100 Subject: [PATCH 19/40] ffi: create lib.rs --- src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/lib.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e3b388e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + +pub mod wrappers { + pub mod point; +} From 3192e81443102592a00e6299e7bbd9480d51fd6f Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:19:37 +0100 Subject: [PATCH 20/40] test: move to tests/ because of issues with `cargo test` --- tests/point_test.rs | 13 +++++++++++++ tests/wrappers/point-test.rs | 10 ---------- 2 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 tests/point_test.rs delete mode 100644 tests/wrappers/point-test.rs diff --git a/tests/point_test.rs b/tests/point_test.rs new file mode 100644 index 0000000..1ab8d00 --- /dev/null +++ b/tests/point_test.rs @@ -0,0 +1,13 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + +use arc_core::wrappers::point::Point; + +#[test] +fn point_creation() { + let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point"); + let coords = p.coordinates(); + assert_eq!(coords, (1.0, 2.0, 3.0)); +} diff --git a/tests/wrappers/point-test.rs b/tests/wrappers/point-test.rs deleted file mode 100644 index 5736cdb..0000000 --- a/tests/wrappers/point-test.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[test] -mod tests { - use arc_core::ffi::point::Point; - - fn point_creation() { - let p = Point::new(1.0, 2.0, 3.0); - let coords = p.coordinates(); - assert_eq!(coords, (1.0, 2.0, 3.0)); - } -} From f84157756e2b57c6fe34cd538e54a34bf83433d8 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:19:55 +0100 Subject: [PATCH 21/40] general: add lib --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 3d63cd0..be840b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,8 @@ version = "0.0.2" edition = "2024" license = "AGPL-3.0-or-later" +[lib] +name = "arc_core" +path = "src/lib.rs" + [dependencies] From 708bb84df79af30ce37dafafd39056f77ffedf50 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:45:57 +0100 Subject: [PATCH 22/40] ffi: add build dependency --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index be840b4..2899a05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,6 @@ name = "arc_core" path = "src/lib.rs" [dependencies] + +[build-dependencies] +cc = "1.0" From 4e8f903b703ac3e3d000d8b9b8521f80afd2ad61 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:46:10 +0100 Subject: [PATCH 23/40] general: add header --- src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main.rs diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..545e950 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,6 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + +fn main() {} From 33366ebb51408023cc3111b5de8ff582da21c66c Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 16:46:22 +0100 Subject: [PATCH 24/40] ci: add build instructions --- src/build.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/build.rs diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..bfba48e --- /dev/null +++ b/src/build.rs @@ -0,0 +1,15 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + +fn main() { + cc::Build::new() + .cpp(true) + .file("src/occt/point.cpp") + .include("src/occt/include") + .compile("point"); + + println!("cargo:rerun-if-changed=src/occt/point.cpp"); + println!("cargo:rerun-if-changed=src/occt/include/point.hpp"); +} From 5b37b34422ed261213526ab408ab9ae7ad13d70c Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 17:21:41 +0100 Subject: [PATCH 25/40] general: add comment for FFI and wrapper phases --- src/wrappers/point.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wrappers/point.rs b/src/wrappers/point.rs index c01917f..12c6e36 100644 --- a/src/wrappers/point.rs +++ b/src/wrappers/point.rs @@ -4,6 +4,8 @@ */ mod ffi_point { + //FFI + use std::os::raw::c_double; #[repr(C)] @@ -23,6 +25,7 @@ mod ffi_point { } // Safe wrapper + pub struct Point { ptr: *mut PointShape, } From 8f874526914575ccfcb14aecf8f2d694b3c81737 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:03:00 +0100 Subject: [PATCH 26/40] ci: move to root --- build.rs | 13 +++++++++++++ src/build.rs | 15 --------------- 2 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 build.rs delete mode 100644 src/build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..749625c --- /dev/null +++ b/build.rs @@ -0,0 +1,13 @@ +/* + SPDX-License-Identifier: AGPL-3.0-or-later + Copyright (C) 2025 Erick Ahmed +*/ + +fn main() { + println!("cargo:rustc-link-search=/usr/local/lib"); + println!("cargo:rustc-link-search=/app/build"); + + println!("cargo:rustc-link-lib=occt"); + + println!("cargo:rerun-if-changed=src/wrappers/"); +} diff --git a/src/build.rs b/src/build.rs deleted file mode 100644 index bfba48e..0000000 --- a/src/build.rs +++ /dev/null @@ -1,15 +0,0 @@ -/* - SPDX-License-Identifier: AGPL-3.0-or-later - Copyright (C) 2025 Erick Ahmed -*/ - -fn main() { - cc::Build::new() - .cpp(true) - .file("src/occt/point.cpp") - .include("src/occt/include") - .compile("point"); - - println!("cargo:rerun-if-changed=src/occt/point.cpp"); - println!("cargo:rerun-if-changed=src/occt/include/point.hpp"); -} From 958d7965cea99ad02dec124bb6d0865ea34196b1 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:03:46 +0100 Subject: [PATCH 27/40] ci: change tag name --- ci/occt.Dockerfile | 18 +++++++----------- ci/scripts/occt.sh | 4 ++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ci/occt.Dockerfile b/ci/occt.Dockerfile index fde0024..7a6de04 100644 --- a/ci/occt.Dockerfile +++ b/ci/occt.Dockerfile @@ -1,5 +1,4 @@ -# Base builder -FROM ubuntu:24.04 AS base +FROM ubuntu:24.04 AS occt-base ENV DEBIAN_FRONTEND=noninteractive @@ -84,10 +83,9 @@ COPY src/occt/ ./src RUN cmake -S src -B build -DCMAKE_PREFIX_PATH=/usr/local RUN cmake --build build -COPY test/occt/ ./test +COPY tests/occt/ ./test -# Unit test runner stage -FROM ubuntu:24.04 AS unit-testing +FROM ubuntu:24.04 AS occt-unit-testing ENV DEBIAN_FRONTEND=noninteractive @@ -98,12 +96,10 @@ RUN apt-get update && apt-get install -y \ xvfb \ && rm -rf /var/lib/apt/lists/* -COPY --from=base /usr/local /usr/local -COPY --from=base /app/build /app/build -COPY --from=base /app/src /app/src -COPY --from=base /app/test /app/test -COPY --from=base /usr/local/include /usr/local/include -COPY --from=base /usr/local/lib /usr/local/lib +COPY --from=occt-base /usr/local /usr/local +COPY --from=occt-base /app/build /app/build +COPY --from=occt-base /app/src /app/src +COPY --from=occt-base /app/test /app/test WORKDIR /app diff --git a/ci/scripts/occt.sh b/ci/scripts/occt.sh index 17d260f..80b83bc 100644 --- a/ci/scripts/occt.sh +++ b/ci/scripts/occt.sh @@ -12,11 +12,11 @@ if [[ "${1-}" == "--unit-testing" ]]; then fi echo "Building base image..." -docker build --target base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" +docker build --target occt-base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" if [ "$RUN_UNIT_TESTS" = true ]; then echo "Building unit-testing stage..." - docker build --target unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" + docker build --target occt-unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" echo "Running unit tests..." docker run --rm "$IMAGE_NAME" /app/build/unit-tests/tests From 163091501050ba0071a76d1ddcceb357d66adbcd Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:04:19 +0100 Subject: [PATCH 28/40] ci: add FFI CI pipeline --- ci/ffi.Dockerfile | 38 ++++++++++++++++++++++++++++++++++++++ ci/scripts/ffi.sh | 23 +++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 ci/ffi.Dockerfile create mode 100644 ci/scripts/ffi.sh diff --git a/ci/ffi.Dockerfile b/ci/ffi.Dockerfile new file mode 100644 index 0000000..d49bd7e --- /dev/null +++ b/ci/ffi.Dockerfile @@ -0,0 +1,38 @@ +FROM occt-builder:latest AS ffi-base + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y curl build-essential + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +WORKDIR /app +COPY . . + +ENV LD_LIBRARY_PATH=/usr/local/lib:/app/build +ENV RUSTFLAGS="-L /usr/local/lib -L /app/build" + +RUN cargo build + +FROM ubuntu:24.04 AS ffi-unit-testing + +RUN apt-get update && apt-get install -y \ + build-essential cmake git curl \ + libfreetype-dev \ + xvfb \ + && rm -rf /var/lib/apt/lists/* + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +COPY --from=occt-builder:latest /usr/local /usr/local +COPY --from=occt-builder:latest /app/build /app/occt-build +COPY --from=ffi-base /app /app + +WORKDIR /app + +ENV LD_LIBRARY_PATH=/usr/local/lib:/app/occt-build +ENV RUSTFLAGS="-L /usr/local/lib -L /app/occt-build" + +CMD ["cargo", "test", "--verbose", "--", "--nocapture"] diff --git a/ci/scripts/ffi.sh b/ci/scripts/ffi.sh new file mode 100644 index 0000000..14a51ab --- /dev/null +++ b/ci/scripts/ffi.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +IMAGE_NAME="ffi-builder:latest" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_PATH="$(cd "$SCRIPT_DIR/../../" && pwd)" +DOCKERFILE_PATH="$SCRIPT_DIR/../ffi.Dockerfile" + +RUN_UNIT_TESTS=false +if [[ "${1-}" == "--tests" ]]; then + RUN_UNIT_TESTS=true +fi + +echo "Building base image..." +docker build --target ffi-base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" + +if [ "$RUN_UNIT_TESTS" = true ]; then + echo "Building unit-testing stage..." + docker build --target ffi-unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" + + echo "Running tests..." + docker run --rm -it "$IMAGE_NAME" cargo test +fi From 5227384e0c163c14ffaaafeb345fc2db9c39809b Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:06:45 +0100 Subject: [PATCH 29/40] general: rename wrappers to ffi for coherency with rest of repo --- src/{wrappers => ffi}/point.rs | 0 src/lib.rs | 2 +- tests/point_test.rs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{wrappers => ffi}/point.rs (100%) diff --git a/src/wrappers/point.rs b/src/ffi/point.rs similarity index 100% rename from src/wrappers/point.rs rename to src/ffi/point.rs diff --git a/src/lib.rs b/src/lib.rs index e3b388e..3748af9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,6 @@ Copyright (C) 2025 Erick Ahmed */ -pub mod wrappers { +pub mod ffi { pub mod point; } diff --git a/tests/point_test.rs b/tests/point_test.rs index 1ab8d00..8cb9b4c 100644 --- a/tests/point_test.rs +++ b/tests/point_test.rs @@ -3,7 +3,7 @@ Copyright (C) 2025 Erick Ahmed */ -use arc_core::wrappers::point::Point; +use arc_core::ffi::point::Point; #[test] fn point_creation() { From 29e7e59ffb69ad47ff6a27c58213d19b8d1ef63e Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:10:04 +0100 Subject: [PATCH 30/40] ci: add CI pipeline for FFI code --- .github/workflows/ffi-unit-test.yml | 67 ++++++++++++++++++++++++++++ .github/workflows/occt-unit-test.yml | 17 +++++++ 2 files changed, 84 insertions(+) create mode 100644 .github/workflows/ffi-unit-test.yml diff --git a/.github/workflows/ffi-unit-test.yml b/.github/workflows/ffi-unit-test.yml new file mode 100644 index 0000000..792225a --- /dev/null +++ b/.github/workflows/ffi-unit-test.yml @@ -0,0 +1,67 @@ +name: FFI CI + +on: + workflow_run: + workflows: ["OCCT CI"] + types: + - completed + push: + branches: [master, dev-occt**, dev-ffi**] + paths: + - "src/ffi/**" + - "test/ffi/**" + - "ci/scripts/ffi.sh" + - "ci/ffi.Dockerfile" + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + pull_request: + branches: [master, dev-occt, dev-ffi] + paths: + - "src/ffi/**" + - "test/ffi/**" + - "ci/scripts/ffi.sh" + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + +jobs: + build-ffi: + name: Build FFI + runs-on: ubuntu-24.04 + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Pull OCCT builder image + run: | + docker pull ghcr.io/${{ github.repository }}/occt-builder:latest || echo "OCCT image not found, will build from source" + + - name: Make script executable + run: chmod +x ./ci/scripts/ffi.sh + + - name: Build FFI and run tests + run: ./ci/scripts/ffi.sh --tests + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: ffi-test-results + path: | + target/debug/ + **/test-results/ + retention-days: 7 diff --git a/.github/workflows/occt-unit-test.yml b/.github/workflows/occt-unit-test.yml index 680a678..20c33e5 100644 --- a/.github/workflows/occt-unit-test.yml +++ b/.github/workflows/occt-unit-test.yml @@ -30,8 +30,25 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Make script executable run: chmod +x ./ci/scripts/occt.sh - name: Build OCCT and run tests run: ./ci/scripts/occt.sh --unit-testing + + - name: Tag and push OCCT image + if: github.event_name != 'pull_request' + run: | + docker tag occt-builder:latest ghcr.io/${{ github.repository }}/occt-builder:latest + docker push ghcr.io/${{ github.repository }}/occt-builder:latest From 3c66a49bb0e952a927cc25f4c6d31236bc5567b0 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:40:13 +0100 Subject: [PATCH 31/40] ffi: implement explicit point deletion --- src/ffi/point.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ffi/point.rs b/src/ffi/point.rs index 12c6e36..bfa9d5f 100644 --- a/src/ffi/point.rs +++ b/src/ffi/point.rs @@ -51,6 +51,19 @@ mod ffi_point { (x, y, z) } } + + pub fn delete(mut self) -> Result<(), &'static str> { + unsafe { + if self.ptr.is_null() { + return Err("Error: Attempted to delete null pointer"); + } + delete_point(self.ptr); + + self.ptr = std::ptr::null_mut(); + std::mem::forget(self); + } + Ok(()) + } } impl Drop for Point { From 2548203b3e5ab9d1fa8be13eaa34ca335bb4ca39 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Wed, 12 Nov 2025 23:40:30 +0100 Subject: [PATCH 32/40] test: move basic test to library file --- src/ffi/point.rs | 45 ++++++++++++++++++++++++++++++++++++--------- tests/point_test.rs | 5 ----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/ffi/point.rs b/src/ffi/point.rs index bfa9d5f..0d5edca 100644 --- a/src/ffi/point.rs +++ b/src/ffi/point.rs @@ -53,17 +53,17 @@ mod ffi_point { } pub fn delete(mut self) -> Result<(), &'static str> { - unsafe { - if self.ptr.is_null() { - return Err("Error: Attempted to delete null pointer"); - } - delete_point(self.ptr); - - self.ptr = std::ptr::null_mut(); - std::mem::forget(self); + unsafe { + if self.ptr.is_null() { + return Err("Error: Attempted to delete null pointer"); } - Ok(()) + delete_point(self.ptr); + + self.ptr = std::ptr::null_mut(); + std::mem::forget(self); } + Ok(()) + } } impl Drop for Point { @@ -85,3 +85,30 @@ mod ffi_point { } pub use ffi_point::Point; + +// Basic tests +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn point_creation() { + let p = Point::new(1.0, 2.0, -3.0).expect("Failed to create point"); + let coords = p.coordinates(); + assert_eq!(coords, (1.0, 2.0, -3.0)); + } + + #[test] + fn point_query() { + let p = Point::new(5.5, 3.15, -0.001).expect("Failed to create point"); + let coords = p.coordinates(); + assert_eq!(coords, (5.5, 3.15, -0.001)); + } + + #[test] + fn point_deletion() { + let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point"); + let result = p.delete(); + assert!(result.is_ok(), "Deletion should succeed for valid point"); + } +} diff --git a/tests/point_test.rs b/tests/point_test.rs index 8cb9b4c..49a33e5 100644 --- a/tests/point_test.rs +++ b/tests/point_test.rs @@ -6,8 +6,3 @@ use arc_core::ffi::point::Point; #[test] -fn point_creation() { - let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point"); - let coords = p.coordinates(); - assert_eq!(coords, (1.0, 2.0, 3.0)); -} From 6ee055c38b13e45f68347468bba6a42b7db6eb2d Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 00:02:09 +0100 Subject: [PATCH 33/40] test: separate tests into debug and API level tests --- src/ffi/point.rs | 57 +++++++++++++++++++----- tests/point_test.rs | 106 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 11 deletions(-) diff --git a/src/ffi/point.rs b/src/ffi/point.rs index 0d5edca..94afd7b 100644 --- a/src/ffi/point.rs +++ b/src/ffi/point.rs @@ -90,25 +90,62 @@ pub use ffi_point::Point; #[cfg(test)] mod tests { use super::*; + use std::os::raw::c_double; #[test] - fn point_creation() { - let p = Point::new(1.0, 2.0, -3.0).expect("Failed to create point"); + fn point_debug_precision() { + let p = Point::new(1.123456789, 2.987654321, -3.555555555).expect("Failed to create point"); + let debug_str = format!("{:?}", p); + let coords = p.coordinates(); - assert_eq!(coords, (1.0, 2.0, -3.0)); + assert!(debug_str.contains(&coords.0.to_string())); + assert!(debug_str.contains(&coords.1.to_string())); + assert!(debug_str.contains(&coords.2.to_string())); } #[test] - fn point_query() { - let p = Point::new(5.5, 3.15, -0.001).expect("Failed to create point"); + fn point_null_pointer_safety() { + let result = Point::new(1.0, 2.0, 3.0); + assert!(result.is_ok()); + + let p = result.unwrap(); let coords = p.coordinates(); - assert_eq!(coords, (5.5, 3.15, -0.001)); + + assert_eq!(coords, (1.0, 2.0, 3.0)); } #[test] - fn point_deletion() { - let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point"); - let result = p.delete(); - assert!(result.is_ok(), "Deletion should succeed for valid point"); + fn point_drop_impl_safety() { + { + let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point"); + // p goes out of scope here and should be dropped + } + let p2 = Point::new(4.0, 5.0, 6.0).expect("Failed to create point after drop"); + assert_eq!(p2.coordinates(), (4.0, 5.0, 6.0)); + } + + #[test] + fn point_ffi_repr_c() { + use std::mem; + + // PointShape should be repr(C) and zero-sized in Rust + assert_eq!(mem::size_of::(), 0); + + // c_double should match f64 + assert_eq!(mem::size_of::(), mem::size_of::()); + assert_eq!(mem::align_of::(), mem::align_of::()); + } + + #[test] + fn point_coordinates_after_multiple_uses() { + let p = Point::new(1.1, 2.2, 3.3).expect("Failed to create point"); + + let _coords1 = p.coordinates(); + let _debug1 = format!("{:?}", p); + let _coords2 = p.coordinates(); + let _debug2 = format!("{:?}", p); + let final_coords = p.coordinates(); + + assert_eq!(final_coords, (1.1, 2.2, 3.3)); } } diff --git a/tests/point_test.rs b/tests/point_test.rs index 49a33e5..e11c9eb 100644 --- a/tests/point_test.rs +++ b/tests/point_test.rs @@ -5,4 +5,108 @@ use arc_core::ffi::point::Point; -#[test] +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn point_creation() { + let p = Point::new(1.0, 2.0, -3.0).expect("Failed to create point"); + let coords = p.coordinates(); + assert_eq!(coords, (1.0, 2.0, -3.0)); + } + + #[test] + fn point_query() { + let p = Point::new(5.5, 3.15, -0.001).expect("Failed to create point"); + let coords = p.coordinates(); + assert_eq!(coords, (5.5, 3.15, -0.001)); + } + + #[test] + fn point_deletion() { + let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point"); + let result = p.delete(); + assert!(result.is_ok(), "Deletion should succeed for valid point"); + } + + #[test] + fn point_creation_extreme_values() { + let cases = [ + (f64::MAX, f64::MIN, 0.0), + (0.0, 0.0, 0.0), + (-f64::MAX, -f64::MIN, f64::EPSILON), + ]; + + for (x, y, z) in cases { + let p = Point::new(x, y, z).expect("Failed to create point with extreme values"); + let coords = p.coordinates(); + assert_eq!(coords, (x, y, z)); + p.delete().expect("Failed to delete point"); + } + } + + #[test] + fn point_coordinates_immutability() { + let p = Point::new(1.5, 2.5, 3.5).expect("Failed to create point"); + + let coords1 = p.coordinates(); + let coords2 = p.coordinates(); + let coords3 = p.coordinates(); + + assert_eq!(coords1, coords2); + assert_eq!(coords2, coords3); + assert_eq!(coords1, (1.5, 2.5, 3.5)); + } + + #[test] + fn point_automatic_cleanup_on_drop() { + let coords = { + let p = Point::new(7.0, 8.0, 9.0).expect("Failed to create point"); + p.coordinates() + }; + + assert_eq!(coords, (7.0, 8.0, 9.0)); + + // Verify we can still create points after automatic cleanup + let p2 = Point::new(10.0, 11.0, 12.0).expect("Failed to create point after drop"); + assert_eq!(p2.coordinates(), (10.0, 11.0, 12.0)); + } + + #[test] + fn point_multiple_instances_independent() { + let p1 = Point::new(1.0, 1.0, 1.0).expect("Failed to create point 1"); + let p2 = Point::new(2.0, 2.0, 2.0).expect("Failed to create point 2"); + let p3 = Point::new(3.0, 3.0, 3.0).expect("Failed to create point 3"); + + assert_eq!(p1.coordinates(), (1.0, 1.0, 1.0)); + assert_eq!(p2.coordinates(), (2.0, 2.0, 2.0)); + assert_eq!(p3.coordinates(), (3.0, 3.0, 3.0)); + + p2.delete().expect("Failed to delete p2"); + p1.delete().expect("Failed to delete p1"); + p3.delete().expect("Failed to delete p3"); + } + + #[test] + fn point_debug_format() { + let p = Point::new(1.1, 2.2, 3.3).expect("Failed to create point"); + let debug_output = format!("{:?}", p); + + assert!(debug_output.contains("Point")); + assert!(debug_output.contains("1.1")); + assert!(debug_output.contains("2.2")); + assert!(debug_output.contains("3.3")); + } + + #[test] + fn point_rapid_creation_deletion_cycle_stress_test() { + for i in 0..100 { + let p = Point::new(i as f64, i as f64, i as f64) + .expect(&format!("Failed to create point in iteration {}", i)); + assert_eq!(p.coordinates(), (i as f64, i as f64, i as f64)); + p.delete() + .expect(&format!("Failed to delete point in iteration {}", i)); + } + } +} From 33407980b5b5bc1607222ccd54b60a07d823ee9c Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 00:06:32 +0100 Subject: [PATCH 34/40] ci: remove login steps from CI pipeline --- .github/workflows/ffi-unit-test.yml | 14 ++++---------- .github/workflows/occt-unit-test.yml | 24 +++++++++++------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ffi-unit-test.yml b/.github/workflows/ffi-unit-test.yml index 792225a..5f47ef3 100644 --- a/.github/workflows/ffi-unit-test.yml +++ b/.github/workflows/ffi-unit-test.yml @@ -38,17 +38,11 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to GitHub Container Registry - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 + - name: Download OCCT build artifacts + uses: actions/download-artifact@v4 with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Pull OCCT builder image - run: | - docker pull ghcr.io/${{ github.repository }}/occt-builder:latest || echo "OCCT image not found, will build from source" + name: occt-build-artifacts + path: build/ - name: Make script executable run: chmod +x ./ci/scripts/ffi.sh diff --git a/.github/workflows/occt-unit-test.yml b/.github/workflows/occt-unit-test.yml index 20c33e5..9a2a31d 100644 --- a/.github/workflows/occt-unit-test.yml +++ b/.github/workflows/occt-unit-test.yml @@ -1,4 +1,5 @@ name: OCCT CI + on: push: branches: [master, dev-occt**] @@ -33,22 +34,19 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to GitHub Container Registry - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Make script executable run: chmod +x ./ci/scripts/occt.sh - name: Build OCCT and run tests run: ./ci/scripts/occt.sh --unit-testing - - name: Tag and push OCCT image - if: github.event_name != 'pull_request' - run: | - docker tag occt-builder:latest ghcr.io/${{ github.repository }}/occt-builder:latest - docker push ghcr.io/${{ github.repository }}/occt-builder:latest + - name: Upload build artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: occt-build-artifacts + path: | + build/ + **/*.so + **/*.a + retention-days: 7 From 1dfa014e05a0bc63fc465089f4e0253dc3b1664f Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 00:23:15 +0100 Subject: [PATCH 35/40] ci: unify OCCT and FFI in single workflow --- .github/workflows/ffi-unit-test.yml | 61 ---------------------- .github/workflows/occt-ffi-unit-test.yml | 65 ++++++++++++++++++++++++ .github/workflows/occt-unit-test.yml | 52 ------------------- 3 files changed, 65 insertions(+), 113 deletions(-) delete mode 100644 .github/workflows/ffi-unit-test.yml create mode 100644 .github/workflows/occt-ffi-unit-test.yml delete mode 100644 .github/workflows/occt-unit-test.yml diff --git a/.github/workflows/ffi-unit-test.yml b/.github/workflows/ffi-unit-test.yml deleted file mode 100644 index 5f47ef3..0000000 --- a/.github/workflows/ffi-unit-test.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: FFI CI - -on: - workflow_run: - workflows: ["OCCT CI"] - types: - - completed - push: - branches: [master, dev-occt**, dev-ffi**] - paths: - - "src/ffi/**" - - "test/ffi/**" - - "ci/scripts/ffi.sh" - - "ci/ffi.Dockerfile" - - "Cargo.toml" - - "Cargo.lock" - - "build.rs" - pull_request: - branches: [master, dev-occt, dev-ffi] - paths: - - "src/ffi/**" - - "test/ffi/**" - - "ci/scripts/ffi.sh" - - "Cargo.toml" - - "Cargo.lock" - - "build.rs" - -jobs: - build-ffi: - name: Build FFI - runs-on: ubuntu-24.04 - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Download OCCT build artifacts - uses: actions/download-artifact@v4 - with: - name: occt-build-artifacts - path: build/ - - - name: Make script executable - run: chmod +x ./ci/scripts/ffi.sh - - - name: Build FFI and run tests - run: ./ci/scripts/ffi.sh --tests - - - name: Upload test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: ffi-test-results - path: | - target/debug/ - **/test-results/ - retention-days: 7 diff --git a/.github/workflows/occt-ffi-unit-test.yml b/.github/workflows/occt-ffi-unit-test.yml new file mode 100644 index 0000000..acb916c --- /dev/null +++ b/.github/workflows/occt-ffi-unit-test.yml @@ -0,0 +1,65 @@ +name: OCCT FFI Unit Tests + +on: + push: + branches: [master, dev-occt**, dev-ffi**] + paths: + - "src/occt/**" + - "src/ffi/**" + - "tests/occt/**" + - "tests/*.rs" + - "ci/occt.Dockerfile" + - "ci/scripts/occt.sh" + - "ci/ffi.Dockerfile" + - "ci/scripts/ffi.sh" + - ".github/**" + - "./CMakeLists.txt" + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + pull_request: + branches: [master, dev-occt, dev-ffi] + paths: + - "src/occt/**" + - "src/ffi/**" + - "tests/occt/**" + - "tests/*.rs" + - "ci/occt.Dockerfile" + - "ci/scripts/occt.sh" + - "ci/ffi.Dockerfile" + - "ci/scripts/ffi.sh" + - ".github/**" + - "./CMakeLists.txt" + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + +jobs: + build-occt: + name: OCCT + runs-on: ubuntu-24.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Make scripts executable + run: chmod +x ./ci/scripts/occt.sh + + - name: Build OCCT and run tests in Docker + run: ./ci/scripts/occt.sh --unit-testing + + build-ffi: + name: FFI + runs-on: ubuntu-24.04 + needs: build-occt + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Make scripts executable + run: chmod +x ./ci/scripts/ffi.sh + + - name: Build and test FFI in Docker + run: ./ci/scripts/ffi.sh --tests diff --git a/.github/workflows/occt-unit-test.yml b/.github/workflows/occt-unit-test.yml deleted file mode 100644 index 9a2a31d..0000000 --- a/.github/workflows/occt-unit-test.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: OCCT CI - -on: - push: - branches: [master, dev-occt**] - paths: - - "src/occt/**" - - "src/ffi/**" - - "test/occt/**" - - "test/ffi/**" - - "ci/**" - - ".github/**" - - "./CMakeLists.txt" - pull_request: - branches: [master, dev-occt] - paths: - - "src/occt/**" - - "src/ffi/**" - - "test/occt/**" - - "test/ffi/**" - - "ci/**" - - ".github/**" - - "./CMakeLists.txt" - -jobs: - build-wrappers: - name: Build OCCT wrappers - runs-on: ubuntu-24.04 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Make script executable - run: chmod +x ./ci/scripts/occt.sh - - - name: Build OCCT and run tests - run: ./ci/scripts/occt.sh --unit-testing - - - name: Upload build artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: occt-build-artifacts - path: | - build/ - **/*.so - **/*.a - retention-days: 7 From ada549c4d4ad10e25fbc1584a2034aee5b89f8fc Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 10:55:22 +0100 Subject: [PATCH 36/40] test: fix error: 'p' was not declared in this scope --- tests/occt/point-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/occt/point-test.cpp b/tests/occt/point-test.cpp index fe54c25..0c7d7b5 100644 --- a/tests/occt/point-test.cpp +++ b/tests/occt/point-test.cpp @@ -95,7 +95,7 @@ TEST_CASE("Test point creation, query and deletion behaviour", "[point]") { } SECTION("Clear single point") { - point_shape_t* point = make_point(1.0, 2.0, 3.0); + point_shape_t* p = make_point(1.0, 2.0, 3.0); double x, y, z; coord_point(p, &x, &y, &z); From 1230b0a8abdb42ea0257ed9b3edbe70507605a24 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 10:55:53 +0100 Subject: [PATCH 37/40] test: fix typo --- tests/occt/point-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/occt/point-test.cpp b/tests/occt/point-test.cpp index 0c7d7b5..0ded2db 100644 --- a/tests/occt/point-test.cpp +++ b/tests/occt/point-test.cpp @@ -147,7 +147,7 @@ TEST_CASE("Benchmark point operations") { SECTION("make_point performance") { point_shape_t* p = nullptr; BENCHMARK("Create point") { - return p = makes_point(1.0, 2.0, 3.0); + return p = make_point(1.0, 2.0, 3.0); }; delete_point(p); } From 87ade68d73026fddc94d89d98077165b6f04ea74 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 10:57:25 +0100 Subject: [PATCH 38/40] test: fix undefined behaviour --- tests/occt/point-test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/occt/point-test.cpp b/tests/occt/point-test.cpp index 0ded2db..b74b876 100644 --- a/tests/occt/point-test.cpp +++ b/tests/occt/point-test.cpp @@ -163,10 +163,9 @@ TEST_CASE("Benchmark point operations") { } SECTION("delete_point performance") { - point_shape_t* p = make_point(1.0, 2.0, 3.0); BENCHMARK("Delete point") { + point_shape_t* p = make_point(1.0, 2.0, 3.0); delete_point(p); - return p; }; } } From 4a1ff99e0630003b84a3af966b7cf8d4908f5740 Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 13:41:37 +0100 Subject: [PATCH 39/40] ci: improve CI/CD automation pipeline --- .github/workflows/occt-ffi-unit-test.yml | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/occt-ffi-unit-test.yml b/.github/workflows/occt-ffi-unit-test.yml index acb916c..6e1dde8 100644 --- a/.github/workflows/occt-ffi-unit-test.yml +++ b/.github/workflows/occt-ffi-unit-test.yml @@ -43,12 +43,29 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Make scripts executable run: chmod +x ./ci/scripts/occt.sh - name: Build OCCT and run tests in Docker run: ./ci/scripts/occt.sh --unit-testing + - name: Tag and push OCCT image + if: github.event_name != 'pull_request' + run: | + docker tag occt-builder:latest ghcr.io/${{ github.repository }}/occt-builder:latest + docker push ghcr.io/${{ github.repository }}/occt-builder:latest + build-ffi: name: FFI runs-on: ubuntu-24.04 @@ -58,8 +75,29 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Pull OCCT image or build locally + run: | + docker pull ghcr.io/${{ github.repository }}/occt-builder:latest || ./ci/scripts/occt.sh --build-only + - name: Make scripts executable run: chmod +x ./ci/scripts/ffi.sh - name: Build and test FFI in Docker run: ./ci/scripts/ffi.sh --tests + + - name: Tag and push FFI image + if: github.event_name != 'pull_request' + run: | + docker tag ffi-builder:latest ghcr.io/${{ github.repository }}/ffi-builder:latest + docker push ghcr.io/${{ github.repository }}/ffi-builder:latest From 191da020e4a2dd635975238cc5d9b950d508ae5e Mon Sep 17 00:00:00 2001 From: Erick Ahmed Date: Thu, 13 Nov 2025 15:16:50 +0100 Subject: [PATCH 40/40] ci: fix action error with pulling OCCT image --- .github/workflows/occt-ffi-unit-test.yml | 14 +++++++------- README.md | 4 ++-- ci/ffi.Dockerfile | 9 ++++++--- ci/occt.Dockerfile | 7 ++++++- ci/scripts/ffi.sh | 22 ++++++++++++++++++---- ci/scripts/occt.sh | 2 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.github/workflows/occt-ffi-unit-test.yml b/.github/workflows/occt-ffi-unit-test.yml index 6e1dde8..0c1cc61 100644 --- a/.github/workflows/occt-ffi-unit-test.yml +++ b/.github/workflows/occt-ffi-unit-test.yml @@ -63,8 +63,8 @@ jobs: - name: Tag and push OCCT image if: github.event_name != 'pull_request' run: | - docker tag occt-builder:latest ghcr.io/${{ github.repository }}/occt-builder:latest - docker push ghcr.io/${{ github.repository }}/occt-builder:latest + docker tag occt-build:latest ghcr.io/${{ github.repository }}/occt-build:latest + docker push ghcr.io/${{ github.repository }}/occt-build:latest build-ffi: name: FFI @@ -79,16 +79,16 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry - if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Pull OCCT image or build locally + - name: Pull and tag OCCT image run: | - docker pull ghcr.io/${{ github.repository }}/occt-builder:latest || ./ci/scripts/occt.sh --build-only + docker pull ghcr.io/${{ github.repository }}/occt-build:latest + docker tag ghcr.io/${{ github.repository }}/occt-build:latest occt-build:latest - name: Make scripts executable run: chmod +x ./ci/scripts/ffi.sh @@ -99,5 +99,5 @@ jobs: - name: Tag and push FFI image if: github.event_name != 'pull_request' run: | - docker tag ffi-builder:latest ghcr.io/${{ github.repository }}/ffi-builder:latest - docker push ghcr.io/${{ github.repository }}/ffi-builder:latest + docker tag ffi-build:latest ghcr.io/${{ github.repository }}/ffi-build:latest + docker push ghcr.io/${{ github.repository }}/ffi-build:latest diff --git a/README.md b/README.md index 233e465..e4f1ade 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ by the Free Software Foundation, either version 3 of the License, or ## License -This project is licensed under the GNU Affero General Public License v3.0 - see [LICENSE.txt](LICENSE.txt) file for details. +This project is licensed under the [GNU Affero General Public License v3.0](https://spdx.org/licenses/AGPL-3.0-or-later.html) - see [LICENSE.txt](LICENSE.txt) file for details. ## Third-Party Dependencies -This software makes use of facilities provided by **Open CASCADE Technology** (OCCT), which is licensed under the GNU LGPL version 2.1 with the following exception: +This software makes use of facilities provided by **Open CASCADE Technology** (OCCT), which is licensed under the [GNU Lesser General Public License v2.1](https://spdx.org/licenses/LGPL-2.1-only.html) with the following exception: ### Open CASCADE Exception (version 1.0) > The object code (i.e. not a source) form of a "work that uses the Library" diff --git a/ci/ffi.Dockerfile b/ci/ffi.Dockerfile index d49bd7e..9ca14db 100644 --- a/ci/ffi.Dockerfile +++ b/ci/ffi.Dockerfile @@ -1,4 +1,7 @@ -FROM occt-builder:latest AS ffi-base +FROM occt-build:latest AS occt-local +FROM ghcr.io/erickahmed/arc-core/occt-build:latest AS occt-ghcr +FROM occt-local AS occt-selected +FROM occt-selected AS ffi-base ENV DEBIAN_FRONTEND=noninteractive @@ -26,8 +29,8 @@ RUN apt-get update && apt-get install -y \ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" -COPY --from=occt-builder:latest /usr/local /usr/local -COPY --from=occt-builder:latest /app/build /app/occt-build +COPY --from=occt-selected /usr/local /usr/local +COPY --from=occt-selected /app/build /app/occt-build COPY --from=ffi-base /app /app WORKDIR /app diff --git a/ci/occt.Dockerfile b/ci/occt.Dockerfile index 7a6de04..eca18d0 100644 --- a/ci/occt.Dockerfile +++ b/ci/occt.Dockerfile @@ -1,3 +1,8 @@ +# Builds Open CASCADE Technology (OCCT) v7.9.2 +# Licensed under LGPL-2.1-only with OCCT Exception v1.0 +# Source: https://github.com/Open-Cascade-SAS/OCCT/tree/c5f20409c52bf8f658314d205a0e5d6f0be0969c + +# Base builder FROM ubuntu:24.04 AS occt-base ENV DEBIAN_FRONTEND=noninteractive @@ -14,7 +19,7 @@ RUN apt-get update && apt-get install -y \ RUN git clone https://github.com/Open-Cascade-SAS/OCCT.git opencascade WORKDIR /opencascade RUN rm -rf /opencascade/build && mkdir build -RUN git checkout V7_9_2 -b OCCT-792 +RUN git checkout c5f20409c52bf8f658314d205a0e5d6f0be0969c #Tag V7_9_2 RUN mkdir -p build WORKDIR /opencascade/build diff --git a/ci/scripts/ffi.sh b/ci/scripts/ffi.sh index 14a51ab..819db98 100644 --- a/ci/scripts/ffi.sh +++ b/ci/scripts/ffi.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -IMAGE_NAME="ffi-builder:latest" +IMAGE_NAME="ffi-build:latest" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_PATH="$(cd "$SCRIPT_DIR/../../" && pwd)" DOCKERFILE_PATH="$SCRIPT_DIR/../ffi.Dockerfile" @@ -11,13 +11,27 @@ if [[ "${1-}" == "--tests" ]]; then RUN_UNIT_TESTS=true fi +# Try to understand where tf are we +if [[ -n "${GITHUB_REPOSITORY-}" ]]; then + BASE_IMAGE="ghcr.io/$GITHUB_REPOSITORY/occt-build:latest" + echo "Using GHCR image: $BASE_IMAGE" + docker pull "$BASE_IMAGE" || true +else + BASE_IMAGE="occt-build:latest" + echo "Using local image: $BASE_IMAGE" +fi + echo "Building base image..." -docker build --target ffi-base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" +docker build --target ffi-base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" \ + --build-arg BASE_IMAGE="$BASE_IMAGE" \ + "$PROJECT_PATH" if [ "$RUN_UNIT_TESTS" = true ]; then echo "Building unit-testing stage..." - docker build --target ffi-unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH" + docker build --target ffi-unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" \ + --build-arg BASE_IMAGE="$BASE_IMAGE" \ + "$PROJECT_PATH" echo "Running tests..." - docker run --rm -it "$IMAGE_NAME" cargo test + docker run --rm "$IMAGE_NAME" cargo test --verbose fi diff --git a/ci/scripts/occt.sh b/ci/scripts/occt.sh index 80b83bc..2c105dd 100644 --- a/ci/scripts/occt.sh +++ b/ci/scripts/occt.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -IMAGE_NAME="occt-builder:latest" +IMAGE_NAME="occt-build:latest" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_PATH="$(cd "$SCRIPT_DIR/../../" && pwd)" DOCKERFILE_PATH="$SCRIPT_DIR/../occt.Dockerfile"