diff --git a/CMakeLists.txt b/CMakeLists.txt index 932d46892d..3239cd36b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +if(WIN32) + cmake_minimum_required(VERSION 3.11 FATAL_ERROR) +else() + cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +endif() if(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index f9d4651f3d..b698db809a 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -865,3 +865,18 @@ else() message (STATUS "Looking for qwt >= 6.1.0 - not found") BUILD_ERROR ("Missing: libqwt-dev. Required for plotting.") endif () + +######################################## +# On Windows, find tiny-process-library +if (WIN32) + option(USE_EXTERNAL_TINY_PROCESS_LIBRARY "Use external tiny-process-library." OFF) + if (USE_EXTERNAL_TINY_PROCESS_LIBRARY) + find_package(tiny-process-library QUIET) + if (NOT tiny-process-library_FOUND) + message (STATUS "Looking for tiny-process-library - not found") + BUILD_ERROR ("Missing: tiny-process-library, even if USE_EXTERNAL_TINY_PROCESS_LIBRARY was enabled.") + else() + message (STATUS "Looking for tiny-process-library - found") + endif() + endif() +endif() diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 2d2080c73c..bc852d9979 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -4,4 +4,8 @@ if (NOT CCD_FOUND) add_subdirectory(libccd) endif() # add_subdirectory(ann) -# add_subdirectory(fcl) +# add_subdirectory(fcl + +if (WIN32 AND NOT USE_EXTERNAL_TINY_PROCESS_LIBRARY) + add_subdirectory(tiny-process-library) +endif() diff --git a/deps/tiny-process-library/CMakeLists.txt b/deps/tiny-process-library/CMakeLists.txt new file mode 100644 index 0000000000..f3b80f9a13 --- /dev/null +++ b/deps/tiny-process-library/CMakeLists.txt @@ -0,0 +1,52 @@ +include(FetchContent) +FetchContent_Declare( + tinyprocesslibrary + GIT_REPOSITORY https://gitlab.com/eidheim/tiny-process-library.git + GIT_TAG v2.0.2) + +FetchContent_GetProperties(tinyprocesslibrary) + +if(NOT tinyprocesslibrary_POPULATED) + FetchContent_Populate(tinyprocesslibrary) + + # We don't want to install this library in the system, we instead + # compile it as an OBJECT library and embed in either the shared or + # static libraries that need it. + # From https://gitlab.kitware.com/cmake/cmake/-/issues/18935 it seems + # that OBJECT libraries that are not installed become INTERFACE when + # part of an EXPORT set. + # This behaviour allows setting transitively tiny-process-library infos + # to the consuming targets while not breaking the EXPORT process. In fact, + # the conversion to INTERFACE allows to add tiny-process-library to the + # targets of the EXPORT that contains targets linking against it. + # See also https://cmake.org/pipermail/cmake/2018-September/068250.html. + + if(WIN32) + add_library(tiny-process-library OBJECT + ${tinyprocesslibrary_SOURCE_DIR}/process.cpp + ${tinyprocesslibrary_SOURCE_DIR}/process_win.cpp) + #If compiled using MSYS2, use sh to run commands + if(MSYS) + target_compile_definitions(tiny-process-library + PUBLIC MSYS_PROCESS_USE_SH) + endif() + else() + add_library(tiny-process-library OBJECT + ${tinyprocesslibrary_SOURCE_DIR}/process.cpp + ${tinyprocesslibrary_SOURCE_DIR}/process_unix.cpp) + endif() + add_library(tiny-process-library::tiny-process-library ALIAS tiny-process-library) + + if(MSVC) + target_compile_definitions(tiny-process-library + PRIVATE /D_CRT_SECURE_NO_WARNINGS) + endif() + + find_package(Threads REQUIRED) + + target_link_libraries(tiny-process-library PRIVATE + ${CMAKE_THREAD_LIBS_INIT}) + target_include_directories(tiny-process-library PUBLIC + $) + +endif() diff --git a/gazebo/CMakeLists.txt b/gazebo/CMakeLists.txt index 27f4b62f87..7ed1ea81c3 100644 --- a/gazebo/CMakeLists.txt +++ b/gazebo/CMakeLists.txt @@ -82,28 +82,35 @@ endif() gz_install_executable(gzserver) manpage(gzserver 1) -# gazebo executable doesn't yet work on Windows -if (NOT WIN32) - gz_add_executable(gazebo gazebo_main.cc) - target_link_libraries(gazebo - libgazebo - libgazebo_client - gazebo_common - gazebo_util - gazebo_transport - gazebo_physics - gazebo_sensors - gazebo_rendering - gazebo_msgs - gazebo_gui - ) - - gz_install_executable(gazebo) - manpage(gazebo 1) + +gz_add_executable(gazebo gazebo_main.cc) +target_link_libraries(gazebo + libgazebo + libgazebo_client + gazebo_common + gazebo_util + gazebo_transport + gazebo_physics + gazebo_sensors + gazebo_rendering + gazebo_msgs + gazebo_gui +) + +if(WIN32) + target_link_libraries(gazebo tiny-process-library::tiny-process-library) endif() +gz_install_executable(gazebo) +manpage(gazebo 1) + + gz_add_library(libgazebo Server.cc Master.cc gazebo.cc gazebo_shared.cc) -set_target_properties(libgazebo PROPERTIES OUTPUT_NAME "gazebo") + +# On Windows calling libgazebo "gazebo" will conflict with the Gazebo executable +if (NOT WIN32) + set_target_properties(libgazebo PROPERTIES OUTPUT_NAME "gazebo") +endif() target_link_libraries(libgazebo gazebo_common diff --git a/gazebo/gazebo_main.cc b/gazebo/gazebo_main.cc index 8f456b24b0..0d7ee00541 100644 --- a/gazebo/gazebo_main.cc +++ b/gazebo/gazebo_main.cc @@ -14,24 +14,20 @@ * limitations under the License. * */ + +#ifndef _WIN32 #include #include #include #include +#else +#include +#endif #include "gazebo/common/Console.hh" #include "gazebo/Server.hh" #include "gazebo/gui/GuiIface.hh" -bool sig_killed = false; -int status1, status2; -// pid of server process -pid_t pid1; -// pid of client process -pid_t pid2; -bool killed1 = false; -bool killed2 = false; - ///////////////////////////////////////////////// void help() { @@ -77,6 +73,16 @@ void help() << "\n"; } +#ifndef _WIN32 +bool sig_killed = false; +int status1, status2; +// pid of server process +pid_t pid1; +// pid of client process +pid_t pid2; +bool killed1 = false; +bool killed2 = false; + /// \brief Try to kill a single process. /// \param[in] _pid Process ID. /// \param[in] _name Process name. @@ -216,3 +222,85 @@ int main(int _argc, char **_argv) return returnValue; } + +#else + +std::atomic g_shouldExit = false; + +int main(int _argc, char **_argv) +{ + if (_argc >= 2 && + (strcmp(_argv[1], "-h") == 0 || strcmp(_argv[1], "--help") == 0)) { + help(); + return 0; + } + + std::vector argvServer; + std::vector argvClient; + + argvServer.push_back("gzserver"); + argvClient.push_back("gzclient"); + + for (int i = 1; i < _argc; ++i) + { + argvServer.push_back(std::string(_argv[i])); + argvClient.push_back(std::string(_argv[i])); + } + + // Start server + TinyProcessLib::Process server(argvServer); + + // Start client + TinyProcessLib::Process client(argvClient); + + // Wait + bool serverClosed = false; + bool clientClosed = false; + int serverExitCode = 0; + int clientExitCode = 0; + + while (!g_shouldExit) + { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + // Check if server and gui are still open, if they are closed + // close this process as well + serverClosed = server.try_get_exit_status(serverExitCode); + clientClosed = client.try_get_exit_status(clientExitCode); + + if (serverClosed || clientClosed) + { + g_shouldExit = true; + } + + } + + // Cleanup + serverClosed = server.try_get_exit_status(serverExitCode); + + if (!serverClosed) + { + server.kill(); + } + + clientClosed = client.try_get_exit_status(clientExitCode); + + if (!clientClosed) + { + client.kill(); + } + + // Get exit status + serverExitCode = server.get_exit_status(); + clientExitCode = client.get_exit_status(); + + int exitCode = 0; + if (serverExitCode != 0 || clientExitCode != 0) + { + exitCode = 1; + } + + return exitCode; +} + +#endif