...
 
Commits (7)
......@@ -8,6 +8,8 @@ differs as follows:
Changes for 1.0.0:
- Events containing binary Readings are encoded using CBOR.
- This introduces a dependency on libcbor.
- Data transformations (other than "mask") are applied for device PUT requests.
- When querying for Device Profiles they are returned in a list in the same way
as devices.
......
......@@ -9,7 +9,7 @@ The C SDK provides a framework for building EdgeX device services in C.
* A Linux build host
* A version of GCC supporting C99.
* CMake version 3 or greater and make.
* Development libraries and headers for curl, microhttpd, yaml and libuuid.
* Development libraries and headers for curl, microhttpd, yaml, libcbor and libuuid.
### Building
......
#!/bin/sh
set -x -e
mkdir /deps
cd /deps
git clone https://github.com/PJK/libcbor
sed -e 's/-flto//' -i libcbor/CMakeLists.txt
cmake -DCMAKE_BUILD_TYPE=Release -DCBOR_CUSTOM_ALLOC=ON libcbor
make
make install
# Build distribution
/edgex-c-sdk/scripts/build.sh $*
......
......@@ -31,6 +31,10 @@ find_package (LibUUID REQUIRED)
if (NOT LIBUUID_FOUND)
message (FATAL_ERROR "UUID library or header not found")
endif ()
find_package (LibCBOR REQUIRED)
if (NOT LIBCBOR_FOUND)
message (FATAL_ERROR "CBOR library or header not found")
endif ()
message (STATUS "C SDK ${CSDK_DOT_VERSION} for ${CMAKE_SYSTEM_NAME}")
......
......@@ -21,7 +21,7 @@ endif ()
# Set default files to compile and libraries
file (GLOB C_FILES *.c iot/*.c)
set (LINK_LIBRARIES ${LIBMICROHTTP_LIBRARIES} ${CURL_LIBRARIES} ${LIBYAML_LIBRARIES} ${LIBUUID_LIBRARIES})
set (LINK_LIBRARIES ${LIBMICROHTTP_LIBRARIES} ${CURL_LIBRARIES} ${LIBYAML_LIBRARIES} ${LIBUUID_LIBRARIES} ${LIBCBOR_LIBRARIES})
configure_file ("defs.h.in" "${CMAKE_SOURCE_DIR}/../include/edgex/csdk-defs.h")
# Main sdk library
......
......@@ -7,6 +7,7 @@
*/
#include "autoevent.h"
#include "errorlist.h"
#include "edgex/eventgen.h"
#include "edgex/edgex.h"
#include "edgex/edgex-logging.h"
......@@ -14,13 +15,14 @@
#include "parson.h"
#include "edgex-rest.h"
#include "correlation.h"
#include "metadata.h"
#include <microhttpd.h>
typedef struct edgex_autoimpl
{
edgex_device_service *svc;
struct json_value_t *last;
edgex_device_commandresult *last;
uint64_t interval;
const edgex_cmdinfo *resource;
char *device;
......@@ -59,21 +61,19 @@ static void edgex_autoimpl_release (edgex_autoimpl *ai)
{
free (ai->device);
edgex_protocols_free (ai->protocols);
json_value_free (ai->last);
edgex_device_commandresult_free (ai->last, ai->resource->nreqs);
free (ai);
}
}
static void ae_runner (void *p)
{
int res;
edgex_autoimpl *ai = (edgex_autoimpl *)p;
atomic_fetch_add (&ai->refs, 1);
edgex_device *dev = edgex_devmap_device_byname (ai->svc->devices, ai->device);
if (dev)
{
JSON_Value *result = NULL;
if (dev->adminState == LOCKED || dev->operatingState == DISABLED)
{
edgex_device_release (dev);
......@@ -81,20 +81,55 @@ static void ae_runner (void *p)
}
edgex_device_alloc_crlid (NULL);
iot_log_info (ai->svc->logger, "AutoEvent: %s/%s", ai->device, ai->resource->name);
res = edgex_device_runget
(ai->svc, dev, ai->resource, ai->onChange ? ai->last : NULL, &result);
if (res == MHD_HTTP_OK)
edgex_device_commandresult *results = calloc (ai->resource->nreqs, sizeof (edgex_device_commandresult));
if
(
ai->svc->userfns.gethandler
(ai->svc->userdata, dev->name, dev->protocols, ai->resource->nreqs, ai->resource->reqs, results)
)
{
if (ai->onChange)
edgex_device_commandresult *resdup = NULL;
if (!(ai->onChange && ai->last && edgex_device_commandresult_equal (results, ai->last, ai->resource->nreqs)))
{
json_value_free (ai->last);
ai->last = result;
}
else
{
json_value_free (result);
edgex_error err = EDGEX_OK;
if (ai->onChange)
{
resdup = edgex_device_commandresult_dup (results, ai->resource->nreqs);
}
edgex_event_cooked *event =
edgex_data_process_event (dev->name, ai->resource, results, ai->svc->config.device.datatransform);
if (event)
{
edgex_data_client_add_event (ai->svc->logger, &ai->svc->config.endpoints, event, &err);
if (err.code == 0)
{
if (ai->onChange)
{
edgex_device_commandresult_free (ai->last, ai->resource->nreqs);
ai->last = resdup;
resdup = NULL;
}
}
else
{
iot_log_error (ai->svc->logger, "AutoEvent: unable to push new event");
}
edgex_event_cooked_free (event);
}
else
{
iot_log_error (ai->svc->logger, "Assertion failed for device %s. Disabling.", dev->name);
edgex_metadata_client_set_device_opstate
(ai->svc->logger, &ai->svc->config.endpoints, dev->id, DISABLED, &err);
}
}
edgex_device_commandresult_free (resdup, ai->resource->nreqs);
}
else
{
iot_log_error (ai->svc->logger, "AutoEvent: Driver for %s failed on GET", dev->name);
}
edgex_device_commandresult_free (results, ai->resource->nreqs);
edgex_device_free_crlid ();
edgex_device_release (dev);
}
......
......@@ -23,7 +23,8 @@ int edgex_device_handler_callback
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
......
......@@ -18,7 +18,8 @@ extern int edgex_device_handler_callback
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
);
......
......@@ -694,7 +694,8 @@ int edgex_device_handler_config
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
......@@ -781,6 +782,7 @@ int edgex_device_handler_config
}
*reply = json_serialize_to_string (val);
*reply_size = strlen (*reply);
*reply_type = "application/json";
json_value_free (val);
......
......@@ -113,7 +113,8 @@ int edgex_device_handler_config
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
);
......
此差异已折叠。
......@@ -18,17 +18,18 @@ typedef struct edgex_reading
uint64_t created;
char *id;
uint64_t modified;
char *name;
const char *name;
uint64_t origin;
uint64_t pushed;
char *value;
edgex_device_commandresult value;
bool binfloat;
struct edgex_reading *next;
} edgex_reading;
typedef struct edgex_event
{
uint64_t created;
char *device;
const char *device;
char *id;
uint64_t modified;
uint64_t origin;
......@@ -37,6 +38,22 @@ typedef struct edgex_event
struct edgex_event *next;
} edgex_event;
typedef enum { JSON, CBOR} edgex_event_encoding;
typedef struct edgex_event_cooked
{
edgex_event_encoding encoding;
union
{
char *json;
struct
{
unsigned char *data;
size_t length;
} cbor;
} value;
} edgex_event_cooked;
typedef struct
{
uint64_t created;
......@@ -58,7 +75,9 @@ typedef struct
typedef struct edgex_service_endpoints edgex_service_endpoints;
JSON_Value *edgex_data_generate_event
void edgex_event_cooked_free (edgex_event_cooked *e);
edgex_event_cooked *edgex_data_process_event
(
const char *device_name,
const edgex_cmdinfo *commandinfo,
......@@ -70,7 +89,7 @@ void edgex_data_client_add_event
(
iot_logger_t *lc,
edgex_service_endpoints *endpoints,
JSON_Value *eventval,
edgex_event_cooked *eventval,
edgex_error *err
);
......@@ -99,4 +118,11 @@ bool edgex_data_client_ping
edgex_error *err
);
void edgex_device_commandresult_free (edgex_device_commandresult *res, int n);
edgex_device_commandresult *edgex_device_commandresult_dup (const edgex_device_commandresult *res, int n);
bool edgex_device_commandresult_equal
(const edgex_device_commandresult *lhs, const edgex_device_commandresult *rhs, int n);
#endif
......@@ -124,14 +124,13 @@ char *edgex_value_tostring (const edgex_device_commandresult *value, bool binflo
}
break;
case String:
res = value->value.string_result;
res = strdup (value->value.string_result);
break;
case Binary:
sz = edgex_b64_encodesize (value->value.binary_result.size);
res = malloc (sz);
edgex_b64_encode
(value->value.binary_result.bytes, value->value.binary_result.size, res, sz);
free (value->value.binary_result.bytes);
break;
}
return res;
......@@ -344,8 +343,7 @@ static int edgex_device_runput
edgex_device_service *svc,
edgex_device *dev,
const edgex_cmdinfo *commandinfo,
const char *data,
JSON_Value **reply
const char *data
)
{
const char *value;
......@@ -408,30 +406,18 @@ static int edgex_device_runput
}
}
for (int i = 0; i < commandinfo->nreqs; i++)
{
if (results[i].type == String)
{
free (results[i].value.string_result);
}
else if (results[i].type == Binary)
{
free (results[i].value.binary_result.bytes);
}
}
free (results);
edgex_device_commandresult_free (results, commandinfo->nreqs);
json_value_free (jval);
return retcode;
}
int edgex_device_runget
static int edgex_device_runget
(
edgex_device_service *svc,
edgex_device *dev,
const edgex_cmdinfo *commandinfo,
const JSON_Value *lastval,
JSON_Value **reply
edgex_event_cooked **reply
)
{
int retcode = MHD_HTTP_INTERNAL_SERVER_ERROR;
......@@ -459,20 +445,13 @@ int edgex_device_runget
)
{
edgex_error err = EDGEX_OK;
*reply = edgex_data_generate_event
*reply = edgex_data_process_event
(dev->name, commandinfo, results, svc->config.device.datatransform);
if (*reply)
{
if (lastval == NULL || (json_value_equals (*reply, lastval) == 0))
{
edgex_data_client_add_event
(svc->logger, &svc->config.endpoints, *reply, &err);
}
if (err.code == 0)
{
retcode = MHD_HTTP_OK;
}
retcode = MHD_HTTP_OK;
edgex_data_client_add_event (svc->logger, &svc->config.endpoints, *reply, &err);
}
else
{
......@@ -486,7 +465,7 @@ int edgex_device_runget
iot_log_error
(svc->logger, "Driver for %s failed on GET", dev->name);
}
free (results);
edgex_device_commandresult_free (results, commandinfo->nreqs);
return retcode;
}
......@@ -497,7 +476,7 @@ static int runOne
const edgex_cmdinfo *command,
const char *upload_data,
size_t upload_data_size,
JSON_Value **reply
edgex_event_cooked **reply
)
{
if (dev->adminState == LOCKED)
......@@ -535,7 +514,7 @@ static int runOne
if (command->isget)
{
return edgex_device_runget (svc, dev, command, NULL, reply);
return edgex_device_runget (svc, dev, command, reply);
}
else
{
......@@ -544,7 +523,7 @@ static int runOne
iot_log_error (svc->logger, "PUT command recieved with no data");
return MHD_HTTP_BAD_REQUEST;
}
return edgex_device_runput (svc, dev, command, upload_data, reply);
return edgex_device_runput (svc, dev, command, upload_data);
}
}
......@@ -562,38 +541,64 @@ static int allCommand
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
int ret = MHD_HTTP_NOT_FOUND;
int retOne;
JSON_Value *jresult;
JSON_Array *jarray;
edgex_cmdqueue_t *cmdq = NULL;
edgex_cmdqueue_t *iter;
uint32_t nret = 0;
int32_t maxret = svc->config.service.readmaxlimit;
char *buff;
edgex_event_encoding enc;
size_t bsize;
iot_log_debug
(svc->logger, "Incoming %s command %s for all", methStr (method), cmd);
jresult = json_value_init_array ();
jarray = json_value_get_array (jresult);
enc = JSON;
bsize = 3; // start-array byte + end-array byte + NUL character
buff = malloc (bsize);
strcpy (buff, "[");
cmdq = edgex_devmap_device_forcmd (svc->devices, cmd, method == GET);
uint32_t nret = 0;
uint32_t maxret = svc->config.service.readmaxlimit;
for (iter = cmdq; iter; iter = iter->next)
{
JSON_Value *jreply = NULL;
edgex_event_cooked *ereply = NULL;
retOne = runOne
(svc, iter->dev, iter->cmd, upload_data, upload_data_size, &jreply);
(svc, iter->dev, iter->cmd, upload_data, upload_data_size, &ereply);
edgex_device_release (iter->dev);
if (jreply && (maxret == 0 || nret++ < maxret))
if (ereply && (maxret == 0 || nret < maxret))
{
json_array_append_value (jarray, jreply);
enc = ereply->encoding;
switch (enc)
{
case JSON:
bsize += (strlen (ereply->value.json) + (nret ? 1 : 0));
buff = realloc (buff, bsize);
if (nret)
{
strcat (buff, ",");
}
strcat (buff, ereply->value.json);
break;
case CBOR:
buff = realloc (buff, bsize + ereply->value.cbor.length);
if (nret == 0)
{
buff[0] = '\374';
}
memcpy (buff + bsize - 2, ereply->value.cbor.data, ereply->value.cbor.length);
bsize += ereply->value.cbor.length;
break;
}
nret++;
}
edgex_event_cooked_free (ereply);
if (ret != MHD_HTTP_OK)
{
ret = retOne;
......@@ -602,10 +607,28 @@ static int allCommand
if (ret == MHD_HTTP_OK)
{
*reply = json_serialize_to_string (jresult);
*reply_type = "application/json";
switch (enc)
{
case JSON:
strcat (buff, "]");
*reply = buff;
*reply_size = strlen (buff);
*reply_type = "application/json";
break;
case CBOR:
buff[bsize - 2] = '\377';
buff[bsize - 1] = '\0';
*reply = buff;
*reply_size = bsize - 1;
*reply_type = "application/cbor";
break;
}
}
json_value_free (jresult);
else
{
free (buff);
}
while (cmdq)
{
iter = cmdq->next;
......@@ -624,7 +647,8 @@ static int oneCommand
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
......@@ -656,15 +680,26 @@ static int oneCommand
if (command)
{
JSON_Value *jreply = NULL;
edgex_event_cooked *ereply = NULL;
result = runOne
(svc, dev, command, upload_data, upload_data_size, &jreply);
(svc, dev, command, upload_data, upload_data_size, &ereply);
edgex_device_release (dev);
if (jreply)
if (ereply)
{
*reply = json_serialize_to_string (jreply);
*reply_type = "application/json";
json_value_free (jreply);
switch (ereply->encoding)
{
case JSON:
*reply = ereply->value.json;
*reply_size = strlen (ereply->value.json);
*reply_type = "application/json";
break;
case CBOR:
*reply = ereply->value.cbor.data;
*reply_size = ereply->value.cbor.length;
*reply_type = "application/cbor";
break;
}
free (ereply);
}
}
else
......@@ -703,7 +738,8 @@ int edgex_device_handler_device
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
......@@ -722,8 +758,7 @@ int edgex_device_handler_device
cmd = url + 4;
if (strlen (cmd))
{
result = allCommand
(svc, cmd, method, upload_data, upload_data_size, reply, reply_type);
result = allCommand (svc, cmd, method, upload_data, upload_data_size, reply, reply_size, reply_type);
}
else
{
......@@ -747,12 +782,7 @@ int edgex_device_handler_device
{
*cmd = '\0';
result = oneCommand
(
svc,
url, byName, cmd + 1, method,
upload_data, upload_data_size,
reply, reply_type
);
(svc, url, byName, cmd + 1, method, upload_data, upload_data_size, reply, reply_size, reply_type);
*cmd = '/';
}
}
......
......@@ -12,8 +12,8 @@
#include "edgex/devsdk.h"
#include "edgex/edgex.h"
#include "rest-server.h"
#include "parson.h"
#include "cmdinfo.h"
#include "data.h"
extern int edgex_device_handler_device
(
......@@ -22,7 +22,8 @@ extern int edgex_device_handler_device
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
);
......@@ -31,13 +32,4 @@ extern char *edgex_value_tostring (const edgex_device_commandresult *value, bool
extern const struct edgex_cmdinfo *edgex_deviceprofile_findcommand
(const char *name, edgex_deviceprofile *prof, bool forGet);
extern int edgex_device_runget
(
edgex_device_service *svc,
edgex_device *dev,
const edgex_cmdinfo *commandinfo,
const JSON_Value *lastval,
JSON_Value **reply
);
#endif
......@@ -24,7 +24,8 @@ int edgex_device_handler_discovery
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
......@@ -43,6 +44,7 @@ int edgex_device_handler_discovery
if (!svc->config.device.discovery)
{
*reply = strdup ("Discovery disabled by configuration\n");
*reply_size = strlen (*reply);
return MHD_HTTP_SERVICE_UNAVAILABLE;
}
......@@ -54,6 +56,7 @@ int edgex_device_handler_discovery
// else discovery was already running; ignore this request
*reply = strdup ("Running discovery\n");
*reply_size = strlen (*reply);
return MHD_HTTP_OK;
}
......
......@@ -20,7 +20,8 @@ extern int edgex_device_handler_discovery
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
);
......
......@@ -28,7 +28,8 @@ int edgex_device_handler_metrics
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
......@@ -65,6 +66,7 @@ int edgex_device_handler_metrics
json_object_set_number (obj, "CpuAvgUsage", cputime / walltime);
}
*reply = json_serialize_to_string (val);
*reply_size = strlen (*reply);
*reply_type = "application/json";
json_value_free (val);
return MHD_HTTP_OK;
......
......@@ -20,7 +20,8 @@ extern int edgex_device_handler_metrics
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
);
......
......@@ -90,7 +90,8 @@ static int http_handler
http_context_t *ctx = (http_context_t *) *context;
edgex_rest_server *svr = (edgex_rest_server *) this;
struct MHD_Response *response = NULL;
char *reply = NULL;
void *reply = NULL;
size_t reply_size = 0;
const char *reply_type = NULL;
handler_list *h;
......@@ -130,19 +131,20 @@ static int http_handler
if (method == GET)
{
/* List available handlers */
int rsize = 1;
reply_size = 0;
pthread_mutex_lock (&svr->lock);
for (h = svr->handlers; h; h = h->next)
{
rsize += strlen (h->url) + 1;
reply_size += strlen (h->url) + 1;
}
reply = malloc (rsize);
reply[0] = '\0';
char *buff = malloc (reply_size + 1);
buff[0] = '\0';
for (h = svr->handlers; h; h = h->next)
{
strcat (reply, h->url);
strcat (reply, "\n");
strcat (buff, h->url);
strcat (buff, "\n");
}
reply = buff;
pthread_mutex_unlock (&svr->lock);
}
else
......@@ -180,6 +182,7 @@ static int http_handler
ctx->m_data,
ctx->m_size,
&reply,
&reply_size,
&reply_type
);
}
......@@ -200,9 +203,9 @@ static int http_handler
if (reply == NULL)
{
reply = strdup ("");
reply_size = 0;
}
response = MHD_create_response_from_buffer
(strlen (reply), reply, MHD_RESPMEM_MUST_FREE);
response = MHD_create_response_from_buffer (reply_size, reply, MHD_RESPMEM_MUST_FREE);
MHD_add_response_header (response, "Content-Type", reply_type);
MHD_queue_response (conn, status, response);
MHD_destroy_response (response);
......
......@@ -33,7 +33,8 @@ typedef int (*http_method_handler_fn)
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
);
......
......@@ -315,6 +315,21 @@ long edgex_http_post
return edgex_run_curl (lc, ctx, hnd, url, writefunc, slist, err);
}
long edgex_http_postbin
(iot_logger_t *lc, edgex_ctx *ctx, const char *url, void *data, size_t length, const char *mime, void *writefunc, edgex_error *err)
{
struct curl_slist *slist;
CURL *hnd = curl_easy_init ();
curl_easy_setopt (hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt (hnd, CURLOPT_POST, 1L);
curl_easy_setopt (hnd, CURLOPT_POSTFIELDS, data);
curl_easy_setopt (hnd, CURLOPT_POSTFIELDSIZE_LARGE, length);
slist = edgex_add_hdr (NULL, "Content-Type", mime);
return edgex_run_curl (lc, ctx, hnd, url, writefunc, slist, err);
}
long edgex_http_postfile
(iot_logger_t *lc, edgex_ctx *ctx, const char *url, const char *fname, void *writefunc, edgex_error *err)
{
......
......@@ -62,6 +62,9 @@ long edgex_http_delete
long edgex_http_post
(iot_logger_t *lc, edgex_ctx *ctx, const char *url, const char *data, void *writefunc, edgex_error *err);
long edgex_http_postbin
(iot_logger_t *lc, edgex_ctx *ctx, const char *url, void *data, size_t length, const char *mime, void *writefunc, edgex_error *err);
long edgex_http_postfile
(iot_logger_t *lc, edgex_ctx *ctx, const char *url, const char *filename, void *writefunc, edgex_error *err);
......
......@@ -42,7 +42,7 @@
typedef struct postparams
{
edgex_device_service *svc;
JSON_Value *jevent;
edgex_event_cooked *event;
} postparams;
edgex_device_service *edgex_device_service_new
......@@ -97,12 +97,14 @@ static int ping_handler
edgex_http_method method,
const char *upload_data,
size_t upload_data_size,
char **reply,
void **reply,
size_t *reply_size,
const char **reply_type
)
{
edgex_device_service *svc = (edgex_device_service *) ctx;
*reply = strdup (svc->version);
*reply_size = strlen (svc->version);
*reply_type = "text/plain";
return MHD_HTTP_OK;
}
......@@ -518,11 +520,11 @@ static void doPost (void *p)
(
pp->svc->logger,
&pp->svc->config.endpoints,
pp->jevent,
pp->event,
&err
);
json_value_free (pp->jevent);
edgex_event_cooked_free (pp->event);
free (pp);
}
......@@ -547,14 +549,14 @@ void edgex_device_post_readings
if (command)
{
JSON_Value *jevent = edgex_data_generate_event
edgex_event_cooked *event = edgex_data_process_event
(devname, command, values, svc->config.device.datatransform);
if (jevent)
if (event)
{
postparams *pp = malloc (sizeof (postparams));
pp->svc = svc;
pp->jevent = jevent;
pp->event = event;
iot_threadpool_add_work (svc->thpool, doPost, pp, NULL);
}
}
......
find_path (LIBCBOR_INCLUDE_DIR cbor.h)
find_library (LIBCBOR_LIBRARIES NAMES cbor libcbor)
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (LIBCBOR DEFAULT_MSG LIBCBOR_LIBRARIES LIBCBOR_INCLUDE_DIR)