Skip to content

Commit 205cabd

Browse files
vdm-devurfeex
andauthored
Support compilation on Windows (UniversalRobots#229)
This commit makes the ur_client_library compile on windows (with MSVC). This does not mark full Windows support and should be considered an experimental addition. It was tested that everything compiles fine using MSVC and that all examples work as expected. --------- Co-authored-by: Felix Exner <[email protected]>
1 parent c00bb3c commit 205cabd

32 files changed

+471
-205
lines changed

.github/workflows/win-build.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Windows build and tests
2+
on:
3+
workflow_dispatch:
4+
pull_request:
5+
push:
6+
branches:
7+
- master
8+
9+
jobs:
10+
win-build:
11+
timeout-minutes: 30
12+
runs-on: windows-2022
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
- name: configure
17+
run: mkdir build && cd build && cmake .. -DBUILDING_TESTS=1
18+
#run: mkdir build && cd build && cmake .. -DBUILDING_TESTS=1 -DINTEGRATION_TESTS=1
19+
- name: build
20+
run: cmake --build build --config Debug
21+
- name: test
22+
run: cd build && ctest --output-on-failure -C Debug

3rdparty/endian/endian.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// endian.h
3+
//
4+
// https://gist.github.com/panzi/6856583
5+
//
6+
// I, Mathias Panzenböck, place this file hereby into the public domain. Use
7+
// it at your own risk for whatever you like. In case there are
8+
// jurisdictions that don't support putting things in the public domain you
9+
// can also consider it to be "dual licensed" under the BSD, MIT and Apache
10+
// licenses, if you want to. This code is trivial anyway. Consider it an
11+
// example on how to get the endian conversion functions on different
12+
// platforms.
13+
14+
#ifndef PORTABLE_ENDIAN_H__
15+
#define PORTABLE_ENDIAN_H__
16+
17+
// Byte order
18+
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__CYGWIN__)
19+
# include <endian.h>
20+
#elif defined(__APPLE__)
21+
# include <libkern/OSByteOrder.h>
22+
23+
# define htobe16(x) OSSwapHostToBigInt16(x)
24+
# define htole16(x) OSSwapHostToLittleInt16(x)
25+
# define be16toh(x) OSSwapBigToHostInt16(x)
26+
# define le16toh(x) OSSwapLittleToHostInt16(x)
27+
28+
# define htobe32(x) OSSwapHostToBigInt32(x)
29+
# define htole32(x) OSSwapHostToLittleInt32(x)
30+
# define be32toh(x) OSSwapBigToHostInt32(x)
31+
# define le32toh(x) OSSwapLittleToHostInt32(x)
32+
33+
# define htobe64(x) OSSwapHostToBigInt64(x)
34+
# define htole64(x) OSSwapHostToLittleInt64(x)
35+
# define be64toh(x) OSSwapBigToHostInt64(x)
36+
# define le64toh(x) OSSwapLittleToHostInt64(x)
37+
38+
# define __BYTE_ORDER BYTE_ORDER
39+
# define __BIG_ENDIAN BIG_ENDIAN
40+
# define __LITTLE_ENDIAN LITTLE_ENDIAN
41+
# define __PDP_ENDIAN PDP_ENDIAN
42+
#elif defined(__OpenBSD__)
43+
# include <sys/endian.h>
44+
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
45+
# include <sys/endian.h>
46+
47+
# define be16toh(x) betoh16(x)
48+
# define le16toh(x) letoh16(x)
49+
50+
# define be32toh(x) betoh32(x)
51+
# define le32toh(x) letoh32(x)
52+
53+
# define be64toh(x) betoh64(x)
54+
# define le64toh(x) letoh64(x)
55+
#elif defined(_WIN32)
56+
# include <stdlib.h>
57+
# if BYTE_ORDER == LITTLE_ENDIAN
58+
# if defined(_MSC_VER)
59+
# define htobe16(x) _byteswap_ushort(x)
60+
# define htole16(x) (x)
61+
# define be16toh(x) _byteswap_ushort(x)
62+
# define le16toh(x) (x)
63+
64+
# define htobe32(x) _byteswap_ulong(x)
65+
# define htole32(x) (x)
66+
# define be32toh(x) _byteswap_ulong(x)
67+
# define le32toh(x) (x)
68+
69+
# define htobe64(x) _byteswap_uint64(x)
70+
# define htole64(x) (x)
71+
# define be64toh(x) _byteswap_uint64(x)
72+
# define le64toh(x) (x)
73+
# elif defined(__GNUC__) || defined(__clang__)
74+
# define htobe16(x) __builtin_bswap16(x)
75+
# define htole16(x) (x)
76+
# define be16toh(x) __builtin_bswap16(x)
77+
# define le16toh(x) (x)
78+
79+
# define htobe32(x) __builtin_bswap32(x)
80+
# define htole32(x) (x)
81+
# define be32toh(x) __builtin_bswap32(x)
82+
# define le32toh(x) (x)
83+
84+
# define htobe64(x) __builtin_bswap64(x)
85+
# define htole64(x) (x)
86+
# define be64toh(x) __builtin_bswap64(x)
87+
# define le64toh(x) (x)
88+
# else
89+
# error Compiler is not supported
90+
# endif
91+
# else
92+
# error Byte order is not supported
93+
# endif
94+
95+
# define __BYTE_ORDER BYTE_ORDER
96+
# define __BIG_ENDIAN BIG_ENDIAN
97+
# define __LITTLE_ENDIAN LITTLE_ENDIAN
98+
# define __PDP_ENDIAN PDP_ENDIAN
99+
#else
100+
# error Platform is not supported
101+
#endif
102+
103+
#endif // PORTABLE_ENDIAN_H__

