diff --git a/3rd/LuaBridge/detail/LuaException.h b/3rd/LuaBridge/detail/LuaException.h index 836cb852..2fe6d608 100644 --- a/3rd/LuaBridge/detail/LuaException.h +++ b/3rd/LuaBridge/detail/LuaException.h @@ -97,8 +97,10 @@ public: { int code = lua_pcall (L, nargs, nresults, msgh); - if (code != LUABRIDGE_LUA_OK) - Throw (LuaException (L, code)); + if (code != LUABRIDGE_LUA_OK) { + // Throw (LuaException (L, code)); + assert(true); + } } //---------------------------------------------------------------------------- @@ -128,7 +130,8 @@ protected: private: static int throwAtPanic (lua_State* L) { - throw LuaException (L, -1); + // throw LuaException (L, -1); + return -1; } }; diff --git a/3rd/LuaBridge/detail/Namespace.h b/3rd/LuaBridge/detail/Namespace.h index 68ae21fa..8a4dd20a 100644 --- a/3rd/LuaBridge/detail/Namespace.h +++ b/3rd/LuaBridge/detail/Namespace.h @@ -101,7 +101,8 @@ protected: { if (m_stackSize == 0) { - throw std::logic_error ("Unable to continue registration"); + std::cerr << ("Unable to continue registration"); + assert(true); } } }; @@ -1054,7 +1055,8 @@ public: { if (m_stackSize == 1) { - throw std::logic_error ("endNamespace () called on global namespace"); + std::cerr << ("endNamespace () called on global namespace"); + assert(true); } assert (m_stackSize > 1); @@ -1150,7 +1152,8 @@ public: { if (m_stackSize == 1) { - throw std::logic_error ("addProperty () called on global namespace"); + std::cerr << ("addProperty () called on global namespace"); + assert(true); } assert (lua_istable (L, -1)); // Stack: namespace table (ns) diff --git a/3rd/LuaBridge/detail/Userdata.h b/3rd/LuaBridge/detail/Userdata.h index ef13d9fc..3993e176 100644 --- a/3rd/LuaBridge/detail/Userdata.h +++ b/3rd/LuaBridge/detail/Userdata.h @@ -33,6 +33,7 @@ #include #include +#include namespace luabridge { @@ -320,7 +321,8 @@ public: lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); if (!lua_istable (L, -1)) { - throw std::logic_error ("The class is not registered in LuaBridge"); + std::cerr << ("The class is not registered in LuaBridge"); + assert(true); } lua_setmetatable (L, -2); return ud; @@ -375,7 +377,8 @@ private: lua_rawgetp (L, LUA_REGISTRYINDEX, key); if (!lua_istable (L, -1)) { - throw std::logic_error ("The class is not registered in LuaBridge"); + std::cerr << ("The class is not registered in LuaBridge"); + assert(true); } lua_setmetatable (L, -2); } diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e4740bd..de404754 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ endif() project(PIP) set(PIP_MAJOR 5) set(PIP_MINOR 6) -set(PIP_REVISION 0) +set(PIP_REVISION 1) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) @@ -67,11 +67,13 @@ set(PIP_DLL_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE STRING "") option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT}) option(STD_IOSTREAM "Building with std iostream operators support" OFF) option(INTROSPECTION "Build with introspection" OFF) -option(TESTS "Build tests and perform their before install step" OFF) +option(TESTS "Build tests" OFF) +option(TESTS_RUN "Run tests before install step" OFF) option(COVERAGE "Build project with coverage info" OFF) option(PIP_FFTW_F "Support fftw module for float" ON) option(PIP_FFTW_L "Support fftw module for long double" ON) option(PIP_FFTW_Q "Support fftw module for quad double" OFF) +option(PIP_MANUAL_TEST "Build dev test (main.cpp)" OFF) set(PIP_UTILS 1) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD 11) @@ -360,10 +362,10 @@ endif() if(WIN32) add_definitions(-DPSAPI_VERSION=1) if(${C_COMPILER} STREQUAL "cl.exe") - set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0") + set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0 /EH-") endif() else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fno-exceptions") if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32") endif() @@ -592,12 +594,14 @@ if (NOT CROSSTOOLS) #target_link_libraries(pip_plugin pip) if (NOT DEFINED ANDROID_PLATFORM) - if(microhttpd_FOUND AND curl_FOUND) - add_executable(pip_test "main.cpp") - target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) - if(sodium_FOUND) - add_executable(pip_cloud_test "main_picloud_test.cpp") - target_link_libraries(pip_cloud_test pip_cloud) + if (PIP_MANUAL_TEST) + if(microhttpd_FOUND AND curl_FOUND) + add_executable(pip_test "main.cpp") + target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) + if(sodium_FOUND) + add_executable(pip_cloud_test "main_picloud_test.cpp") + target_link_libraries(pip_cloud_test pip_cloud) + endif() endif() endif() endif() @@ -823,6 +827,9 @@ if (PIP_TESTS_LIST) foreach(_test ${PIP_TESTS_LIST}) message(" * ${_test}") endforeach() + if (TESTS_RUN) + message("TESTS_RUN ON -> Run tests before install step") + endif() else() message(" Tests: skip (tests off)") endif() diff --git a/libs/client_server/piclientserver_server.cpp b/libs/client_server/piclientserver_server.cpp index f1700b01..edfd2083 100644 --- a/libs/client_server/piclientserver_server.cpp +++ b/libs/client_server/piclientserver_server.cpp @@ -72,11 +72,7 @@ PIClientServer::Server::~Server() { clean_thread->waitForFinish(); piDeleteSafety(clean_thread); stopServer(); - for (auto c: clients) { - c->aboutDelete(); - c->destroy(); - delete c; - } + closeAll(); piDeleteSafety(tcp_server); } diff --git a/libs/main/core/pibase.h b/libs/main/core/pibase.h index 8f6223c8..b379dea4 100644 --- a/libs/main/core/pibase.h +++ b/libs/main/core/pibase.h @@ -726,6 +726,7 @@ inline bool piDeleteSafety(T *& pointer) { //! \~english In this example "Error!" will be printed on every \b false function return. //! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции. class PIP_EXPORT PIScopeExitCall { + NO_COPY_CLASS(PIScopeExitCall) public: //! \~\brief //! \~english Constructor that takes a function to execute @@ -758,8 +759,6 @@ public: } private: - NO_COPY_CLASS(PIScopeExitCall) - std::function func; }; @@ -768,14 +767,14 @@ private: //! \~english Inherit from this class to make your class non-trivially copyable. //! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым. struct PIP_EXPORT PINonTriviallyCopyable { - PINonTriviallyCopyable() noexcept = default; - PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default; - PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept; - PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default; - PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default; + PINonTriviallyCopyable() = default; + PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default; + PINonTriviallyCopyable(PINonTriviallyCopyable &&) ; + PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default; + PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default; ~PINonTriviallyCopyable() = default; }; -inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default; +inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default; //! \~\brief diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index 19eddf28..7cb7a1dc 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -527,6 +527,7 @@ bool PIEthernet::listen(bool threaded) { listen_threaded = true; server_bounded = false; server_thread_.start(server_func); + server_thread_.waitForStart(); return true; } listen_threaded = server_bounded = false; diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index 5390fb5d..754eb4e3 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -4,22 +4,22 @@ //! \~english Thread-safe variable //! \~russian Потокобезопасная переменная /* - PIP - Platform Independent Primitives - Thread-safe variable - Ivan Pelipenko peri4ko@yandex.ru + PIP - Platform Independent Primitives + Thread-safe variable + Ivan Pelipenko peri4ko@yandex.ru - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ #ifndef PIPROTECTEDVARIABLE_H @@ -34,21 +34,29 @@ template class PIP_EXPORT PIProtectedVariable { public: + //! \~english Constructs %PIProtectedVariable and initialize variable by value `v`. + //! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`. + PIProtectedVariable(T v = T()): var(std::move(v)) {} + //! \~\brief //! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked. //! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным. class PIP_EXPORT Pointer { friend class PIProtectedVariable; + NO_COPY_CLASS(Pointer); + Pointer & operator=(Pointer && other) = delete; public: - //! \~english Copies wrapper state for access to the same protected value. - //! \~russian Копирует состояние обертки для доступа к тому же защищенному значению. - Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {} + //! \~english Move constructor - transfers ownership of the lock. + //! \~russian Конструктор перемещения - передает владение блокировкой. + Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; }; - //! \~english Destroys wrapper and releases the mutex when it owns the original lock. - //! \~russian Уничтожает обертку и освобождает мьютекс, когда она владеет исходной блокировкой. + //! \~english Destroys wrapper and releases the mutex. + //! \~russian Уничтожает обертку и освобождает мьютекс. ~Pointer() { - if (counter == 0) pv.mutex.unlock(); + if (can_unlock) { + pv.mutex.unlock(); + } } //! \~english Returns pointer access to the protected value. @@ -60,11 +68,11 @@ public: T & operator*() { return pv.var; } private: - Pointer() = delete; - Pointer(PIProtectedVariable & v): pv(v) {} + explicit Pointer() = delete; + explicit Pointer(PIProtectedVariable & v): pv(v) { pv.mutex.lock(); } PIProtectedVariable & pv; - int counter = 0; + bool can_unlock = true; }; //! \~english Replaces the protected value with \a v. @@ -76,10 +84,7 @@ public: //! \~english Locks the value and returns wrapper-based access to it. //! \~russian Блокирует значение и возвращает обертку для доступа к нему. - Pointer getRef() { - mutex.lock(); - return Pointer(*this); - } + Pointer getRef() { return Pointer(*this); } //! \~english Returns a copy of the protected value. //! \~russian Возвращает копию защищенного значения. @@ -98,7 +103,7 @@ public: private: mutable PIMutex mutex; - T var; + T var = {}; }; diff --git a/libs/main/types/pisystemtime.cpp b/libs/main/types/pisystemtime.cpp index 4d218968..f26b7bd6 100644 --- a/libs/main/types/pisystemtime.cpp +++ b/libs/main/types/pisystemtime.cpp @@ -105,7 +105,7 @@ extern clock_serv_t __pi_mac_clock; //! Используйте этот метод для ожидания разниц системных времен или своего времени. //! Если метод будет вызван для системного времени \a PISystemTime::current(), то //! ожидание будет почти бесконечным -void PISystemTime::sleep() { +void PISystemTime::sleep() const { piUSleep(piFloord(toMicroseconds())); } @@ -118,7 +118,7 @@ void PISystemTime::toTimespec(void * ts) { } -PISystemTime::Frequency PISystemTime::toFrequency() { +PISystemTime::Frequency PISystemTime::toFrequency() const { return PISystemTime::Frequency::fromSystemTime(*this); } diff --git a/libs/main/types/pisystemtime.h b/libs/main/types/pisystemtime.h index bb197377..c7438c7e 100644 --- a/libs/main/types/pisystemtime.h +++ b/libs/main/types/pisystemtime.h @@ -253,7 +253,7 @@ public: //! \~english Sleep for this time //! \~russian Ожидать это время - void sleep(); + void sleep() const; //! \~english On *nix system assign current value to timespec struct //! \~russian На *nix системах присваивает время к timespec структуре @@ -261,7 +261,7 @@ public: //! \~english Returns \a Frequency that corresponds this time interval //! \~russian Возвращает \a Frequency соответствующую этому временному интервалу - PISystemTime::Frequency toFrequency(); + PISystemTime::Frequency toFrequency() const; //! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and " ..." for relative //! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и " ..." для относительного diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a5ba75e2..20503839 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,8 +24,10 @@ macro(pip_test NAME) file(GLOB _HDRS "${NAME}/*.h") set(_target pip_${NAME}_test) add_executable(${_target} ${_CPPS} ${_HDRS}) - target_link_libraries(${_target} pip ${ARGN} gtest_main) - gtest_discover_tests(${_target}) + target_link_libraries(${_target} pip ${ARGN} gtest_main) + if (TESTS_RUN) + gtest_discover_tests(${_target}) + endif() list(APPEND PIP_TESTS_LIST "${NAME}") set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE) endmacro() @@ -37,3 +39,4 @@ pip_test(piobject) pip_test(client_server pip_client_server) pip_test(io) pip_test(system) +pip_test(thread) diff --git a/tests/client_server/client_server_test.cpp b/tests/client_server/client_server_test.cpp index bbe639c5..cb6fc68c 100644 --- a/tests/client_server/client_server_test.cpp +++ b/tests/client_server/client_server_test.cpp @@ -32,7 +32,8 @@ Client * createAndConnectClient() { TEST(ClientServer, OneClient) { auto const loop_timeout = 1000_ms; auto s = createServer(); - auto c = createAndConnectClient>(); + piMinSleep(); + auto c = createAndConnectClient>(); waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout); EXPECT_EQ(1, s->clientsCount()); @@ -102,10 +103,11 @@ int getClientsPings(const PIVector & clients) { TEST(ClientServer, ManyClients) { - auto const loop_timeout = 100_ms; + auto const loop_timeout = 1_s; constexpr int clients_count = 20; PIVector clients; auto s = createServer(); + piMinSleep(); piForTimes(clients_count) { clients.append(new ClientSendThread()); @@ -137,8 +139,9 @@ TEST(ClientServer, ManyClients) { for (const auto c: clients) { c->startSend(); } - (100_ms).sleep(); + waitLoop([&clients]() { return getClientsPings(clients) > clients_count * 2; }, loop_timeout); EXPECT_TRUE(getClientsPings(clients) > clients_count * 2); + waitLoop([s]() { return getServerPongs(s) > clients_count * 2; }, loop_timeout); EXPECT_TRUE(getServerPongs(s) > clients_count * 2); piDeleteAllAndClear(clients); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); @@ -147,7 +150,7 @@ TEST(ClientServer, ManyClients) { } TEST(ClientServer, DynamicClients) { - auto const loop_timeout = 100_ms; + auto const loop_timeout = 3_s; constexpr int clients_count = 20; PIVector clients; PIMutex clients_mutex; @@ -160,7 +163,6 @@ TEST(ClientServer, DynamicClients) { clients_mutex.lock(); clients << c; clients_mutex.unlock(); - piCout << "new client" << clients.size(); }; piForTimes(clients_count) { @@ -178,9 +180,8 @@ TEST(ClientServer, DynamicClients) { piForTimes(new_cnt) { spawnClient(); } - piCout << "+++++++"; }, - 12_Hz); + 120_Hz); deleteThread.start( [&clients, &clients_mutex]() { @@ -194,26 +195,17 @@ TEST(ClientServer, DynamicClients) { clients_mutex.unlock(); if (c) { delete c; - piCout << "remove client" << clients.size(); } } - piCout << "----------"; }, - 13_Hz); - - (2_s).sleep(); + 130_Hz); + waitLoop([s]() { return s->clientsCount() >= 10; }, loop_timeout); EXPECT_GE(s->clientsCount(), 10); - piCout << "now clients" << clients.size(); - - deleteThread.stopAndWait(); spawnThread.stopAndWait(); - - piCout << "total clients" << clients.size(); - piDeleteAllAndClear(clients); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); EXPECT_EQ(0, s->clientsCount()); diff --git a/tests/math/testpimathmatrixt.cpp b/tests/math/testpimathmatrixt.cpp index 0245599c..243431ec 100644 --- a/tests/math/testpimathmatrixt.cpp +++ b/tests/math/testpimathmatrixt.cpp @@ -376,7 +376,7 @@ TEST(PIMathMatrixT_Test, invert) { matrix3 = matrix1; matrix1.invert(); EXPECT_EQ(matrix1, matrix3); - EXPECT_DOUBLE_EQ(std::abs(d1 - (1. / d2)), 0.); + EXPECT_NEAR(std::abs(d1 - (1. / d2)), 0., 1e-12); } TEST(PIMathMatrixT_Test, inverted) { diff --git a/tests/thread/piprotectedvariable_test.cpp b/tests/thread/piprotectedvariable_test.cpp new file mode 100644 index 00000000..e7facc40 --- /dev/null +++ b/tests/thread/piprotectedvariable_test.cpp @@ -0,0 +1,118 @@ +#include "piprotectedvariable.h" +#include "pistring.h" +#include "pithread.h" +#include "piliterals_time.h" + +#include "gtest/gtest.h" +#include + +// Basic functionality tests +TEST(PIProtectedVariable_Basic, BasicFunctionality) { + // Test basic set/get with different types + PIProtectedVariable pvInt; + PIProtectedVariable pvDouble; + PIProtectedVariable pvString; + + pvInt.set(123); + pvDouble.set(3.14159); + pvString.set(PIString("Hello, World!")); + + EXPECT_EQ(pvInt.get(), 123); + EXPECT_DOUBLE_EQ(pvDouble.get(), 3.14159); + EXPECT_EQ(pvString.get(), PIString("Hello, World!")); + + // Test operator= + pvInt = 999; + EXPECT_EQ(pvInt.get(), 999); + + // Test getRef() with Pointer + struct TestStruct { + int x = 10; + int y = 20; + int getValue() const { return x + y; } + }; + + PIProtectedVariable pvStruct; + + auto ptr = pvStruct.getRef(); + EXPECT_EQ(ptr->x, 10); + EXPECT_EQ(ptr->y, 20); + EXPECT_EQ(ptr->getValue(), 30); + + // Modify through pointer + *ptr = TestStruct(); + ptr->x = 100; + EXPECT_EQ(pvStruct.get().x, 100); + + // Test for Pointer + pvInt.set(42); + auto ptr1 = pvInt.getRef(); + EXPECT_EQ(*ptr1, 42); + *ptr1 = 55; + EXPECT_EQ(*ptr1, 55); + auto ptr2 = std::move(ptr1); + EXPECT_EQ(*ptr2, 55); + *ptr2 = 100; + EXPECT_EQ(*ptr2, 100); + auto ptr3 = pvInt.getRef(); + EXPECT_EQ(*ptr3, 100); + *ptr3 = 333; + EXPECT_EQ(*ptr3, 333); + EXPECT_EQ(pvInt.get(), 333); +} + +// Thread safety tests +TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) { + PIProtectedVariable pv; + + std::atomic writeCount(0); + std::atomic readCount(0); + std::atomic invalidReads(0); + const int NUM_ITERATIONS = 1000; + const int NUM_WRITERS = 10; + const int NUM_READERS = 20; + const int TOTAL_WRITES = NUM_WRITERS * NUM_ITERATIONS; + + // Collect thread handles for joining + PIVector threads; + + // Create writer threads + for (int i = 0; i < NUM_WRITERS; ++i) { + threads.push_back(new PIThread([&pv, &writeCount]() { + for (int j = 0; j < NUM_ITERATIONS; ++j) { + auto val = pv.getRef(); + (*val)++; + auto val2 = pv.getRef(); + writeCount++; + ASSERT_EQ(writeCount, *val2); + } + })); + } + + // Create reader threads + for (int i = 0; i < NUM_READERS; ++i) { + threads.push_back(new PIThread([&pv, &invalidReads, &readCount]() { + for (int j = 0; j < NUM_ITERATIONS; ++j) { + auto val = pv.get(); + readCount++; + // Value should always be in valid range [0, TOTAL_WRITES] + if (val < 0 || val > TOTAL_WRITES) { + invalidReads++; + } + } + })); + } + + threads.forEach([](PIThread * & t) {t->startOnce();}); + threads.forEach([](PIThread * & t) {t->waitForFinish(2_s);}); + piDeleteAll(threads); + + // Verify results + EXPECT_EQ(writeCount, TOTAL_WRITES); + EXPECT_EQ(readCount, NUM_READERS * NUM_ITERATIONS); + EXPECT_EQ(invalidReads, 0) << "All reads should return valid values in range [0, " << TOTAL_WRITES << "]"; + + // Final value should be TOTAL_WRITES + int finalVal = pv.get(); + EXPECT_EQ(finalVal, TOTAL_WRITES); +} diff --git a/utils/deploy_tool/main.cpp b/utils/deploy_tool/main.cpp index 899c9a46..69b15942 100644 --- a/utils/deploy_tool/main.cpp +++ b/utils/deploy_tool/main.cpp @@ -467,11 +467,20 @@ bool procDpkg(const PIString & l) { if (!vs.isEmpty()) { PIStringList lines = vs.split('\n').reverse(); for (auto l: lines) { - auto sl = l; - l = l.left(l.find(":")); - if (!l.isEmpty() && !l.endsWith("-cross") && !l.contains(' ')) all_deps << l; - // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; - return true; + l = l.left(l.find(":")); + if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross") && !l.endsWith("-dev")) { + // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; + all_deps << l; + return true; + } + } + for (auto l: lines) { + l = l.left(l.find(":")); + if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross")) { + // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; + all_deps << l; + return true; + } } } // piCout << "No dep on" << l;