Finish port to windows

Fix all problems on windows port, add build instructions to Readme
This commit is contained in:
Johannes Schriewer 2018-08-05 18:24:35 +02:00
parent ccd0ea3bf8
commit b26cf9d917
8 changed files with 130 additions and 60 deletions

View file

@ -25,5 +25,6 @@
"**/*.a": true, "**/*.a": true,
"**/build": true "**/build": true
}, },
"coverage-gutters.lcovname": "coverage.info" "coverage-gutters.lcovname": "coverage.info",
"cmake.buildDirectory": "${workspaceRoot}/build"
} }

View file

@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.0)
project(LIBMQTT) project(LIBMQTT)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_C_STANDARD 99)
# #
# Version # Version
@ -24,8 +26,8 @@ if (UNIX)
endif() # UNIX endif() # UNIX
if (MSVC) if (MSVC)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG=1 -DMSVC") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG=1 -DMSVC -Dstrdup=_strdup /std:c++latest")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DMSVC") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DMSVC -Dstrdup=_strdup /std:c++latest")
endif() endif()
# #

View file

@ -5,7 +5,7 @@ MQTT library for multiple platforms including embedded targets.
1. Readable code, modern C 1. Readable code, modern C
2. Simple and fool-proof API 2. Simple and fool-proof API
3. High test coverage (aim is >85% of all lines tested) 3. High test coverage (aim is >85% of all lines tested)
4. Suitable for embedded or Unixy targets (no static allocation though) 4. Suitable for embedded or bigger targets (no static allocation though)
## Current state ## Current state
@ -14,13 +14,15 @@ MQTT library for multiple platforms including embedded targets.
- Supports MQTT 3.1.1 (aka. protocol level 4) - Supports MQTT 3.1.1 (aka. protocol level 4)
- Platform support for linux is working - Platform support for linux is working
- Test have a coverage of about 92% (lines) and 97% (functions), untested code is just bail-out error handling for fatal errors (usually programming errors or network failure) - Test have a coverage of about 92% (lines) and 97% (functions), untested code is just bail-out error handling for fatal errors (usually programming errors or network failure)
- Builds on Linux (GCC and Clang) and Windows (MSVC and Clang/c2)
## TODO ## TODO
- [ ] QoS testing, server -> client does not - [ ] QoS testing, server -> client does not
- [ ] Running in MQTT Broker mode - [ ] Reconnect does not work correctly
- [ ] Running in MQTT Broker mode (very low prio)
- [ ] Implement Protocol level 3 (low prio) - [ ] Implement Protocol level 3 (low prio)
- [ ] Implement Draft Protocol level 5 (somewhat low prio as it's a draft spec) - [ ] Implement Draft Protocol level 5
- [ ] Support ESP8266 (RTOS) - [ ] Support ESP8266 (RTOS)
- [ ] Support ESP32 (IDF) - [ ] Support ESP32 (IDF)
@ -86,6 +88,73 @@ make coverage
Be aware to create the coverage report the tests must run, so you'll need the MQTT broker. Be aware to create the coverage report the tests must run, so you'll need the MQTT broker.
### Building on Windows
Requirements:
- `cmake` in path
- Visual Studio (Community Edition is sufficient)
- Checked out repo
#### With Visual Studio (MSVC)
1. Run cmake:
```powershell
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release .. -G "Visual Studio 15 2017 Win64"
```
2. Build the library
```powershell
cd build
cmake --build c:/absolute/path/to/build --config Debug --target ALL_BUILD -- /m /property:GenerateFullPaths=true
```
The dynamic and static libs will be placed in the `<config>/` sub-dir of that folder and the include is in `src/mqtt.h`
Alternatively open the Visual Studio solution in the `build` dir in Visual Studio.
**Attention:** As MSVC is not C99 compliant the tests for encode and decode packet will not work. Use `clang` for that.
#### With Visual Studio (clang)
Requirements:
- Clang/C2 installed (experimental option in Visual Studio Installer)
1. Run cmake:
```powershell
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release .. -G "Visual Studio 15 2017 Win64" -T v141_clang_c2
```
2. Build the library
```powershell
cd build
cmake --build c:/absolute/path/to/build --config Debug --target ALL_BUILD -- /m /property:GenerateFullPaths=true
```
The dynamic and static libs will be placed in the `<config>/` sub-dir of that folder and the include is in `src/mqtt.h`
Alternatively open the Visual Studio solution in the `build` dir in Visual Studio.
### Testing on Windows
Requirements:
- Local MQTT broker, simplest is mosquitto (can be run by calling `mosquitto -v` in a terminal for debug output)
**Attention:** Coverage report is not possible on Windows currently
1. Build everything
```powershell
cd build
cmake --build c:/absolute/path/to/build --config Debug --target ALL_BUILD -- /m /property:GenerateFullPaths=true
```
2. Run tests
```powershell
cd build
ctest -C Debug -T test --output-on-failure
```
### API ### API
#### Configuration #### Configuration

View file

@ -3,7 +3,19 @@
#include "mqtt_internal.h" #include "mqtt_internal.h"
typedef void *(*PlatformTask)(MQTTHandle *handle); /* Sadly we have to have a Windows-ism here */
#if MSVC
typedef uint32_t (__stdcall *PlatformTask)(void *context);
// Use this to define the PlatformTask callback functions to be cross platform
#define PlatformTaskFunc(_name) uint32_t __stdcall _name(void *context)
#else
typedef void *(*PlatformTask)(void *context);
// Use this to define the PlatformTask callback functions to be cross platform
#define PlatformTaskFunc(_name) void *_name(void *context)
#endif
typedef void (*PlatformTimerCallback)(MQTTHandle *handle, int timer_handle); typedef void (*PlatformTimerCallback)(MQTTHandle *handle, int timer_handle);
/** maximum receiver buffer size, defined by platform */ /** maximum receiver buffer size, defined by platform */

View file

@ -32,7 +32,8 @@ struct _PlatformData {
int timer_task; int timer_task;
}; };
void *timer_task(MQTTHandle *handle) { PlatformTaskFunc(timer_task) {
MQTTHandle *handle = (MQTTHandle *)context;
while (1) { while (1) {
platform_sleep(1000); platform_sleep(1000);
@ -53,7 +54,7 @@ void *timer_task(MQTTHandle *handle) {
} }
if (!active) { if (!active) {
return NULL; return 0;
} }
} }
} }
@ -159,7 +160,7 @@ PlatformStatusCode platform_resolve_host(char *hostname , char *ip) {
addr_list = (struct in_addr **) he->h_addr_list; addr_list = (struct in_addr **) he->h_addr_list;
for(i = 0; addr_list[i] != NULL; i++) { for(i = 0; addr_list[i] != NULL; i++) {
strcpy(ip , inet_ntoa(*addr_list[i])); strncpy_s(ip, 40, inet_ntoa(*addr_list[i]), 40);
break; break;
} }
@ -179,7 +180,9 @@ PlatformStatusCode platform_connect(MQTTHandle *handle) {
if (free_handle) { if (free_handle) {
mqtt_free(handle); mqtt_free(handle);
} }
DEBUG_LOG("Resolving hostname failed: %s", strerror(errno)); char err_msg[200];
strerror_s(err_msg, 200, errno);
DEBUG_LOG("Resolving hostname failed: %s", err_msg);
return PlatformStatusError; return PlatformStatusError;
} }
servaddr.sin_family = AF_INET; servaddr.sin_family = AF_INET;
@ -191,7 +194,9 @@ PlatformStatusCode platform_connect(MQTTHandle *handle) {
if (free_handle) { if (free_handle) {
mqtt_free(handle); mqtt_free(handle);
} }
DEBUG_LOG("Resolving hostname failed: %s", strerror(errno)); char err_msg[200];
strerror_s(err_msg, 200, errno);
DEBUG_LOG("Resolving hostname failed: %s", err_msg);
closesocket(p->sock); closesocket(p->sock);
return PlatformStatusError; return PlatformStatusError;
} }
@ -203,7 +208,9 @@ PlatformStatusCode platform_connect(MQTTHandle *handle) {
if (free_handle) { if (free_handle) {
mqtt_free(handle); mqtt_free(handle);
} }
DEBUG_LOG("Connection failed: %s", strerror(errno)); char err_msg[200];
strerror_s(err_msg, 200, errno);
DEBUG_LOG("Connection failed: %s", err_msg);
closesocket(p->sock); closesocket(p->sock);
return PlatformStatusError; return PlatformStatusError;
} }
@ -215,7 +222,7 @@ PlatformStatusCode platform_read(MQTTHandle *handle, Buffer *buffer) {
PlatformData *p = handle->platform; PlatformData *p = handle->platform;
while (1) { while (1) {
int num_bytes = recv(p->sock, &buffer->data[buffer->position], buffer_free_space(buffer), 0); int num_bytes = recv(p->sock, &buffer->data[buffer->position], (int)buffer_free_space(buffer), 0);
if (num_bytes == 0) { if (num_bytes == 0) {
/* Socket closed, coordinated shutdown */ /* Socket closed, coordinated shutdown */
DEBUG_LOG("Socket closed"); DEBUG_LOG("Socket closed");
@ -238,7 +245,7 @@ PlatformStatusCode platform_write(MQTTHandle *handle, Buffer *buffer) {
PlatformData *p = handle->platform; PlatformData *p = handle->platform;
while (!buffer_eof(buffer)) { while (!buffer_eof(buffer)) {
int bytes = send(p->sock, buffer->data + buffer->position, buffer_free_space(buffer), 0); int bytes = send(p->sock, buffer->data + buffer->position, (int)buffer_free_space(buffer), 0);
if (bytes <= 0) { if (bytes <= 0) {
return PlatformStatusError; return PlatformStatusError;
} }

View file

@ -70,7 +70,8 @@ static inline void parse_packet(MQTTHandle *handle, MQTTPacket *packet) {
} }
} }
static void *_reader(MQTTHandle *handle) { PlatformTaskFunc(_reader) {
MQTTHandle *handle = (MQTTHandle *)context;
Buffer *buffer = buffer_allocate(max_receive_buffer_size); Buffer *buffer = buffer_allocate(max_receive_buffer_size);
handle->reader_alive = true; handle->reader_alive = true;
@ -79,7 +80,7 @@ static void *_reader(MQTTHandle *handle) {
PlatformStatusCode ret = platform_read(handle, buffer); PlatformStatusCode ret = platform_read(handle, buffer);
if (ret == PlatformStatusError) { if (ret == PlatformStatusError) {
handle->reader_alive = false; handle->reader_alive = false;
return NULL; return 0;
} }
while (1) { while (1) {
@ -100,7 +101,7 @@ static void *_reader(MQTTHandle *handle) {
platform_disconnect(handle); platform_disconnect(handle);
handle->reader_alive = false; handle->reader_alive = false;
buffer_release(buffer); buffer_release(buffer);
return NULL; return 0;
} }
} else { } else {
hexdump(buffer->data, num_bytes, 2); hexdump(buffer->data, num_bytes, 2);

View file

@ -14,12 +14,19 @@
#define LIBMQTT_VERSION_MAJOR @LIBMQTT_VERSION_MAJOR@ #define LIBMQTT_VERSION_MAJOR @LIBMQTT_VERSION_MAJOR@
#define LIBMQTT_VERSION_MINOR @LIBMQTT_VERSION_MINOR@ #define LIBMQTT_VERSION_MINOR @LIBMQTT_VERSION_MINOR@
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#if MSVC
#define API __declspec(dllexport)
#else
#define API
#endif
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#ifndef _unused
#define _unused __attribute__((unused))
#endif
typedef struct _MQTTHandle MQTTHandle; typedef struct _MQTTHandle MQTTHandle;
@ -84,7 +91,7 @@ typedef void (*MQTTPublishEventHandler)(MQTTHandle *handle, char *topic, char *p
* the handler is in charge of freeing the handle by returning true * the handler is in charge of freeing the handle by returning true
* or re-trying by changing settings and calling mqtt_reconnect() and returning false * or re-trying by changing settings and calling mqtt_reconnect() and returning false
*/ */
MQTTHandle *mqtt_connect(MQTTConfig *config, MQTTEventHandler callback, void *callback_context, MQTTErrorHandler error_callback); API MQTTHandle *mqtt_connect(MQTTConfig *config, MQTTEventHandler callback, void *callback_context, MQTTErrorHandler error_callback);
/** /**
* Re-Connect to MQTT broker * Re-Connect to MQTT broker
@ -100,7 +107,7 @@ MQTTHandle *mqtt_connect(MQTTConfig *config, MQTTEventHandler callback, void *ca
* @param callback_context: Context pointer for the callback * @param callback_context: Context pointer for the callback
* @returns: Status code * @returns: Status code
*/ */
MQTTStatus mqtt_reconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context); API MQTTStatus mqtt_reconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context);
/** /**
* Subscribe to a topic * Subscribe to a topic
@ -111,7 +118,7 @@ MQTTStatus mqtt_reconnect(MQTTHandle *handle, MQTTEventHandler callback, void *c
* @param callback: Callback function to call when receiving something for that topic * @param callback: Callback function to call when receiving something for that topic
* @returns: Status code * @returns: Status code
*/ */
MQTTStatus mqtt_subscribe(MQTTHandle *handle, char *topic, MQTTQosLevel qos_level, MQTTPublishEventHandler callback); API MQTTStatus mqtt_subscribe(MQTTHandle *handle, char *topic, MQTTQosLevel qos_level, MQTTPublishEventHandler callback);
/** /**
* Un-Subscribe from a topic * Un-Subscribe from a topic
@ -120,7 +127,7 @@ MQTTStatus mqtt_subscribe(MQTTHandle *handle, char *topic, MQTTQosLevel qos_leve
* @param topic: Topic to unsubscribe * @param topic: Topic to unsubscribe
* @returns: Status code * @returns: Status code
*/ */
MQTTStatus mqtt_unsubscribe(MQTTHandle *handle, char *topic); API MQTTStatus mqtt_unsubscribe(MQTTHandle *handle, char *topic);
/** /**
* Publish something to the broker * Publish something to the broker
@ -132,7 +139,7 @@ MQTTStatus mqtt_unsubscribe(MQTTHandle *handle, char *topic);
* @param callback: finish callback * @param callback: finish callback
* @returns: Status code * @returns: Status code
*/ */
MQTTStatus mqtt_publish(MQTTHandle *handle, char *topic, char *payload, MQTTQosLevel qos_level, MQTTPublishEventHandler callback); API MQTTStatus mqtt_publish(MQTTHandle *handle, char *topic, char *payload, MQTTQosLevel qos_level, MQTTPublishEventHandler callback);
/** /**
* Disconnect from MQTT broker * Disconnect from MQTT broker
@ -143,6 +150,10 @@ MQTTStatus mqtt_publish(MQTTHandle *handle, char *topic, char *payload, MQTTQosL
* @attention: do not use the handle after calling this function, * @attention: do not use the handle after calling this function,
* all resources will be freed, this handle is now invalid! * all resources will be freed, this handle is now invalid!
*/ */
MQTTStatus mqtt_disconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context); API MQTTStatus mqtt_disconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* mqtt_h__included */ #endif /* mqtt_h__included */

View file

@ -1,33 +0,0 @@
SRCS=encode_packet.c decode_packet.c connect_publish.c connect_subscribe.c
OBJS=$(SRCS:%.c=%.o)
COVERAGE_FILES=$(SRCS:%.c=%.gcno) $(SRCS:%.c=%.gcda)
TARGETS=$(SRCS:%.c=%.test)
COVERAGE_FLAGS=-fprofile-arcs -ftest-coverage
DEBUG_FLAGS=-DDEBUG=1
CC=gcc
CFLAGS=-g -Os -Wall -I.. -I../src -I../platform $(DEBUG_FLAGS) $(COVERAGE_FLAGS)
LDFLAGS=
LIBS=-L.. -lmqtt-debug -lpthread
all: $(TARGETS)
%.test: %.o
$(CC) $(COVERAGE_FLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
./$@
rm $@
%.o: %.c test.h
$(CC) $(CFLAGS) -o $@ -c $<
%.e: %.c test.h
$(CC) $(CFLAGS) -E -o $@ -c $<
less $@
rm $@
clean:
rm -f $(TARGETS)
rm -f $(OBJS)
rm -f $(COVERAGE_FILES)
rm -f *.e