CMakeLists.txt

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
77
endif()
88

99
option(WITH_ASAN "Compile with address sanitizer support" OFF)
10+
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
1011

11-
add_library(urcl SHARED
12+
if(MSVC)
13+
set(BUILD_SHARED_LIBS OFF)
14+
endif()
15+
16+
17+
add_library(urcl
1218
src/comm/tcp_socket.cpp
1319
src/comm/tcp_server.cpp
1420
src/control/reverse_interface.cpp
@@ -46,12 +52,21 @@ add_library(urcl SHARED
4652
src/helpers.cpp
4753
)
4854
add_library(ur_client_library::urcl ALIAS urcl)
49-
target_compile_options(urcl PRIVATE -Wall -Wextra -Wno-unused-parameter)
5055
target_compile_features(urcl PUBLIC cxx_std_17)
51-
if(WITH_ASAN)
52-
target_compile_options(urcl PUBLIC -fsanitize=address)
53-
target_link_options(urcl PUBLIC -fsanitize=address)
56+
57+
if(MSVC)
58+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/endian)
59+
target_link_libraries(urcl ws2_32)
60+
else()
61+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
62+
target_compile_options(urcl PRIVATE -Wall -Wextra -Wno-unused-parameter)
63+
64+
if(WITH_ASAN)
65+
target_compile_options(urcl PUBLIC -fsanitize=address)
66+
target_link_options(urcl PUBLIC -fsanitize=address)
67+
endif()
5468
endif()
69+
5570
target_include_directories( urcl PUBLIC
5671
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
5772
$<INSTALL_INTERFACE:include>

examples/dashboard_example.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@
3333
#include <ur_client_library/log.h>
3434
#include <ur_client_library/ur/dashboard_client.h>
3535

36-
#include <iostream>
3736
#include <memory>
3837
#include <thread>
39-
#include <unistd.h>
4038

4139
using namespace urcl;
4240

@@ -100,7 +98,7 @@ int main(int argc, char* argv[])
10098
return 1;
10199
}
102100

103-
sleep(1);
101+
std::this_thread::sleep_for(std::chrono::seconds(1));
104102

105103
// Play loaded program
106104
if (!my_dashboard->commandPlay())
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2024, RoboDK Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#ifdef _WIN32
20+
21+
# define NOMINMAX
22+
# define WIN32_LEAN_AND_MEAN
23+
# include <WinSock2.h>
24+
# include <ws2tcpip.h>
25+
26+
# ifndef TCP_QUICKACK
27+
# define TCP_QUICKACK 12
28+
# endif
29+
30+
# ifdef ERROR
31+
# undef ERROR
32+
# endif // ERROR
33+
34+
typedef SOCKET socket_t;
35+
typedef SSIZE_T ssize_t;
36+
37+
static inline int ur_setsockopt(socket_t s, int level, int optname, const void* optval, unsigned int optlen)
38+
{
39+
return ::setsockopt(s, level, optname, reinterpret_cast<const char*>(optval), static_cast<int>(optlen));
40+
}
41+
42+
static inline int ur_close(socket_t s)
43+
{
44+
return ::closesocket(s);
45+
}
46+
47+
#else // _WIN32
48+
49+
# include <netdb.h>
50+
# include <sys/socket.h>
51+
# include <sys/types.h>
52+
# include <unistd.h>
53+
54+
typedef int socket_t;
55+
56+
# ifndef INVALID_SOCKET
57+
# define INVALID_SOCKET (-1)
58+
# endif
59+
60+
# define ur_setsockopt setsockopt
61+
# define ur_close close
62+
63+
#endif // _WIN32

include/ur_client_library/comm/stream.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
*/
2020

2121
#pragma once
22-
#include <netdb.h>
23-
#include <sys/socket.h>
24-
#include <sys/types.h>
2522
#include <atomic>
2623
#include <chrono>
2724
#include <mutex>

include/ur_client_library/comm/tcp_server.h

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,13 @@
2929
#ifndef UR_CLIENT_LIBRARY_TCP_SERVER_H_INCLUDED
3030
#define UR_CLIENT_LIBRARY_TCP_SERVER_H_INCLUDED
3131

32-
#include <netdb.h>
33-
#include <sys/socket.h>
34-
#include <sys/types.h>
35-
#include <unistd.h>
36-
3732
#include <atomic>
3833
#include <chrono>
3934
#include <functional>
4035
#include <thread>
4136

37+
#include "ur_client_library/comm/socket_t.h"
38+
4239
namespace urcl
4340
{
4441
namespace comm
@@ -80,7 +77,7 @@ class TCPServer
8077
* \param func Function handling the event information. The file descriptor created by the
8178
* connection event will be passed to the function.
8279
*/
83-
void setConnectCallback(std::function<void(const int)> func)
80+
void setConnectCallback(std::function<void(const socket_t)> func)
8481
{
8582
new_connection_callback_ = func;
8683
}
@@ -91,7 +88,7 @@ class TCPServer
9188
* \param func Function handling the event information. The file descriptor created by the
9289
* connection event will be passed to the function.
9390
*/
94-
void setDisconnectCallback(std::function<void(const int)> func)
91+
void setDisconnectCallback(std::function<void(const socket_t)> func)
9592
{
9693
disconnect_callback_ = func;
9794
}
@@ -102,7 +99,7 @@ class TCPServer
10299
* \param func Function handling the event information. The file client's file_descriptor will be
103100
* passed to the function as well as the actual message received from the client.
104101
*/
105-
void setMessageCallback(std::function<void(const int, char*, int)> func)
102+
void setMessageCallback(std::function<void(const socket_t, char*, int)> func)
106103
{
107104
message_callback_ = func;
108105
}
@@ -133,7 +130,7 @@ class TCPServer
133130
*
134131
* \returns True on success, false otherwise
135132
*/
136-
bool write(const int fd, const uint8_t* buf, const size_t buf_len, size_t& written);
133+
bool write(const socket_t fd, const uint8_t* buf, const size_t buf_len, size_t& written);
137134

138135
/*!
139136
* \brief Get the maximum number of clients allowed to connect to this server
@@ -164,10 +161,10 @@ class TCPServer
164161
//! Handles connection events
165162
void handleConnect();
166163

167-
void handleDisconnect(const int fd);
164+
void handleDisconnect(const socket_t fd);
168165

169166
//! read data from socket
170-
void readData(const int fd);
167+
void readData(const socket_t fd);
171168

172169
//! Event handler. Blocks until activity on any client or connection attempt
173170
void spin();
@@ -178,25 +175,22 @@ class TCPServer
178175
std::atomic<bool> keep_running_;
179176
std::thread worker_thread_;
180177

181-
std::atomic<int> listen_fd_;
178+
std::atomic<socket_t> listen_fd_;
182179
int port_;
183180

184-
int maxfd_;
181+
socket_t maxfd_;
185182
fd_set masterfds_;
186183
fd_set tempfds_;
187184

188185
uint32_t max_clients_allowed_;
189-
std::vector<int> client_fds_;
190-
191-
// Pipe for the self-pipe trick (https://cr.yp.to/docs/selfpipe.html)
192-
int self_pipe_[2];
186+
std::vector<socket_t> client_fds_;
193187

194188
static const int INPUT_BUFFER_SIZE = 100;
195189
char input_buffer_[INPUT_BUFFER_SIZE];
196190

197-
std::function<void(const int)> new_connection_callback_;
198-
std::function<void(const int)> disconnect_callback_;
199-
std::function<void(const int, char* buffer, int nbytesrecv)> message_callback_;
191+
std::function<void(const socket_t)> new_connection_callback_;
192+
std::function<void(const socket_t)> disconnect_callback_;
193+
std::function<void(const socket_t, char* buffer, int nbytesrecv)> message_callback_;
200194
};
201195

202196
} // namespace comm

0 commit comments

Comments
 (0)