From 79261d6bd51bc3872fc199b5818ea9e168d8ba40 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 11 Nov 2016 16:33:23 -0800 Subject: [PATCH 1/7] Add websocket support, twin and method to Python. Fix Java MQTT reconnection timeout bug --- .gitignore | 2 + c/build_all/windows/build_client.cmd | 3 +- c/configs/azure_iot_sdksFunctions.cmake | 3 + .../transport/mqtt/MqttIotHubConnection.java | 2 +- python/build_all/windows/build_client.cmd | 24 ++ .../iothub_client_python/CMakeLists.txt | 4 + .../iothub_client_python/src/CMakeLists.txt | 47 +++- .../src/iothub_client_python.cpp | 258 +++++++++++++++++- .../test/iothub_client_mock.cpp | 72 ++++- .../windows/iothub_client.def | 3 + .../windows/iothub_client_ws.def | 68 +++++ python/device/samples/iothub_client_args.py | 36 ++- python/device/samples/iothub_client_sample.py | 53 +++- .../samples/iothub_client_sample_class.py | 49 +++- python/device/tests/iothub_client_ut.py | 164 +++++++++-- 15 files changed, 720 insertions(+), 68 deletions(-) create mode 100644 python/device/iothub_client_python/windows/iothub_client_ws.def diff --git a/.gitignore b/.gitignore index 160428109ef..b8f6fca8380 100644 --- a/.gitignore +++ b/.gitignore @@ -239,3 +239,5 @@ javawrapper/device/samples/file-upload-sample/target javawrapper/device/samples/send-event-sample/target javawrapper/device/test/target javawrapper/device/samples/target +*.so +python/build_all/windows/pyenv.bat diff --git a/c/build_all/windows/build_client.cmd b/c/build_all/windows/build_client.cmd index e48861b0fec..dd2e1b1cca6 100644 --- a/c/build_all/windows/build_client.cmd +++ b/c/build_all/windows/build_client.cmd @@ -103,7 +103,7 @@ if %build-platform% == Win32 ( if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) else ( echo ***Running CMAKE for Win64*** - cmake %build-root% -G "Visual Studio 14 Win64" -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -G "Visual Studio 14 Win64" -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) @@ -130,4 +130,5 @@ echo --config ^ [Debug] build configuration (e.g. Debug, Releas echo --platform ^ [Win32] build platform (e.g. Win32, x64, ...) echo --buildpython ^ [2.7] build python extension (e.g. 2.7, 3.4, ...) echo --no-logging Disable logging +echo --use-websockets Enable websocket support for AMQP and MQTT goto :eof diff --git a/c/configs/azure_iot_sdksFunctions.cmake b/c/configs/azure_iot_sdksFunctions.cmake index eda252489ab..a67f078fbfd 100644 --- a/c/configs/azure_iot_sdksFunctions.cmake +++ b/c/configs/azure_iot_sdksFunctions.cmake @@ -16,6 +16,9 @@ function(linkUAMQP whatExecutableIsBuilding) file(COPY $ENV{OpenSSLDir}/bin/libeay32.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug) file(COPY $ENV{OpenSSLDir}/bin/ssleay32.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug) + + file(COPY $ENV{OpenSSLDir}/bin/libeay32.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Release) + file(COPY $ENV{OpenSSLDir}/bin/ssleay32.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Release) endif() else() target_link_libraries(${whatExecutableIsBuilding} uamqp aziotsharedutil ssl crypto) diff --git a/java/device/iothub-java-client/src/main/java/com/microsoft/azure/iothub/transport/mqtt/MqttIotHubConnection.java b/java/device/iothub-java-client/src/main/java/com/microsoft/azure/iothub/transport/mqtt/MqttIotHubConnection.java index 68d440e22a3..9ec34881e72 100644 --- a/java/device/iothub-java-client/src/main/java/com/microsoft/azure/iothub/transport/mqtt/MqttIotHubConnection.java +++ b/java/device/iothub-java-client/src/main/java/com/microsoft/azure/iothub/transport/mqtt/MqttIotHubConnection.java @@ -291,7 +291,7 @@ public void connectionLost(Throwable throwable) // Codes_SRS_MQTTIOTHUBCONNECTION_15_018: [The maximum wait interval // until a reconnect is attempted shall be 60 seconds.] - Thread.sleep(TransportUtils.generateSleepInterval(currentReconnectionAttempt) * 1000); + Thread.sleep(TransportUtils.generateSleepInterval(currentReconnectionAttempt)); } catch (InterruptedException exception) { // do nothing, reconnection attempts will continue diff --git a/python/build_all/windows/build_client.cmd b/python/build_all/windows/build_client.cmd index 9ef857465ff..d07053983c4 100644 --- a/python/build_all/windows/build_client.cmd +++ b/python/build_all/windows/build_client.cmd @@ -27,6 +27,7 @@ set build-config=Release set build-python=2.7 set wheel=0 set platname=win32 +set use-websockets=OFF python python_version_check.py >pyenv.bat if errorlevel 1 goto :NeedPython @@ -43,6 +44,8 @@ exit /b 1 if "%1" equ "" goto args-done if "%1" equ "--config" goto arg-build-config if "%1" equ "--wheel" goto arg-build-wheel +if "%1" equ "--use-websockets" goto arg-use-websockets + call :usage && exit /b 1 :arg-build-config @@ -55,6 +58,10 @@ goto args-continue set wheel=1 goto args-continue +:arg-use-websockets +set use-websockets=ON +goto args-continue + :args-continue shift goto args-loop @@ -67,7 +74,13 @@ set cmake-output=cmake_%build-platform% REM -- C -- cd %build-root%..\..\..\c\build_all\windows + +if %use-websockets% == ON ( +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets +) else ( call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% +) + if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! cd %build-root% @@ -110,3 +123,14 @@ if %wheel%==1 ( dir dist echo Yet another Python wheel done ) +goto :eof + +:usage +echo build_client.cmd [options] +echo options: +echo --config ^ [Debug] build configuration (e.g. Debug, Release) +echo --platform ^ [Win32] build platform (e.g. Win32, x64, ...) +echo --buildpython ^ [2.7] build python extension (e.g. 2.7, 3.4, ...) +echo --no-logging Disable logging +echo --use-websockets Enable websocket support for AMQP and MQTT +goto :eof diff --git a/python/device/iothub_client_python/CMakeLists.txt b/python/device/iothub_client_python/CMakeLists.txt index d34747b738c..d82b2859891 100644 --- a/python/device/iothub_client_python/CMakeLists.txt +++ b/python/device/iothub_client_python/CMakeLists.txt @@ -75,5 +75,9 @@ if(${use_mqtt}) include_directories(${IOTHUB_CLIENT_MQTT_TRANSPORT_INC_FOLDER} ${MQTT_INC_FOLDER}) endif() +if(${use_wsio}) + add_definitions(-DUSE_WEBSOCKETS) +endif() + add_subdirectory(src) add_subdirectory(test) diff --git a/python/device/iothub_client_python/src/CMakeLists.txt b/python/device/iothub_client_python/src/CMakeLists.txt index 3daa3bd5202..19deaf968a7 100644 --- a/python/device/iothub_client_python/src/CMakeLists.txt +++ b/python/device/iothub_client_python/src/CMakeLists.txt @@ -7,9 +7,12 @@ set(iothub_client_python_c_files ./iothub_client_python.cpp ) - if(WIN32) - set(iothub_client_python_c_files ${iothub_client_python_c_files} ../windows/dllmain.c ../windows/iothub_client.def) + if(${use_wsio}) + set(iothub_client_python_c_files ${iothub_client_python_c_files} ../windows/dllmain.c ../windows/iothub_client_ws.def) + else() + set(iothub_client_python_c_files ${iothub_client_python_c_files} ../windows/dllmain.c ../windows/iothub_client.def) + endif() endif() include_directories(.) @@ -25,16 +28,36 @@ IF(WIN32) SET_TARGET_PROPERTIES(iothub_client_python PROPERTIES SUFFIX ".pyd") ENDIF(WIN32) -target_link_libraries( - iothub_client_python - iothub_client_mqtt_transport - iothub_client_http_transport - iothub_client_amqp_transport - iothub_client - uamqp - ${Boost_LIBRARIES} - ${PYTHON_LIBRARIES} -) +if(${use_wsio}) + target_link_libraries( + iothub_client_python + iothub_client_http_transport + iothub_client_amqp_transport + iothub_client_amqp_ws_transport + iothub_client_mqtt_transport + iothub_client_mqtt_ws_transport + iothub_client + uamqp + ${Boost_LIBRARIES} + ${PYTHON_LIBRARIES} + ) +else() + target_link_libraries( + iothub_client_python + iothub_client_http_transport + iothub_client_amqp_transport + iothub_client_mqtt_transport + iothub_client + uamqp + ${Boost_LIBRARIES} + ${PYTHON_LIBRARIES} + ) +endif() + +if(${use_wsio} AND WIN32) + file(COPY $ENV{OpenSSLDir}/bin/libeay32.dll DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../../samples) + file(COPY $ENV{OpenSSLDir}/bin/ssleay32.dll DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../../samples) +endif() linkSharedUtil(iothub_client_python) linkUAMQP(iothub_client_python) diff --git a/python/device/iothub_client_python/src/iothub_client_python.cpp b/python/device/iothub_client_python/src/iothub_client_python.cpp index 968f54f76d7..2fbb69f09ca 100644 --- a/python/device/iothub_client_python/src/iothub_client_python.cpp +++ b/python/device/iothub_client_python/src/iothub_client_python.cpp @@ -25,6 +25,11 @@ #include "iothubtransportamqp.h" #include "iothubtransportmqtt.h" +#ifdef USE_WEBSOCKETS +#include "iothubtransportamqp_websockets.h" +#include "iothubtransportmqtt_websockets.h" +#endif + #ifndef IMPORT_NAME #define IMPORT_NAME iothub_client #endif @@ -85,10 +90,17 @@ class ScopedGILRelease // iothubtransportxxxx.h // -enum IOTHUB_TRANSPORT_PROVIDER -{ - HTTP, AMQP, MQTT -}; +#ifdef USE_WEBSOCKETS + enum IOTHUB_TRANSPORT_PROVIDER + { + HTTP, AMQP, MQTT, AMQP_WS, MQTT_WS + }; +#else + enum IOTHUB_TRANSPORT_PROVIDER + { + HTTP, AMQP, MQTT + }; +#endif // // IotHubError exception handler base class @@ -828,6 +840,137 @@ ReceiveMessageCallback( return boost::python::extract(returnObject); } +typedef struct +{ + boost::python::object deviceTwinCallback; + boost::python::object userContext; +} DeviceTwinContext; + +extern "C" +void +DeviceTwinCallback( + DEVICE_TWIN_UPDATE_STATE updateState, + const unsigned char* payLoad, + size_t size, + void* userContextCallback + ) +{ + DeviceTwinContext *deviceTwinContext = (DeviceTwinContext *)userContextCallback; + boost::python::object userContext = deviceTwinContext->userContext; + boost::python::object deviceTwinCallback = deviceTwinContext->deviceTwinCallback; + + std::string payload_std_string(reinterpret_cast(payLoad), size); + + ScopedGILAcquire acquire; + try + { + deviceTwinCallback(updateState, payload_std_string, userContext); + } + catch (const boost::python::error_already_set) + { + // Catch and ignore exception that is thrown in Python callback. + // There is nothing we can do about it here. + PyErr_Print(); + } +} + +typedef struct +{ + boost::python::object sendReportedStateCallback; + boost::python::object userContext; +} SendReportedStateContext; + +extern "C" +void +SendReportedStateCallback( + int status_code, + void* userContextCallback + ) +{ + (void*)userContextCallback; + + SendReportedStateContext *sendReportedStateContext = (SendReportedStateContext *)userContextCallback; + boost::python::object userContext = sendReportedStateContext->userContext; + boost::python::object sendReportedStateCallback = sendReportedStateContext->sendReportedStateCallback; + + ScopedGILAcquire acquire; + try + { + sendReportedStateCallback(status_code, userContext); + } + catch (const boost::python::error_already_set) + { + // Catch and ignore exception that is thrown in Python callback. + // There is nothing we can do about it here. + PyErr_Print(); + } +} + +typedef struct +{ + boost::python::object deviceMethodCallback; + boost::python::object userContext; +} DeviceMethodContext; + +class DeviceMethodReturnValue +{ +public: + std::string response; + int status; +}; + +extern "C" +int +DeviceMethodCallback( + const char* method_name, + const unsigned char* payLoad, + size_t size, + unsigned char** response, + size_t* resp_size, + void* userContextCallback + ) +{ + int retVal = -1; + + DeviceMethodContext *deviceMethodContext = (DeviceMethodContext *)userContextCallback; + boost::python::object userContext = deviceMethodContext->userContext; + boost::python::object deviceMethodCallback = deviceMethodContext->deviceMethodCallback; + + std::string method_name_std_string(method_name, sizeof(method_name)); + std::string payload_std_string(reinterpret_cast(payLoad), size); + + ScopedGILAcquire acquire; + try + { + boost::python::object user_response_obj = deviceMethodCallback(method_name_std_string, payload_std_string, userContext); + + DeviceMethodReturnValue user_response_value = boost::python::extract(user_response_obj)(); + + retVal = user_response_value.status; + + printf("Response: %s", user_response_value.response.c_str()); + printf("Status: %d", user_response_value.status); + + *resp_size = strlen(user_response_value.response.c_str()); + if ((*response = (unsigned char*)malloc(*resp_size)) == NULL) + { + *resp_size = 0; + } + else + { + memcpy(*response, user_response_value.response.c_str(), *resp_size); + } + } + catch (const boost::python::error_already_set) + { + // Catch and ignore exception that is thrown in Python callback. + // There is nothing we can do about it here. + PyErr_Print(); + } + + return retVal; +} + typedef struct { boost::python::object blobUploadCallback; @@ -877,6 +1020,14 @@ class IoTHubClient case MQTT: protocol = MQTT_Protocol; break; +#ifdef USE_WEBSOCKETS + case AMQP_WS: + protocol = AMQP_Protocol_over_WebSocketsTls; + break; + case MQTT_WS: + protocol = MQTT_WebSocket_Protocol; + break; +#endif default: PyErr_SetString(PyExc_TypeError, "IoTHubTransportProvider set to unknown protocol"); boost::python::throw_error_already_set(); @@ -1060,6 +1211,84 @@ class IoTHubClient } } + void SetDeviceTwinCallback( + boost::python::object& deviceTwinCallback, + boost::python::object& userContext + ) + { + if (!PyCallable_Check(deviceTwinCallback.ptr())) + { + PyErr_SetString(PyExc_TypeError, "set_device_twin_callback expected type callable"); + boost::python::throw_error_already_set(); + return; + } + DeviceTwinContext *deviceTwinContext = new DeviceTwinContext(); + deviceTwinContext->deviceTwinCallback = deviceTwinCallback; + deviceTwinContext->userContext = userContext; + IOTHUB_CLIENT_RESULT result; + { + ScopedGILRelease release; + result = IoTHubClient_SetDeviceTwinCallback(iotHubClientHandle, DeviceTwinCallback, deviceTwinContext); + } + if (result != IOTHUB_CLIENT_OK) + { + throw IoTHubClientError(__func__, result); + } + } + + void SendReportedState( + std::string reportedState, + size_t size, + boost::python::object& reportedStateCallback, + boost::python::object& userContext + ) + { + if (!PyCallable_Check(reportedStateCallback.ptr())) + { + PyErr_SetString(PyExc_TypeError, "send_reported_state expected type callable"); + boost::python::throw_error_already_set(); + return; + } + SendReportedStateContext *sendReportedStateContext = new SendReportedStateContext(); + sendReportedStateContext->sendReportedStateCallback = reportedStateCallback; + sendReportedStateContext->userContext = userContext; + + IOTHUB_CLIENT_RESULT result; + { + ScopedGILRelease release; + result = IoTHubClient_SendReportedState(iotHubClientHandle, (const unsigned char*)reportedState.c_str(), size, SendReportedStateCallback, sendReportedStateContext); + } + if (result != IOTHUB_CLIENT_OK) + { + throw IoTHubClientError(__func__, result); + } + } + + void SetDeviceMethodCallback( + boost::python::object& deviceMethodCallback, + boost::python::object& userContext + ) + { + if (!PyCallable_Check(deviceMethodCallback.ptr())) + { + PyErr_SetString(PyExc_TypeError, "set_device_method_callback expected type callable"); + boost::python::throw_error_already_set(); + return; + } + DeviceMethodContext *deviceMethodContext = new DeviceMethodContext(); + deviceMethodContext->deviceMethodCallback = deviceMethodCallback; + deviceMethodContext->userContext = userContext; + IOTHUB_CLIENT_RESULT result; + { + ScopedGILRelease release; + result = IoTHubClient_SetDeviceMethodCallback(iotHubClientHandle, DeviceMethodCallback, deviceMethodContext); + } + if (result != IOTHUB_CLIENT_OK) + { + throw IoTHubClientError(__func__, result); + } + } + time_t GetLastMessageReceiveTime() { time_t lastMessageReceiveTime; @@ -1275,10 +1504,19 @@ BOOST_PYTHON_MODULE(IMPORT_NAME) .value("UNKNOWN", IOTHUBMESSAGE_UNKNOWN) ; + enum_("IoTHubTwinUpdateState") + .value("COMPLETE", DEVICE_TWIN_UPDATE_COMPLETE) + .value("PARTIAL", DEVICE_TWIN_UPDATE_PARTIAL) + ; + enum_("IoTHubTransportProvider") .value("HTTP", HTTP) .value("AMQP", AMQP) .value("MQTT", MQTT) +#ifdef USE_WEBSOCKETS + .value("AMQP_WS", AMQP_WS) + .value("MQTT_WS", MQTT_WS) +#endif ; enum_("IoTHubClientFileUploadResult") @@ -1320,10 +1558,22 @@ BOOST_PYTHON_MODULE(IMPORT_NAME) #endif ; + class_("DeviceMethodReturnValue") + .def_readwrite("response", &DeviceMethodReturnValue::response) + .def_readwrite("status", &DeviceMethodReturnValue::status) +#ifdef SUPPORT___STR__ + .def("__str__", &DeviceMethodReturnValue::str) + .def("__repr__", &DeviceMethodReturnValue::repr) +#endif + ; + class_("IoTHubClient", no_init) .def(init()) .def("send_event_async", &IoTHubClient::SendEventAsync) .def("set_message_callback", &IoTHubClient::SetMessageCallback) + .def("set_device_twin_callback", &IoTHubClient::SetDeviceTwinCallback) + .def("send_reported_state", &IoTHubClient::SendReportedState) + .def("set_device_method_callback", &IoTHubClient::SetDeviceMethodCallback) .def("set_option", &IoTHubClient::SetOption) .def("get_send_status", &IoTHubClient::GetSendStatus) .def("get_last_message_receive_time", &IoTHubClient::GetLastMessageReceiveTime) diff --git a/python/device/iothub_client_python/test/iothub_client_mock.cpp b/python/device/iothub_client_python/test/iothub_client_mock.cpp index 20869b939a7..a90004932c2 100644 --- a/python/device/iothub_client_python/test/iothub_client_mock.cpp +++ b/python/device/iothub_client_python/test/iothub_client_mock.cpp @@ -13,6 +13,8 @@ #pragma warning(pop) #endif +#pragma warning(disable:4996) /* This is because of we need strncpy */ + #include #include #include @@ -25,6 +27,11 @@ #include "iothubtransportamqp.h" #include "iothubtransportmqtt.h" +#ifdef USE_WEBSOCKETS +#include "iothubtransportamqp_websockets.h" +#include "iothubtransportmqtt_websockets.h" +#endif + #define IMPORT_NAME iothub_client_mock // "platform.h" @@ -207,6 +214,24 @@ IOTHUB_CLIENT_RESULT IoTHubClient_SetMessageCallback(IOTHUB_CLIENT_HANDLE iotHub return IOTHUB_CLIENT_OK; } +IOTHUB_CLIENT_RESULT IoTHubClient_SetDeviceTwinCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + (void)iotHubClientHandle, deviceTwinCallback, userContextCallback; + return IOTHUB_CLIENT_OK; +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SendReportedState(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const unsigned char* reportedState, size_t size , IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reportedStateCallback, void* userContextCallback) +{ + (void)iotHubClientHandle, reportedState, size, reportedStateCallback, userContextCallback; + return IOTHUB_CLIENT_OK; +} + +IOTHUB_CLIENT_RESULT IoTHubClient_SetDeviceMethodCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC deviceMethodCallback, void* userContextCallback) +{ + (void)iotHubClientHandle, deviceMethodCallback, userContextCallback; + return IOTHUB_CLIENT_OK; +} + IOTHUB_CLIENT_RESULT IoTHubClient_GetLastMessageReceiveTime(IOTHUB_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime) { (void)iotHubClientHandle, lastMessageReceiveTime; @@ -338,22 +363,41 @@ void IoTHubMessage_Destroy(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle) // "iothubtransporthttp.h" TRANSPORT_PROVIDER *mockProtocol = (TRANSPORT_PROVIDER *)0x12345678; -const TRANSPORT_PROVIDER* HTTP_Protocol(void) -{ - return mockProtocol; -} +extern "C" { + const TRANSPORT_PROVIDER* HTTP_Protocol(void) + { + return mockProtocol; + } -// "iothubtransportamqp.h" + // "iothubtransportamqp.h" -const TRANSPORT_PROVIDER* AMQP_Protocol(void) -{ - return mockProtocol; -} + const TRANSPORT_PROVIDER* AMQP_Protocol(void) + { + return mockProtocol; + } -// "iothubtransportmqtt.h" + // "iothubtransportmqtt.h" -const TRANSPORT_PROVIDER* MQTT_Protocol(void) -{ - return mockProtocol; -} + const TRANSPORT_PROVIDER* MQTT_Protocol(void) + { + return mockProtocol; + } + +#ifdef USE_WEBSOCKETS + + // "iothubtransportamqp_webscokets.h" + + const TRANSPORT_PROVIDER* AMQP_Protocol_over_WebSocketsTls(void) + { + return mockProtocol; + } + + // "iothubtransportmqtt_websockets.h" + + const TRANSPORT_PROVIDER* MQTT_WebSocket_Protocol(void) + { + return mockProtocol; + } +#endif +}/*extern "C"*/ diff --git a/python/device/iothub_client_python/windows/iothub_client.def b/python/device/iothub_client_python/windows/iothub_client.def index 19118c6865d..ffdeaee020e 100644 --- a/python/device/iothub_client_python/windows/iothub_client.def +++ b/python/device/iothub_client_python/windows/iothub_client.def @@ -15,6 +15,9 @@ EXPORTS IoTHubClient_SetMessageCallback IoTHubClient_GetLastMessageReceiveTime IoTHubClient_SetOption + IoTHubClient_SetDeviceTwinCallback + IoTHubClient_SendReportedState + IoTHubClient_SetDeviceMethodCallback IoTHubClient_UploadToBlobAsync ; iothub_client_ll.h IoTHubClient_LL_CreateFromConnectionString diff --git a/python/device/iothub_client_python/windows/iothub_client_ws.def b/python/device/iothub_client_python/windows/iothub_client_ws.def new file mode 100644 index 00000000000..6719dfda5c9 --- /dev/null +++ b/python/device/iothub_client_python/windows/iothub_client_ws.def @@ -0,0 +1,68 @@ +; Copyright(c) Microsoft.All rights reserved. +; Licensed under the MIT license.See LICENSE file in the project root for full license information. +; IotHub_Client_Python.def : Declares the module parameters. +; LIBRARY "IotHub_Client.PYD" + +EXPORTS +; version.h + IoTHubClient_GetVersionString +; iothub_client.h + IoTHubClient_CreateFromConnectionString + IoTHubClient_Create + IoTHubClient_Destroy + IoTHubClient_SendEventAsync + IoTHubClient_GetSendStatus + IoTHubClient_SetMessageCallback + IoTHubClient_GetLastMessageReceiveTime + IoTHubClient_SetOption + IoTHubClient_SetDeviceTwinCallback + IoTHubClient_SendReportedState + IoTHubClient_SetDeviceMethodCallback + IoTHubClient_UploadToBlobAsync +; iothub_client_ll.h + IoTHubClient_LL_CreateFromConnectionString + IoTHubClient_LL_Create + IoTHubClient_LL_Destroy + IoTHubClient_LL_SendEventAsync + IoTHubClient_LL_GetSendStatus + IoTHubClient_LL_SetMessageCallback + IoTHubClient_LL_GetLastMessageReceiveTime + IoTHubClient_LL_DoWork + IoTHubClient_LL_SetOption +; iothub_message.h + IoTHubMessage_CreateFromByteArray + IoTHubMessage_CreateFromString + IoTHubMessage_Clone + IoTHubMessage_GetByteArray + IoTHubMessage_GetString + IoTHubMessage_GetContentType + IoTHubMessage_Properties + IoTHubMessage_GetMessageId + IoTHubMessage_SetMessageId + IoTHubMessage_GetCorrelationId + IoTHubMessage_SetCorrelationId + IoTHubMessage_Destroy +; iothubtransporthttp.h + HTTP_Protocol +; iothubtransportamqp.h + AMQP_Protocol +; iothubtransportmqtt.h + MQTT_Protocol +; iothubtransportamqp_websockets.h + AMQP_Protocol_over_WebSocketsTls +; iothubtransportmqtt_websocket.h + MQTT_WebSocket_Protocol +; map.h (c-utility) + Map_Create + Map_Destroy + Map_Clone + Map_Add + Map_AddOrUpdate + Map_Delete + Map_ContainsKey + Map_ContainsValue + Map_GetValueFromKey + Map_GetInternals +; platform.h + platform_init + platform_deinit diff --git a/python/device/samples/iothub_client_args.py b/python/device/samples/iothub_client_args.py index ba5745a82af..00e689f74ad 100644 --- a/python/device/samples/iothub_client_args.py +++ b/python/device/samples/iothub_client_args.py @@ -18,7 +18,7 @@ def __str__(self): def get_iothub_opt( argv, connection_string, - protocol=IoTHubTransportProvider.AMQP): + protocol=IoTHubTransportProvider.MQTT): if len(argv) > 0: try: opts, args = getopt.getopt( @@ -31,12 +31,36 @@ def get_iothub_opt( raise OptionError("Help:") elif opt in ("-p", "--protocol"): protocol_string = arg.lower() - if (protocol_string == "amqp"): - protocol = IoTHubTransportProvider.AMQP + + if (protocol_string == "http"): + if hasattr(IoTHubTransportProvider, "HTTP"): + protocol = IoTHubTransportProvider.HTTP + else: + raise OptionError("Error: HTTP protocol is not supported") + + elif (protocol_string == "amqp"): + if hasattr(IoTHubTransportProvider, "AMQP"): + protocol = IoTHubTransportProvider.AMQP + else: + raise OptionError("Error: AMQP protocol is not supported") + + elif (protocol_string == "amqp_ws"): + if hasattr(IoTHubTransportProvider, "AMQP_WS"): + protocol = IoTHubTransportProvider.AMQP_WS + else: + raise OptionError("Error: AMQP_WS protocol is not supported") + elif (protocol_string == "mqtt"): - protocol = IoTHubTransportProvider.MQTT - elif (protocol_string == "http"): - protocol = IoTHubTransportProvider.HTTP + if hasattr(IoTHubTransportProvider, "MQTT"): + protocol = IoTHubTransportProvider.MQTT + else: + raise OptionError("Error: MQTT protocol is not supported") + + elif hasattr(IoTHubTransportProvider, "MQTT_WS"): + if hasattr(IoTHubTransportProvider, "MQTT_WS"): + protocol = IoTHubTransportProvider.MQTT_WS + else: + raise OptionError("Error: MQTT_WS protocol is not supported") else: raise OptionError( "Error: unknown protocol %s" % diff --git a/python/device/samples/iothub_client_sample.py b/python/device/samples/iothub_client_sample.py index 360d6f55b72..4743260c700 100755 --- a/python/device/samples/iothub_client_sample.py +++ b/python/device/samples/iothub_client_sample.py @@ -29,14 +29,20 @@ avg_wind_speed = 10.0 message_count = 5 received_count = 0 +twin_context = 0 +send_reported_sate_context = 0 +method_context = 0 # global counters receive_callbacks = 0 send_callbacks = 0 blob_callbacks = 0 +twin_callbacks = 0 +send_reported_state_callbacks = 0 +method_callbacks = 0 # chose HTTP, AMQP or MQTT as transport protocol -protocol = IoTHubTransportProvider.AMQP +protocol = IoTHubTransportProvider.MQTT # String containing Hostname, Device Id & Device Key in the format: # "HostName=;DeviceId=;SharedAccessKey=" @@ -85,6 +91,37 @@ def send_confirmation_callback(message, result, user_context): print(" Total calls confirmed: %d" % send_callbacks) +def device_twin_callback(updateState, payLoad, user_context): + global twin_callbacks + print( + "\nTwin callback called with:\nupdateStatus = %s\npayload = %s\ncontext = %s" % + (updateState, payLoad, user_context)) + twin_callbacks += 1 + print("Total calls confirmed: %d\n" % twin_callbacks) + + +def send_reported_state_callback(status_code, user_context): + global send_reported_state_callbacks + print( + "Confirmation for reported state received with:\nstatus_code = [%d]\ncontext = %s" % + (status_code, user_context)) + send_reported_state_callbacks += 1 + print(" Total calls confirmed: %d" % send_reported_state_callbacks) + + +def device_method_callback(method_name, payload, user_context): + global method_callbacks + print( + "\nMethod callback called with:\nmethodName = %s\npayload = %s\ncontext = %s" % + (method_name, payload, user_context)) + method_callbacks += 1 + print("Total calls confirmed: %d\n" % method_callbacks) + deviceMethodReturnValue = DeviceMethodReturnValue() + deviceMethodReturnValue.response = "{ \"Response\": \"This is the response from the device\" }" + deviceMethodReturnValue.status = 200 + return deviceMethodReturnValue + + def blob_upload_confirmation_callback(result, user_context): global blob_callbacks print("Blob upload confirmation[%d] received for message with result = %s" % (user_context, result)) @@ -101,12 +138,17 @@ def iothub_client_init(): # set the time until a message times out iotHubClient.set_option("messageTimeout", message_timeout) # some embedded platforms need certificate information - # set_certificates(iotHubClient) + set_certificates(iotHubClient) # to enable MQTT logging set to 1 if iotHubClient.protocol == IoTHubTransportProvider.MQTT: iotHubClient.set_option("logtrace", 0) iotHubClient.set_message_callback( receive_message_callback, receive_context) + if iotHubClient.protocol == IoTHubTransportProvider.MQTT: + iotHubClient.set_device_twin_callback( + device_twin_callback, twin_context) + iotHubClient.set_device_method_callback( + device_method_callback, method_context) return iotHubClient @@ -135,6 +177,11 @@ def iothub_client_sample_run(): iotHubClient.upload_blob_async(filename, content, len(content), blob_upload_confirmation_callback, 1001) print("IoTHubClient.upload_blob_async accepted the blob to upload to IoT Hub.") + if iotHubClient.protocol == IoTHubTransportProvider.MQTT: + print("IoTHubClient is reporting state") + reportedState = "{\"newState\":\"standBy\"}" + iotHubClient.send_reported_state(reportedState, len(reportedState), send_reported_state_callback, send_reported_sate_context) + while True: # send a few messages every minute print("IoTHubClient sending %d messages" % message_count) @@ -181,7 +228,7 @@ def iothub_client_sample_run(): def usage(): print("Usage: iothub_client_sample.py -p -c ") - print(" protocol : ") + print(" protocol : ") print(" connectionstring: ;DeviceId=;SharedAccessKey=>") diff --git a/python/device/samples/iothub_client_sample_class.py b/python/device/samples/iothub_client_sample_class.py index 5fbfbf281af..8f85d9db4c8 100755 --- a/python/device/samples/iothub_client_sample_class.py +++ b/python/device/samples/iothub_client_sample_class.py @@ -29,11 +29,16 @@ avg_wind_speed = 10.0 message_count = 5 received_count = 0 +twin_context = 0 +method_context = 0 # global counters receive_callbacks = 0 send_callbacks = 0 blob_callbacks = 0 +twin_callbacks = 0 +send_reported_state_callbacks = 0 +method_callbacks = 0 # String containing Hostname, Device Id & Device Key in the format: # "HostName=;DeviceId=;SharedAccessKey=" @@ -47,7 +52,7 @@ class HubManager(object): def __init__( self, connection_string, - protocol=IoTHubTransportProvider.AMQP): + protocol=IoTHubTransportProvider.MQTT): self.client_protocol = protocol self.client = IoTHubClient(connection_string, protocol) if protocol == IoTHubTransportProvider.HTTP: @@ -58,6 +63,8 @@ def __init__( # some embedded platforms need certificate information # self.set_certificates() self.client.set_message_callback(self._receive_message_callback, receive_context) + self.client.set_device_twin_callback(self._device_twin_callback, twin_context) + self.client.set_device_method_callback(self._device_method_callback, method_context) def set_certificates(self): from iothub_client_cert import certificates @@ -94,6 +101,37 @@ def _send_confirmation_callback(self, message, result, user_context): print(" Total calls confirmed: %d" % send_callbacks) + def _device_twin_callback(updateState, payLoad, user_context): + global twin_callbacks + print( + "\nTwin callback called with:\nupdateStatus = %s\npayload = %s\ncontext = %s" % + (updateState, payLoad, user_context)) + twin_callbacks += 1 + print("Total calls confirmed: %d\n" % twin_callbacks) + + + def _send_reported_state_callback(status_code, user_context): + global send_reported_state_callbacks + print( + "Confirmation for reported state received with:\nstatus_code = [%d]\ncontext = %s" % + (status_code, user_context)) + send_reported_state_callbacks += 1 + print(" Total calls confirmed: %d" % send_reported_state_callbacks) + + + def _device_method_callback(method_name, payload, user_context): + global method_callbacks + print( + "\nMethod callback called with:\nmethodName = %s\npayload = %s\ncontext = %s" % + (method_name, payload, user_context)) + method_callbacks += 1 + print("Total calls confirmed: %d\n" % method_callbacks) + deviceMethodReturnValue = DeviceMethodReturnValue() + deviceMethodReturnValue.response = "{ \"Response\": \"This is the response from the device\" }" + deviceMethodReturnValue.status = 200 + return deviceMethodReturnValue + + def _blob_upload_confirmation_callback(self, result, user_context): global blob_callbacks print("Blob upload confirmation[%d] received for message with result = %s" % (user_context, result)) @@ -114,6 +152,12 @@ def send_event(self, event, properties, send_context): event, self._send_confirmation_callback, send_context) + def send_reported_state(self, reportedState, size, user_context): + self.client.send_reported_state( + reportedState, size + self._send_reported_state_callback, user_context) + + def upload_to_blob(self, destinationfilename, source, size, usercontext): self.client.upload_blob_async( destinationfilename, source, size, @@ -138,6 +182,9 @@ def main(connection_string, protocol): content = "Hello World from Python Blob APi" hub_manager.upload_to_blob(filename, content, len(content), 1001) + reportedState = "{\"newState\":\"standBy\"}" + hub_manager.send_reported_state(reportedState, len(reportedState), 1002) + while True: # send a few messages every minute print("IoTHubClient sending %d messages" % message_count) diff --git a/python/device/tests/iothub_client_ut.py b/python/device/tests/iothub_client_ut.py index 5a6065c6dc1..e9742e1ec6e 100755 --- a/python/device/tests/iothub_client_ut.py +++ b/python/device/tests/iothub_client_ut.py @@ -40,9 +40,23 @@ def receive_message_callback(message, counter): def send_confirmation_callback(message, result, userContext): return + +def device_twin_callback(updateState, payLoad, user_context): + return + + +def send_reported_state_callback(status_code, userContext): + return + + +def device_method_callback(updateState, payLoad, user_context): + return + + def blob_upload_callback(result, userContext): return + class TestExceptionDefinitions(unittest.TestCase): def test_IoTHubError(self): @@ -223,17 +237,22 @@ def test_IoTHubMessageContent(self): self.assertEqual(len(dispositionResult.values), lastEnum) def test_IoTHubTransportProvider(self): - self.assertEqual(IoTHubTransportProvider.HTTP, 0) - self.assertEqual(IoTHubTransportProvider.AMQP, 1) - self.assertEqual(IoTHubTransportProvider.MQTT, 2) - lastEnum = IoTHubTransportProvider.MQTT + 1 - with self.assertRaises(AttributeError): - self.assertEqual(IoTHubTransportProvider.ANY, 0) - transportProvider = IoTHubTransportProvider() - self.assertEqual(transportProvider, 0) - self.assertEqual(len(transportProvider.names), lastEnum) - self.assertEqual(len(transportProvider.values), lastEnum) - + if hasattr(IoTHubTransportProvider, "MQTT_WS"): + self.assertEqual(IoTHubTransportProvider.HTTP, 0) + self.assertEqual(IoTHubTransportProvider.AMQP, 1) + self.assertEqual(IoTHubTransportProvider.MQTT, 2) + self.assertEqual(IoTHubTransportProvider.AMQP_WS, 3) + self.assertEqual(IoTHubTransportProvider.MQTT_WS, 4) + lastEnum = IoTHubTransportProvider.MQTT_WS + 1 + with self.assertRaises(AttributeError): + self.assertEqual(IoTHubTransportProvider.ANY, 0) + else: + self.assertEqual(IoTHubTransportProvider.HTTP, 0) + self.assertEqual(IoTHubTransportProvider.AMQP, 1) + self.assertEqual(IoTHubTransportProvider.MQTT, 2) + lastEnum = IoTHubTransportProvider.MQTT + 1 + with self.assertRaises(AttributeError): + self.assertEqual(IoTHubTransportProvider.ANY, 0) class TestClassDefinitions(unittest.TestCase): @@ -489,25 +508,47 @@ def test_IoTHubClient(self): client = IoTHubClient(connectionString) with self.assertRaises(Exception): client = IoTHubClient(connectionString, 1) - client = IoTHubClient(connectionString, IoTHubTransportProvider.HTTP) - self.assertIsInstance(client, IoTHubClient) - self.assertEqual(client.protocol, IoTHubTransportProvider.HTTP) - with self.assertRaises(AttributeError): - client.protocol = IoTHubTransportProvider.AMQP - client = IoTHubClient(connectionString, IoTHubTransportProvider.AMQP) - self.assertIsInstance(client, IoTHubClient) - self.assertEqual(client.protocol, IoTHubTransportProvider.AMQP) - with self.assertRaises(AttributeError): - client.protocol = IoTHubTransportProvider.AMQP - client = IoTHubClient(connectionString, IoTHubTransportProvider.MQTT) - self.assertIsInstance(client, IoTHubClient) - self.assertEqual(client.protocol, IoTHubTransportProvider.MQTT) - with self.assertRaises(AttributeError): - client.protocol = IoTHubTransportProvider.AMQP + + if hasattr(IoTHubTransportProvider, "HTTP"): + client = IoTHubClient(connectionString, IoTHubTransportProvider.HTTP) + self.assertIsInstance(client, IoTHubClient) + self.assertEqual(client.protocol, IoTHubTransportProvider.HTTP) + with self.assertRaises(AttributeError): + client.protocol = IoTHubTransportProvider.AMQP + + if hasattr(IoTHubTransportProvider, "AMQP"): + client = IoTHubClient(connectionString, IoTHubTransportProvider.AMQP) + self.assertIsInstance(client, IoTHubClient) + self.assertEqual(client.protocol, IoTHubTransportProvider.AMQP) + with self.assertRaises(AttributeError): + client.protocol = IoTHubTransportProvider.AMQP + + if hasattr(IoTHubTransportProvider, "MQTT"): + client = IoTHubClient(connectionString, IoTHubTransportProvider.MQTT) + self.assertIsInstance(client, IoTHubClient) + self.assertEqual(client.protocol, IoTHubTransportProvider.MQTT) + with self.assertRaises(AttributeError): + client.protocol = IoTHubTransportProvider.AMQP + + if hasattr(IoTHubTransportProvider, "AMQP_WS"): + client = IoTHubClient(connectionString, IoTHubTransportProvider.AMQP_WS) + self.assertIsInstance(client, IoTHubClient) + self.assertEqual(client.protocol, IoTHubTransportProvider.AMQP_WS) + with self.assertRaises(AttributeError): + client.protocol = IoTHubTransportProvider.AMQP + + if hasattr(IoTHubTransportProvider, "MQTT_WS"): + client = IoTHubClient(connectionString, IoTHubTransportProvider.MQTT_WS) + self.assertIsInstance(client, IoTHubClient) + self.assertEqual(client.protocol, IoTHubTransportProvider.MQTT_WS) + with self.assertRaises(AttributeError): + client.protocol = IoTHubTransportProvider.AMQP + with self.assertRaises(AttributeError): client.protocol = IoTHubTransportProvider.AMQP with self.assertRaises(AttributeError): client.protocol = 1 + # set_message_callback counter = 1 context = {"a": "b"} @@ -526,6 +567,7 @@ def test_IoTHubClient(self): self.assertIsNone(result) result = client.set_message_callback(receive_message_callback, context) self.assertIsNone(result) + # send_event_async counter = 1 message = IoTHubMessage("myMessage") @@ -543,6 +585,7 @@ def test_IoTHubClient(self): result = client.send_event_async( message, send_confirmation_callback, counter) self.assertIsNone(result) + # get_send_status with self.assertRaises(AttributeError): client.GetSendStatus() @@ -552,6 +595,7 @@ def test_IoTHubClient(self): client.get_send_status(counter) result = client.get_send_status() self.assertIsNotNone(result) + # get_last_message_receive_time with self.assertRaises(AttributeError): result = client.GetLastMessageReceiveTime() @@ -561,6 +605,7 @@ def test_IoTHubClient(self): client.get_last_message_receive_time(counter) with self.assertRaises(IoTHubClientError): result = client.get_last_message_receive_time() + # set_option timeout = 241000 with self.assertRaises(AttributeError): @@ -575,6 +620,73 @@ def test_IoTHubClient(self): self.assertIsNone(result) result = client.set_option("timeout", timeout) self.assertIsNone(result) + + # set_device_twin_callback + counter = 1 + context = {"a": "b"} + with self.assertRaises(AttributeError): + client.SetDeviceTwinCallback() + with self.assertRaises(Exception): + client.set_device_twin_callback() + with self.assertRaises(Exception): + client.set_device_twin_callback(device_twin_callback) + with self.assertRaises(Exception): + client.set_device_twin_callback(counter, device_twin_callback) + with self.assertRaises(Exception): + client.set_device_twin_callback( + device_twin_callback, counter, context) + result = client.set_device_twin_callback(device_twin_callback, counter) + self.assertIsNone(result) + result = client.set_device_twin_callback(device_twin_callback, context) + self.assertIsNone(result) + + # send_reported_state + counter = 1 + reportedState = "{}" + size = 2 + with self.assertRaises(AttributeError): + client.SendReportedState() + with self.assertRaises(Exception): + client.send_reported_state() + with self.assertRaises(Exception): + client.send_reported_state(send_reported_state_callback) + with self.assertRaises(Exception): + client.send_reported_state(send_reported_state_callback, reportedState) + with self.assertRaises(Exception): + client.send_reported_state(send_reported_state_callback, size) + with self.assertRaises(Exception): + client.send_reported_state(send_reported_state_callback, reportedState, size) + with self.assertRaises(Exception): + client.send_reported_state(reportedState) + with self.assertRaises(Exception): + client.send_reported_state(reportedState, size) + with self.assertRaises(Exception): + client.send_reported_state(reportedState, send_reported_state_callback) + with self.assertRaises(Exception): + client.send_reported_state(reportedState, size, send_reported_state_callback) + result = client.send_reported_state( + reportedState, size, send_reported_state_callback, counter) + self.assertIsNone(result) + + # set_device_method_callback + counter = 1 + context = {"a": "b"} + with self.assertRaises(AttributeError): + client.SetDeviceMethodCallback() + with self.assertRaises(Exception): + client.set_device_method_callback() + with self.assertRaises(Exception): + client.set_device_method_callback(device_method_callback) + with self.assertRaises(Exception): + client.set_device_method_callback(counter, device_method_callback) + with self.assertRaises(Exception): + client.set_device_method_callback( + device_method_callback, counter, context) + result = client.set_device_method_callback(device_method_callback, counter) + self.assertIsNone(result) + result = client.set_device_method_callback(device_method_callback, context) + self.assertIsNone(result) + # upload_blob_async destinationFileName = "fname" source = "src" From 95b3edbd404a7ec9462f0d502798f77ed1aa3c44 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Mon, 14 Nov 2016 11:45:34 -0800 Subject: [PATCH 2/7] Always use Cmake --- jenkins/windows_python.cmd | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/jenkins/windows_python.cmd b/jenkins/windows_python.cmd index cdcf0aaf3f3..a2a2cf36791 100644 --- a/jenkins/windows_python.cmd +++ b/jenkins/windows_python.cmd @@ -10,12 +10,8 @@ for %%i in ("%build-root%") do set build-root=%%~fi REM -- Python -- cd %build-root%\python\build_all\windows -if "%1" equ "--use-cmake" ( - echo Building client using cmake - call build_client.cmd %2 %3 -) else ( - echo Building client using Nuget packages - call build.cmd --run-ut -) +echo Building client using cmake +call build_client.cmd %2 %3 + if errorlevel 1 exit /b 1 cd %build-root% From 89666cd69df2902c0f30f908fa0eb63210acae2b Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Mon, 14 Nov 2016 14:47:59 -0800 Subject: [PATCH 3/7] Skip unit tests for C client when building the wrapper --- c/build_all/windows/build_client.cmd | 10 ++++++++-- python/build_all/windows/build_client.cmd | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/c/build_all/windows/build_client.cmd b/c/build_all/windows/build_client.cmd index dd2e1b1cca6..70797ec5877 100644 --- a/c/build_all/windows/build_client.cmd +++ b/c/build_all/windows/build_client.cmd @@ -28,6 +28,7 @@ set CMAKE_use_wsio=OFF set CMAKE_build_python=OFF set CMAKE_build_javawrapper=OFF set CMAKE_no_logging=OFF +set CMAKE_skip_unittests=OFF :args-loop if "%1" equ "" goto args-done @@ -37,6 +38,7 @@ if "%1" equ "--use-websockets" goto arg-use-websockets if "%1" equ "--buildpython" goto arg-build-python if "%1" equ "--build-javawrapper" goto arg-build-javawrapper if "%1" equ "--no-logging" goto arg-no-logging +if "%1" equ "--skip-unittests" goto arg-skip-unittests call :usage && exit /b 1 :arg-build-config @@ -72,6 +74,10 @@ goto args-continue set CMAKE_no_logging=ON goto args-continue +:arg-skip-unittests +set CMAKE_skip_unittests=ON +goto args-continue + :args-continue shift goto args-loop @@ -99,11 +105,11 @@ pushd %USERPROFILE%\%cmake-output% if %build-platform% == Win32 ( echo ***Running CMAKE for Win32*** - cmake %build-root% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) else ( echo ***Running CMAKE for Win64*** - cmake %build-root% -G "Visual Studio 14 Win64" -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -G "Visual Studio 14 Win64" -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) diff --git a/python/build_all/windows/build_client.cmd b/python/build_all/windows/build_client.cmd index d07053983c4..50637801e22 100644 --- a/python/build_all/windows/build_client.cmd +++ b/python/build_all/windows/build_client.cmd @@ -76,9 +76,9 @@ REM -- C -- cd %build-root%..\..\..\c\build_all\windows if %use-websockets% == ON ( -call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets --skip-unittests ) else ( -call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --skip-unittests ) if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! From e28cfd72ef838e99e4e7d384043ceb34312a6c58 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 16 Nov 2016 07:13:47 -0800 Subject: [PATCH 4/7] Set CMAKE variables for Python --- c/CMakeLists.txt | 3 +++ c/build_all/windows/build_client.cmd | 13 +++++++++++-- python/build_all/windows/build_client.cmd | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt index 56cc09df9a6..b187db0e78f 100644 --- a/c/CMakeLists.txt +++ b/c/CMakeLists.txt @@ -1,6 +1,9 @@ #Copyright (c) Microsoft. All rights reserved. #Licensed under the MIT license. See LICENSE file in the project root for full license information. +message(STATUS "PYTHON_LIBRARIES=${PYTHON_LIBRARIES}") +message(STATUS "PYTHON_INCLUDE_DIRS=${PYTHON_INCLUDE_DIRS}") + cmake_minimum_required(VERSION 2.8.11) project(azure_iot_sdks) # diff --git a/c/build_all/windows/build_client.cmd b/c/build_all/windows/build_client.cmd index 70797ec5877..4d8935c73bd 100644 --- a/c/build_all/windows/build_client.cmd +++ b/c/build_all/windows/build_client.cmd @@ -39,6 +39,7 @@ if "%1" equ "--buildpython" goto arg-build-python if "%1" equ "--build-javawrapper" goto arg-build-javawrapper if "%1" equ "--no-logging" goto arg-no-logging if "%1" equ "--skip-unittests" goto arg-skip-unittests +if "%1" equ "--python-path" goto arg-python-path call :usage && exit /b 1 :arg-build-config @@ -78,10 +79,18 @@ goto args-continue set CMAKE_skip_unittests=ON goto args-continue +:arg-python-path +if "%2"=="" goto usage +set CMAKE_python-libs="%2\Lib" +set CMAKE_python-inc="%2\include" +shift +goto args-continue + :args-continue shift goto args-loop + :args-done set cmake-output=cmake_%build-platform% @@ -105,11 +114,11 @@ pushd %USERPROFILE%\%cmake-output% if %build-platform% == Win32 ( echo ***Running CMAKE for Win32*** - cmake %build-root% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -DPYTHON_LIBRARIES=%CMAKE_python-libs% -DPYTHON_INCLUDE_DIRS=%CMAKE_python-inc% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) else ( echo ***Running CMAKE for Win64*** - cmake %build-root% -G "Visual Studio 14 Win64" -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -G "Visual Studio 14 Win64" -DPYTHON_LIBRARIES=%CMAKE_python-libs% -DPYTHON_INCLUDE_DIRS=%CMAKE_python-inc% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) diff --git a/python/build_all/windows/build_client.cmd b/python/build_all/windows/build_client.cmd index 50637801e22..162c3b9d302 100644 --- a/python/build_all/windows/build_client.cmd +++ b/python/build_all/windows/build_client.cmd @@ -76,9 +76,9 @@ REM -- C -- cd %build-root%..\..\..\c\build_all\windows if %use-websockets% == ON ( -call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets --skip-unittests +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets --skip-unittests --python-path %PYTHON_PATH% ) else ( -call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --skip-unittests +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --skip-unittests --python-path %PYTHON_PATH% ) if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! From 1bbb533de4435c5f4d31ed7da3b687e7a6ca1800 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 16 Nov 2016 10:01:01 -0800 Subject: [PATCH 5/7] Changed batch file --- python/build_all/windows/build_client.cmd | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/python/build_all/windows/build_client.cmd b/python/build_all/windows/build_client.cmd index 162c3b9d302..280f855f6b4 100644 --- a/python/build_all/windows/build_client.cmd +++ b/python/build_all/windows/build_client.cmd @@ -1,6 +1,15 @@ @REM Copyright (c) Microsoft. All rights reserved. @REM Licensed under the MIT license. See LICENSE file in the project root for full license information. +rem ensure python.exe exists +where /q python.exe +if errorlevel 1 goto :NeedPython + +python python_version_check.py >pyenv.bat +if errorlevel 1 goto :NeedPython + +call pyenv.bat + @setlocal EnableExtensions EnableDelayedExpansion @echo off @@ -13,25 +22,18 @@ rem ---------------------------------------------------------------------------- rem -- check prerequisites rem ----------------------------------------------------------------------------- -rem ensure python.exe exists -where /q python.exe -if errorlevel 1 goto :NeedPython + rem ----------------------------------------------------------------------------- rem -- detect Python x86 or x64 version, select build target accordingly rem ----------------------------------------------------------------------------- REM target may be set to 64 bit build if a Python x64 detected -set build-platform=Win32 set build-config=Release -set build-python=2.7 set wheel=0 set platname=win32 set use-websockets=OFF -python python_version_check.py >pyenv.bat -if errorlevel 1 goto :NeedPython -call pyenv.bat @Echo Using Python found in: %PYTHON_PATH%, building Python %build-python% %build-platform% extension goto :args-loop @@ -76,9 +78,9 @@ REM -- C -- cd %build-root%..\..\..\c\build_all\windows if %use-websockets% == ON ( -call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets --skip-unittests --python-path %PYTHON_PATH% +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --use-websockets --skip-unittests ) else ( -call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --skip-unittests --python-path %PYTHON_PATH% +call build_client.cmd --platform %build-platform% --buildpython %build-python% --config %build-config% --skip-unittests ) if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! From 435e697a09583417d163520fbcef9314f36faed1 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 16 Nov 2016 13:06:21 -0800 Subject: [PATCH 6/7] Revert CMake variable setting --- c/CMakeLists.txt | 3 --- c/build_all/windows/build_client.cmd | 13 ++----------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt index b187db0e78f..56cc09df9a6 100644 --- a/c/CMakeLists.txt +++ b/c/CMakeLists.txt @@ -1,9 +1,6 @@ #Copyright (c) Microsoft. All rights reserved. #Licensed under the MIT license. See LICENSE file in the project root for full license information. -message(STATUS "PYTHON_LIBRARIES=${PYTHON_LIBRARIES}") -message(STATUS "PYTHON_INCLUDE_DIRS=${PYTHON_INCLUDE_DIRS}") - cmake_minimum_required(VERSION 2.8.11) project(azure_iot_sdks) # diff --git a/c/build_all/windows/build_client.cmd b/c/build_all/windows/build_client.cmd index 4d8935c73bd..70797ec5877 100644 --- a/c/build_all/windows/build_client.cmd +++ b/c/build_all/windows/build_client.cmd @@ -39,7 +39,6 @@ if "%1" equ "--buildpython" goto arg-build-python if "%1" equ "--build-javawrapper" goto arg-build-javawrapper if "%1" equ "--no-logging" goto arg-no-logging if "%1" equ "--skip-unittests" goto arg-skip-unittests -if "%1" equ "--python-path" goto arg-python-path call :usage && exit /b 1 :arg-build-config @@ -79,18 +78,10 @@ goto args-continue set CMAKE_skip_unittests=ON goto args-continue -:arg-python-path -if "%2"=="" goto usage -set CMAKE_python-libs="%2\Lib" -set CMAKE_python-inc="%2\include" -shift -goto args-continue - :args-continue shift goto args-loop - :args-done set cmake-output=cmake_%build-platform% @@ -114,11 +105,11 @@ pushd %USERPROFILE%\%cmake-output% if %build-platform% == Win32 ( echo ***Running CMAKE for Win32*** - cmake %build-root% -DPYTHON_LIBRARIES=%CMAKE_python-libs% -DPYTHON_INCLUDE_DIRS=%CMAKE_python-inc% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) else ( echo ***Running CMAKE for Win64*** - cmake %build-root% -G "Visual Studio 14 Win64" -DPYTHON_LIBRARIES=%CMAKE_python-libs% -DPYTHON_INCLUDE_DIRS=%CMAKE_python-inc% -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% + cmake %build-root% -G "Visual Studio 14 Win64" -Dskip_unittests:BOOL=%CMAKE_skip_unittests% -Duse_wsio:BOOL=%CMAKE_use_wsio% -Dbuild_python:STRING=%CMAKE_build_python% -Dbuild_javawrapper:BOOL=%CMAKE_build_javawrapper% -Dno_logging:BOOL=%CMAKE_no_logging% if not !ERRORLEVEL!==0 exit /b !ERRORLEVEL! ) From 74488503d3818ec05166022d8dbdebe8d3c4a819 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 16 Nov 2016 15:25:30 -0800 Subject: [PATCH 7/7] Fix build_platform setting --- python/build_all/windows/build_client.cmd | 5 ++--- python/build_all/windows/python_version_check.py | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/build_all/windows/build_client.cmd b/python/build_all/windows/build_client.cmd index 280f855f6b4..b4c08250baa 100644 --- a/python/build_all/windows/build_client.cmd +++ b/python/build_all/windows/build_client.cmd @@ -1,6 +1,8 @@ @REM Copyright (c) Microsoft. All rights reserved. @REM Licensed under the MIT license. See LICENSE file in the project root for full license information. +@echo off + rem ensure python.exe exists where /q python.exe if errorlevel 1 goto :NeedPython @@ -11,7 +13,6 @@ if errorlevel 1 goto :NeedPython call pyenv.bat @setlocal EnableExtensions EnableDelayedExpansion -@echo off set build-root=%~dp0 rem // resolve to fully qualified path @@ -22,8 +23,6 @@ rem ---------------------------------------------------------------------------- rem -- check prerequisites rem ----------------------------------------------------------------------------- - - rem ----------------------------------------------------------------------------- rem -- detect Python x86 or x64 version, select build target accordingly rem ----------------------------------------------------------------------------- diff --git a/python/build_all/windows/python_version_check.py b/python/build_all/windows/python_version_check.py index a410422566c..884b09e294e 100644 --- a/python/build_all/windows/python_version_check.py +++ b/python/build_all/windows/python_version_check.py @@ -22,6 +22,8 @@ def __str__(self): else: if (plat[0] != "32bit"): raise PlatformError("Require Windows CPython >= 2.7 or >= 3.4 32bit or 64bit version") + print ("SET build-platform=Win32") + print ("SET PYTHON_PATH=%s" % os.path.dirname(sys.executable)) print ("SET build-python=%s.%s" % (sys.version_info[0],sys.version_info[1])) sys.exit(0)