From 9d421415285222ee0cdbf7bd586a56cc58d7ecaf Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Sat, 16 Mar 2024 19:48:08 -0400 Subject: [PATCH 01/17] feat: Allow empty BU_: nodes --- .github/workflows/pipeline.yml | 2 +- src/dbc.cpp | 2 +- test/test_dbc.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 71f9766..49c3b6e 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -218,6 +218,6 @@ jobs: - name: Make build directory run: cmake -Bbuild -H$GITHUB_WORKSPACE - - name: Run clang-tidy check + - name: Run build run: cmake --build build diff --git a/src/dbc.cpp b/src/dbc.cpp index 360f05c..bfbc177 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -59,7 +59,7 @@ DbcParser::DbcParser() : version_re("^(VERSION)\\s\"(.*)\"") , bit_timing_re("^(BS_:)") , name_space_re("^(NS_)\\s\\:") - , node_re("^(BU_:)\\s((?:[\\w]+?\\s?)*)") + , node_re("^(BU_:)\\s?((?:[\\w]+?\\s?)*)?") , message_re("^(BO_)\\s(\\d+)\\s(\\w+)\\:\\s(\\d+)\\s(\\w+|Vector__XXX)") , value_re("^(VAL_)\\s(\\d+)\\s(\\w+)((?:\\s(\\d+)\\s\"([^\"]*)\")+)\\s;$") , diff --git a/test/test_dbc.cpp b/test/test_dbc.cpp index 5fd2515..19e6b5e 100644 --- a/test/test_dbc.cpp +++ b/test/test_dbc.cpp @@ -239,3 +239,30 @@ VAL_ 123 State1 123 "Description 3" 0 "Description 4" ;)"; REQUIRE(signal2.value_descriptions.at(1).value == 0); REQUIRE(signal2.value_descriptions.at(1).description == "Description 4"); } + +TEST_CASE("Should parse DBC with empty BU_") { + std::string contents = R"(VERSION "" + + +NS_ : + +BS_: + +BU_: + + +BO_ 293 Msg1: 2 Vector__XXX + SG_ Wert7 : 0|16@1- (1,0) [0|0] "" Vector__XXX + +BO_ 292 Msg2: 8 Vector__XXX + SG_ Wert8 : 56|8@1- (1,0) [0|0] "" Vector__XXX +)"; + const auto filename = create_temporary_dbc_with(contents.c_str()); + + auto parser = Libdbc::DbcParser(); + parser.parse_file(filename.c_str()); + + REQUIRE(parser.get_messages().size() == 2); + REQUIRE(parser.get_messages().at(0).name() == "Msg1"); + REQUIRE(parser.get_messages().at(1).name() == "Msg2"); +} From 58669c99551f5be1831a1157b1bd562bf5d4932c Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 19 Mar 2024 16:22:51 -0400 Subject: [PATCH 02/17] Exception error cleanup (#24) * feat(error): Adding non dbc file format error (extension based), Missing version header error added Fixed the tests so that they now make .dbc files since we are checking the extensions. * feat(error): Adding a missing bit timing error class * feat(error): Adding lines we attempted to parse and more error messaging for the Exceptions * test: Updating tests names to be more clear at what is being validated. * feat(parsing): Adding a way to push and read all of the unused lines since we don't error out on unused * fix(format): Running code format * fix: Fixing clang-tidy warnings --- include/libdbc/dbc.hpp | 6 +++ include/libdbc/exceptions/error.hpp | 44 ++++++++++++++++++ src/dbc.cpp | 32 ++++++++++--- test/dbcs/MissingVersion.dbc | 72 ++++++++++++++--------------- test/test_dbc.cpp | 59 +++++++++++++++++++---- test/testing_utils/common.cpp | 2 +- 6 files changed, 164 insertions(+), 51 deletions(-) diff --git a/include/libdbc/dbc.hpp b/include/libdbc/dbc.hpp index 68e9789..2dfe7bf 100644 --- a/include/libdbc/dbc.hpp +++ b/include/libdbc/dbc.hpp @@ -31,6 +31,8 @@ class DbcParser : public Parser { Message::ParseSignalsStatus parse_message(uint32_t message_id, const std::vector& data, std::vector& out_values); + std::vector unused_lines() const; + private: std::string version; std::vector nodes; @@ -44,9 +46,13 @@ class DbcParser : public Parser { std::regex value_re; std::regex signal_re; + std::vector missed_lines; + void parse_dbc_header(std::istream& file_stream); void parse_dbc_nodes(std::istream& file_stream); void parse_dbc_messages(const std::vector& lines); + + static std::string get_extension(const std::string& file_name); }; } diff --git a/include/libdbc/exceptions/error.hpp b/include/libdbc/exceptions/error.hpp index 27e2fa4..03fd5a0 100644 --- a/include/libdbc/exceptions/error.hpp +++ b/include/libdbc/exceptions/error.hpp @@ -2,6 +2,7 @@ #define ERROR_HPP #include +#include namespace Libdbc { @@ -19,6 +20,49 @@ class ValidityError : public Exception { } }; +class NonDbcFileFormatError : public ValidityError { +public: + NonDbcFileFormatError(const std::string& path, const std::string& extension) { + error_msg = {"File is not of DBC format. Expected a .dbc extension. Cannot read this type of file (" + path + "). Found the extension (" + extension + + ")."}; + } + + const char* what() const throw() override { + return error_msg.c_str(); + } + +private: + std::string error_msg; +}; + +class DbcFileIsMissingVersion : public ValidityError { +public: + DbcFileIsMissingVersion(const std::string& line) { + error_msg = {"Invalid dbc file. Missing the required version header. Attempting to read line: (" + line + ")."}; + } + + const char* what() const throw() override { + return error_msg.c_str(); + } + +private: + std::string error_msg; +}; + +class DbcFileIsMissingBitTiming : public ValidityError { +public: + DbcFileIsMissingBitTiming(const std::string& line) { + error_msg = {"Invalid dbc file. Missing required bit timing in the header. Attempting to read line: (" + line + ")."}; + } + + const char* what() const throw() override { + return error_msg.c_str(); + } + +private: + std::string error_msg; +}; + } // libdbc #endif // ERROR_HPP diff --git a/src/dbc.cpp b/src/dbc.cpp index bfbc177..108ab7e 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -76,8 +77,12 @@ void DbcParser::parse_file(const std::string& file) { messages.clear(); - parse_dbc_header(stream); + auto extension = get_extension(file); + if (extension != ".dbc") { + throw NonDbcFileFormatError(file, extension); + } + parse_dbc_header(stream); parse_dbc_nodes(stream); while (!stream.eof()) { @@ -88,6 +93,15 @@ void DbcParser::parse_file(const std::string& file) { parse_dbc_messages(lines); } +std::string DbcParser::get_extension(const std::string& file_name) { + std::size_t dot = file_name.find_last_of("."); + if (dot != std::string::npos) { + return file_name.substr(dot, file_name.size() - dot); + } + + return ""; +} + std::string DbcParser::get_version() const { return version; } @@ -116,7 +130,7 @@ void DbcParser::parse_dbc_header(std::istream& file_stream) { Utils::StreamHandler::get_line(file_stream, line); if (!std::regex_search(line, match, version_re)) { - throw ValidityError(); + throw DbcFileIsMissingVersion(line); } version = match.str(2); @@ -126,7 +140,7 @@ void DbcParser::parse_dbc_header(std::istream& file_stream) { Utils::StreamHandler::get_next_non_blank_line(file_stream, line); if (!std::regex_search(line, match, bit_timing_re)) { - throw ValidityError(); + throw DbcFileIsMissingBitTiming(line); } } @@ -136,9 +150,7 @@ void DbcParser::parse_dbc_nodes(std::istream& file_stream) { Utils::StreamHandler::get_next_non_blank_line(file_stream, line); - if (!std::regex_search(line, match, node_re)) { - throw ValidityError(); - } + std::regex_search(line, match, node_re); if (match.length() > 2) { std::string node = match.str(2); @@ -213,6 +225,10 @@ void DbcParser::parse_dbc_messages(const std::vector& lines) { signal_value.push_back(val); continue; } + + if (line.length() > 0) { + missed_lines.push_back(line); + } } for (const auto& signal : signal_value) { @@ -225,4 +241,8 @@ void DbcParser::parse_dbc_messages(const std::vector& lines) { } } +std::vector DbcParser::unused_lines() const { + return missed_lines; +} + } diff --git a/test/dbcs/MissingVersion.dbc b/test/dbcs/MissingVersion.dbc index 0db087a..40d5967 100644 --- a/test/dbcs/MissingVersion.dbc +++ b/test/dbcs/MissingVersion.dbc @@ -1,36 +1,36 @@ -NS_ : - BA_ - BA_DEF_ - BA_DEF_DEF_ - BA_DEF_DEF_REL_ - BA_DEF_REL_ - BA_DEF_SGTYPE_ - BA_REL_ - BA_SGTYPE_ - BO_TX_BU_ - BU_BO_REL_ - BU_EV_REL_ - BU_SG_REL_ - CAT_ - CAT_DEF_ - CM_ - ENVVAR_DATA_ - EV_DATA_ - FILTER - NS_DESC_ - SGTYPE_ - SGTYPE_VAL_ - SG_MUL_VAL_ - SIGTYPE_VALTYPE_ - SIG_GROUP_ - SIG_TYPE_REF_ - SIG_VALTYPE_ - VAL_ - VAL_TABLE_ - -BS_: - -BU_: DBG DRIVER IO MOTOR SENSOR - -BO_ 500 IO_DEBUG: 4 IO - SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG \ No newline at end of file +NS_ : + BA_ + BA_DEF_ + BA_DEF_DEF_ + BA_DEF_DEF_REL_ + BA_DEF_REL_ + BA_DEF_SGTYPE_ + BA_REL_ + BA_SGTYPE_ + BO_TX_BU_ + BU_BO_REL_ + BU_EV_REL_ + BU_SG_REL_ + CAT_ + CAT_DEF_ + CM_ + ENVVAR_DATA_ + EV_DATA_ + FILTER + NS_DESC_ + SGTYPE_ + SGTYPE_VAL_ + SG_MUL_VAL_ + SIGTYPE_VALTYPE_ + SIG_GROUP_ + SIG_TYPE_REF_ + SIG_VALTYPE_ + VAL_ + VAL_TABLE_ + +BS_: + +BU_: DBG DRIVER IO MOTOR SENSOR + +BO_ 500 IO_DEBUG: 4 IO + SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG diff --git a/test/test_dbc.cpp b/test/test_dbc.cpp index 19e6b5e..8ae1b54 100644 --- a/test/test_dbc.cpp +++ b/test/test_dbc.cpp @@ -3,29 +3,35 @@ #include #include #include +#include #include #include #include +using Catch::Matchers::ContainsSubstring; + TEST_CASE("Testing dbc file loading error issues", "[fileio][error]") { auto parser = std::unique_ptr(new Libdbc::DbcParser()); SECTION("Loading a non dbc file should throw an error", "[error]") { - REQUIRE_THROWS_AS(parser->parse_file(TEXT_FILE), Libdbc::ValidityError); + REQUIRE_THROWS_AS(parser->parse_file(TEXT_FILE), Libdbc::NonDbcFileFormatError); + REQUIRE_THROWS_WITH(parser->parse_file(TEXT_FILE), ContainsSubstring("TextFile.txt")); } - SECTION("Loading a dbc with bad headers throws an error", "[error]") { - REQUIRE_THROWS_AS(parser->parse_file(MISSING_VERSION_DBC_FILE), Libdbc::ValidityError); + SECTION("Loading a dbc with missing version header throws an error (VERSION)", "[error]") { + REQUIRE_THROWS_AS(parser->parse_file(MISSING_VERSION_DBC_FILE), Libdbc::DbcFileIsMissingVersion); + REQUIRE_THROWS_WITH(parser->parse_file(MISSING_VERSION_DBC_FILE), ContainsSubstring("line: (NS_ :)")); } SECTION("Loading a dbc without the required bit timing section (BS_:)", "[error]") { - REQUIRE_THROWS_AS(parser->parse_file(MISSING_BIT_TIMING_DBC_FILE), Libdbc::ValidityError); + REQUIRE_THROWS_AS(parser->parse_file(MISSING_BIT_TIMING_DBC_FILE), Libdbc::DbcFileIsMissingBitTiming); + REQUIRE_THROWS_WITH(parser->parse_file(MISSING_BIT_TIMING_DBC_FILE), ContainsSubstring("BU_: DBG DRIVER IO MOTOR SENSOR")); } SECTION("Loading a dbc with some missing namespace section tags (NS_ :)", "[error]") { // Confusion about this type of error. it appears that the header isn't // very well standardized for now we ignore this type of error. - CHECK_NOTHROW(parser->parse_file(MISSING_NEW_SYMBOLS_DBC_FILE)); + REQUIRE_NOTHROW(parser->parse_file(MISSING_NEW_SYMBOLS_DBC_FILE)); } SECTION("Verify that what() method is accessible for all exceptions", "[error]") { @@ -240,7 +246,7 @@ VAL_ 123 State1 123 "Description 3" 0 "Description 4" ;)"; REQUIRE(signal2.value_descriptions.at(1).description == "Description 4"); } -TEST_CASE("Should parse DBC with empty BU_") { +TEST_CASE("Should parse DBC with empty BU_", "[error][optional]") { std::string contents = R"(VERSION "" @@ -254,15 +260,52 @@ NS_ : BO_ 293 Msg1: 2 Vector__XXX SG_ Wert7 : 0|16@1- (1,0) [0|0] "" Vector__XXX -BO_ 292 Msg2: 8 Vector__XXX +BO_ 292 Msg2: 1 Vector__XXX SG_ Wert8 : 56|8@1- (1,0) [0|0] "" Vector__XXX )"; const auto filename = create_temporary_dbc_with(contents.c_str()); auto parser = Libdbc::DbcParser(); - parser.parse_file(filename.c_str()); + REQUIRE_NOTHROW(parser.parse_file(filename.c_str())); REQUIRE(parser.get_messages().size() == 2); REQUIRE(parser.get_messages().at(0).name() == "Msg1"); REQUIRE(parser.get_messages().at(1).name() == "Msg2"); } + +TEST_CASE("Should report unused lines since we don't have tracing.", "[parsing]") { + std::string contents = R"(VERSION "" + +NS_ : + +BS_: + +BU_: + + +BO_ 293 Msg1: 2 Vector__XXX + SG_ Whitespace: | 0|16@1- (1,0) [0|0] "" Vector__XXX + SG_ Wert7 : 0|16@1- (1,0) [0|0] "" Vector__XXX + SG_ Wert8 : 0|16@1- (1,0) [0|0] "" Vector__XXX + +BO_ 292 Msg2: 1 Vector__XXX + SG_ Wert8 : 56|8@1- (1,0) [0|0] "" Vector__XXX + SB_ not a correct line + +BO_ have a issue here: +)"; + + const auto filename = create_temporary_dbc_with(contents.c_str()); + + auto parser = Libdbc::DbcParser(); + REQUIRE_NOTHROW(parser.parse_file(filename.c_str())); + + REQUIRE(parser.get_messages().size() == 2); + REQUIRE(parser.get_messages()[0].size() == 2); + REQUIRE(parser.get_messages()[1].size() == 1); + + auto unused = parser.unused_lines(); + + // We could match them all here but i think just a check that the size is sufficent. + REQUIRE(unused.size() == 3); +} diff --git a/test/testing_utils/common.cpp b/test/testing_utils/common.cpp index e058054..4c3ef54 100644 --- a/test/testing_utils/common.cpp +++ b/test/testing_utils/common.cpp @@ -23,7 +23,7 @@ std::string generate_unique_filename() { int random_num = dis(gen); // Concatenate time and random number to create a unique filename - return "temp_file_" + std::to_string(milliseconds) + "_" + std::to_string(random_num) + ".txt"; + return "temp_file_" + std::to_string(milliseconds) + "_" + std::to_string(random_num) + ".dbc"; } std::string create_temporary_dbc_with(const char* contents) { From cc4e80c7b6e1f7a13cc47b45f226756f823888e2 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 19 Mar 2024 16:35:07 -0400 Subject: [PATCH 03/17] feat: Bump to version 0.4.0 for release --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d4fb58..cbf2d37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) # Keep this on one line for release checking -project(dbc VERSION 0.3.0 DESCRIPTION "C++ DBC Parser") +project(dbc VERSION 0.4.0 DESCRIPTION "C++ DBC Parser") # -- PROJECT OPTIONS -- # option(DBC_ENABLE_TESTS "Enable Unittests" ON) From 57da1f7aa29c09828a9835c02f4d9f8f69dcd3ad Mon Sep 17 00:00:00 2001 From: Stefan Gerlach Date: Fri, 10 May 2024 00:08:14 +0200 Subject: [PATCH 04/17] Update CMakeLists.txt (#26) Fix CMake error "install DIRECTORY given no DESTINATION!" --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbf2d37..ea27c35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ if(DBC_GENERATE_SINGLE_HEADER) endif() ## Installation +include(GNUInstallDirs) # install lib install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}) From f08e688e6162fa227f0367d591ce9e15d99e7ee4 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 21 May 2024 17:32:38 -0400 Subject: [PATCH 05/17] fix: Set the ubuntu runner to 22.04 to keep the same compiler version Probably should setup the job to setup the gcc version instead but for now this works. --- .github/workflows/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 49c3b6e..bb94b75 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -9,7 +9,7 @@ env: jobs: linux-builds: name: linux ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: From eea0184f4d919d364bec5b9b27e9d42f6121ec9c Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 21 May 2024 17:40:13 -0400 Subject: [PATCH 06/17] fix(CI): Bump gcc to 14, add debug print, and set the mac os to 12 --- .github/workflows/pipeline.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index bb94b75..29e1951 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -14,13 +14,13 @@ jobs: fail-fast: false matrix: cxx: - - g++-13 + - g++-14 - clang++-16 build_type: [Debug, Release] std: [11] include: - - cxx: g++-13 - cc: gcc-13 + - cxx: g++-14 + cc: gcc-14 - cxx: clang++-16 cc: clang-16 llvm_version: 16 @@ -48,6 +48,11 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source "$HOME/.cargo/env" + - name: Print gcc version + run: | + which gcc + gcc --version + - name: Configure build env: CC: ${{matrix.cc}} @@ -114,7 +119,7 @@ jobs: macos-builds: name: macos ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} - runs-on: macos-latest + runs-on: macos-12 strategy: fail-fast: false matrix: From c79a284ccf68947818130f467c50d02716244b24 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 21 May 2024 17:41:56 -0400 Subject: [PATCH 07/17] fix(CI): Change gcc to 11 which is default installed on ubuntu 22 --- .github/workflows/pipeline.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 29e1951..cc0cf9e 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -14,13 +14,13 @@ jobs: fail-fast: false matrix: cxx: - - g++-14 + - g++-11 - clang++-16 build_type: [Debug, Release] std: [11] include: - - cxx: g++-14 - cc: gcc-14 + - cxx: g++-11 + cc: gcc-11 - cxx: clang++-16 cc: clang-16 llvm_version: 16 @@ -49,9 +49,7 @@ jobs: source "$HOME/.cargo/env" - name: Print gcc version - run: | - which gcc - gcc --version + run: gcc --version - name: Configure build env: From a58f04c0448ba71d9b725fb3a2119df0f35d425b Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 21 May 2024 17:49:33 -0400 Subject: [PATCH 08/17] feat(CI): Run cmake install for all OS --- .github/workflows/pipeline.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index cc0cf9e..881138c 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -73,6 +73,10 @@ jobs: CTEST_OUTPUT_ON_FAILURE: 1 run: ctest --output-on-failure --test-dir build -j `nproc` + - name: Run install + run: cmake --install . + + windows-build: name: ${{matrix.os}}, ${{matrix.std}}, ${{matrix.build_type}}, ${{matrix.platform}} runs-on: ${{matrix.os}} @@ -115,6 +119,9 @@ jobs: run: ctest --output-on-failure --test-dir build -j %NUMBER_OF_PROCESSORS% shell: cmd + - name: Run install + run: cmake --install . + macos-builds: name: macos ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} runs-on: macos-12 @@ -162,6 +169,9 @@ jobs: working-directory: ${{runner.workspace}} run: ctest --output-on-failure --test-dir build -j `sysctl -n hw.ncpu` -V + - name: Run install + run: cmake --install . + format-check: runs-on: ubuntu-latest From 2e974178f598e615f65a6ad1a00f7d926823deb2 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 21 May 2024 17:53:50 -0400 Subject: [PATCH 09/17] fix(CI): fix the cmake build path for the install command --- .github/workflows/pipeline.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 881138c..15f21e4 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -74,7 +74,7 @@ jobs: run: ctest --output-on-failure --test-dir build -j `nproc` - name: Run install - run: cmake --install . + run: cmake --install build windows-build: @@ -120,7 +120,7 @@ jobs: shell: cmd - name: Run install - run: cmake --install . + run: cmake --install build macos-builds: name: macos ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} @@ -170,7 +170,7 @@ jobs: run: ctest --output-on-failure --test-dir build -j `sysctl -n hw.ncpu` -V - name: Run install - run: cmake --install . + run: cmake --install build format-check: runs-on: ubuntu-latest From 6d6288550fb0226c8b9c65a69c6421b25f4ab42f Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Tue, 21 May 2024 17:58:04 -0400 Subject: [PATCH 10/17] fix(CI): In the pipeline we only can install on linux and need to pass the prefix to avoid permission issues --- .github/workflows/pipeline.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 15f21e4..f36b26d 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -74,7 +74,7 @@ jobs: run: ctest --output-on-failure --test-dir build -j `nproc` - name: Run install - run: cmake --install build + run: cmake --install build --prefix . windows-build: @@ -119,9 +119,6 @@ jobs: run: ctest --output-on-failure --test-dir build -j %NUMBER_OF_PROCESSORS% shell: cmd - - name: Run install - run: cmake --install build - macos-builds: name: macos ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} runs-on: macos-12 @@ -169,9 +166,6 @@ jobs: working-directory: ${{runner.workspace}} run: ctest --output-on-failure --test-dir build -j `sysctl -n hw.ncpu` -V - - name: Run install - run: cmake --install build - format-check: runs-on: ubuntu-latest From 0dbe4541e99b81049ef7109d1670e37a67b41a8a Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Sun, 26 May 2024 16:38:42 -0400 Subject: [PATCH 11/17] feat: Parse dbc with istream (#30) * feat: Add an istream interface to the avoid the ifstream creation given a file name * fix: formatting the code base to fix checks and clang-tidy errors --- include/libdbc/dbc.hpp | 4 +++- src/dbc.cpp | 19 +++++++++------ .../test_single_header.cpp | 24 +++++++++++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/include/libdbc/dbc.hpp b/include/libdbc/dbc.hpp index 2dfe7bf..5e0e9e4 100644 --- a/include/libdbc/dbc.hpp +++ b/include/libdbc/dbc.hpp @@ -15,6 +15,7 @@ class Parser { virtual ~Parser() = default; virtual void parse_file(const std::string& file) = 0; + virtual void parse_file(std::istream& file) = 0; protected: }; @@ -23,7 +24,8 @@ class DbcParser : public Parser { public: DbcParser(); - void parse_file(const std::string& file) override; + void parse_file(const std::string& file_name) override; + void parse_file(std::istream& stream) override; std::string get_version() const; std::vector get_nodes() const; diff --git a/src/dbc.cpp b/src/dbc.cpp index 108ab7e..c6baae5 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -70,18 +70,12 @@ DbcParser::DbcParser() + whiteSpace + receiverPattern) { } -void DbcParser::parse_file(const std::string& file) { - std::ifstream stream(file.c_str()); +void DbcParser::parse_file(std::istream& stream) { std::string line; std::vector lines; messages.clear(); - auto extension = get_extension(file); - if (extension != ".dbc") { - throw NonDbcFileFormatError(file, extension); - } - parse_dbc_header(stream); parse_dbc_nodes(stream); @@ -93,6 +87,17 @@ void DbcParser::parse_file(const std::string& file) { parse_dbc_messages(lines); } +void DbcParser::parse_file(const std::string& file_name) { + auto extension = get_extension(file_name); + if (extension != ".dbc") { + throw NonDbcFileFormatError(file_name, extension); + } + + std::ifstream stream(file_name.c_str()); + + parse_file(stream); +} + std::string DbcParser::get_extension(const std::string& file_name) { std::size_t dot = file_name.find_last_of("."); if (dot != std::string::npos) { diff --git a/test/single_header_testing/test_single_header.cpp b/test/single_header_testing/test_single_header.cpp index e4abc6c..c512d35 100644 --- a/test/single_header_testing/test_single_header.cpp +++ b/test/single_header_testing/test_single_header.cpp @@ -54,3 +54,27 @@ TEST_CASE("Testing big endian, little endian") { REQUIRE(signal.is_bigendian == false); } } + +TEST_CASE("Testing file stream mirrors the filename interface") { + std::string dbc_contents = PRIMITIVE_DBC + R"(BO_ 234 MSG1: 8 Vector__XXX + SG_ Sig1 : 55|16@0- (0.1,0) [-3276.8|-3276.7] "C" Vector__XXX + SG_ Sig2 : 39|16@1- (0.1,0) [-3276.8|-3276.7] "C" Vector__XXX)"; + const auto filename = create_temporary_dbc_with(dbc_contents.c_str()); + std::ifstream file(filename.c_str()); + + auto parser = Libdbc::DbcParser(); + parser.parse_file(file); + + REQUIRE(parser.get_messages().size() == 1); + REQUIRE(parser.get_messages().at(0).name() == "MSG1"); + REQUIRE(parser.get_messages().at(0).size() == 8); + REQUIRE(parser.get_messages().at(0).get_signals().size() == 2); + { + const auto signal = parser.get_messages().at(0).get_signals().at(0); + REQUIRE(signal.is_bigendian == true); + } + { + const auto signal = parser.get_messages().at(0).get_signals().at(1); + REQUIRE(signal.is_bigendian == false); + } +} From 1bfead321ec4f10a5dd371c8d05769da6a0a78a2 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Sun, 26 May 2024 16:45:59 -0400 Subject: [PATCH 12/17] fix: Crashed when BO_ and the message content were on different lines --- src/dbc.cpp | 4 ++-- test/test_parse_message.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/dbc.cpp b/src/dbc.cpp index 108ab7e..f19fe25 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -176,7 +176,7 @@ void DbcParser::parse_dbc_messages(const std::vector& lines) { continue; } - if (std::regex_search(line, match, signal_re)) { + if (std::regex_search(line, match, signal_re) && messages.size() > 0) { std::string name = match.str(SIGNAL_NAME_GROUP); bool is_multiplexed = false; // No support yet uint32_t start_bit = static_cast(std::stoul(match.str(SIGNAL_START_BIT_GROUP))); @@ -199,7 +199,7 @@ void DbcParser::parse_dbc_messages(const std::vector& lines) { continue; } - if (std::regex_search(line, match, value_re)) { + if (std::regex_search(line, match, value_re) && messages.size() > 0) { uint32_t message_id = static_cast(std::stoul(match.str(2))); std::string signal_name = match.str(3); diff --git a/test/test_parse_message.cpp b/test/test_parse_message.cpp index aad3b1c..08d68f3 100644 --- a/test/test_parse_message.cpp +++ b/test/test_parse_message.cpp @@ -173,3 +173,39 @@ TEST_CASE("Parse Message data length < 8 unsigned") { REQUIRE(Catch::Approx(result_values.at(0)) == 0x1); REQUIRE(Catch::Approx(result_values.at(1)) == 0x2); } + +TEST_CASE("Parse message with BO_ on single line should fail.") { + std::string dbc_contents = PRIMITIVE_DBC + R"(BO_ +234 MSG1: 8 Vector__XXX + SG_ State1 : 0|8@1+ (1,0) [0|200] "Km/h" DEVICE1,DEVICE2,DEVICE3 + SG_ State2 : 0|8@1+ (1,0) [0|204] "" DEVICE1,DEVICE2,DEVICE3 +VAL_ 234 State1 123 "Description 1" 0 "Description 2" 90903489 "Big value and special characters &$§())!")"; + const auto filename = create_temporary_dbc_with(dbc_contents.c_str()); + + Libdbc::DbcParser p; + p.parse_file(filename); + + std::vector data{0x1, 0x2}; + std::vector result_values; + REQUIRE(p.get_messages().size() == 0); + REQUIRE(p.parse_message(234, data, result_values) == Libdbc::Message::ParseSignalsStatus::ErrorUnknownID); +} + +TEST_CASE("Parse signal with SG_ on single line should fail.") { + std::string dbc_contents = PRIMITIVE_DBC + R"(BO_ 234 MSG1: 8 Vector__XXX + SG_ +State1 : 0|8@1+ (1,0) [0|200] "Km/h" DEVICE1,DEVICE2,DEVICE3 + SG_ State2 : 0|8@1+ (1,0) [0|204] "" DEVICE1,DEVICE2,DEVICE3 +VAL_ 234 State1 123 "Description 1" 0 "Description 2" 90903489 "Big value and special characters &$§())!")"; + const auto filename = create_temporary_dbc_with(dbc_contents.c_str()); + + Libdbc::DbcParser p; + p.parse_file(filename); + + std::vector data{0x1, 0x2}; + std::vector result_values; + REQUIRE(p.get_messages().size() == 1); + REQUIRE(p.parse_message(234, data, result_values) == Libdbc::Message::ParseSignalsStatus::Success); + REQUIRE(result_values.size() == 1); + REQUIRE(Catch::Approx(result_values.at(0)) == 0x1); +} From ceeef752101dccfa045106592f17dedaf64c9656 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Thu, 27 Jun 2024 18:17:25 -0400 Subject: [PATCH 13/17] fix(checks): Fixing clang warnings about size comparison on the vector. Should have been just a empty check. --- src/dbc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dbc.cpp b/src/dbc.cpp index 2d999da..89e8c24 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -181,7 +181,7 @@ void DbcParser::parse_dbc_messages(const std::vector& lines) { continue; } - if (std::regex_search(line, match, signal_re) && messages.size() > 0) { + if (std::regex_search(line, match, signal_re) && !messages.empty()) { std::string name = match.str(SIGNAL_NAME_GROUP); bool is_multiplexed = false; // No support yet uint32_t start_bit = static_cast(std::stoul(match.str(SIGNAL_START_BIT_GROUP))); @@ -204,7 +204,7 @@ void DbcParser::parse_dbc_messages(const std::vector& lines) { continue; } - if (std::regex_search(line, match, value_re) && messages.size() > 0) { + if (std::regex_search(line, match, value_re) && !messages.empty()) { uint32_t message_id = static_cast(std::stoul(match.str(2))); std::string signal_name = match.str(3); From 887d779fbf416032f539675f74360a5a43a93f86 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Thu, 27 Jun 2024 18:19:17 -0400 Subject: [PATCH 14/17] chore(version): Version bump to v0.5.0 --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea27c35..551af92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) # Keep this on one line for release checking -project(dbc VERSION 0.4.0 DESCRIPTION "C++ DBC Parser") +project(dbc VERSION 0.5.0 DESCRIPTION "C++ DBC Parser") # -- PROJECT OPTIONS -- # option(DBC_ENABLE_TESTS "Enable Unittests" ON) @@ -95,7 +95,6 @@ if(DBC_GENERATE_SINGLE_HEADER) endif() ## Installation -include(GNUInstallDirs) # install lib install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}) From 17b3e3dd121f3dc13f07a3cc0d9a19ce7b138ece Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Wed, 10 Sep 2025 21:52:33 -0400 Subject: [PATCH 15/17] Add manual workflow dispatch to CI pipeline --- .github/workflows/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index f36b26d..0947cd6 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -1,6 +1,6 @@ name: Libdbc Pipeline -on: [push, workflow_call] +on: [push, workflow_call, workflow_dispatch] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) From 316686831a1a064fedebdd77f8fd4e6d9b183db3 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Wed, 10 Sep 2025 21:54:48 -0400 Subject: [PATCH 16/17] Enable pull_request to trigger CI pipeline --- .github/workflows/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 0947cd6..d24e09a 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -1,6 +1,6 @@ name: Libdbc Pipeline -on: [push, workflow_call, workflow_dispatch] +on: [push, pull_request, workflow_call, workflow_dispatch] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) From 2c8acdbd2d1ea33545f99f9a0d23e10b7f8e0852 Mon Sep 17 00:00:00 2001 From: Devon Adair Date: Fri, 12 Sep 2025 10:29:36 -0400 Subject: [PATCH 17/17] Update windows runner to 2025 and mac runners to 15 (#35) --- .github/workflows/pipeline.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index d24e09a..3f96f40 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -83,7 +83,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-2019, windows-2022] + os: [windows-2025, windows-2022] platform: [Win32, x64] build_type: [Debug, Release] std: [11] @@ -120,11 +120,12 @@ jobs: shell: cmd macos-builds: - name: macos ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} - runs-on: macos-12 + name: ${{matrix.os}}, ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}} + runs-on: ${{matrix.os}} strategy: fail-fast: false matrix: + os: [macos-15-large] cxx: - g++ - clang++