diff --git a/.travis.yml b/.travis.yml index 74d638c..eec503e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -192,7 +192,6 @@ script: -DENABLE_LIFESPAN=${LIFESPAN} -DENABLE_DEADLINE_MISSED=${DEADLINE} -DBUILD_TESTING=on - -DBUILD_SCHEMA=on -DWERROR=on -G "${GENERATOR}" .. - | @@ -219,8 +218,6 @@ script: fi - | if [ "${SSL}" = "YES" ] && [ "${SECURITY}" = "YES" ]; then - cmake --build . --config ${BUILD_TYPE} --target schema && \ - cmake --build . --config ${BUILD_TYPE} --target options_doc && \ diff --strip-trailing-cr ../etc/cyclonedds.rnc docs/cyclonedds.rnc && \ diff --strip-trailing-cr ../etc/cyclonedds.xsd docs/cyclonedds.xsd && \ diff --strip-trailing-cr ../docs/manual/options.md docs/manual/options.md diff --git a/CMakeLists.txt b/CMakeLists.txt index a925545..b30ede7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,8 +210,12 @@ option(BUILD_TESTING "Build the testing tree." OFF) include(CTest) option(BUILD_DOCS "Build documentation." OFF) -option(BUILD_SCHEMA "Build generated schema for configuration options." OFF) - +if(CMAKE_CROSSCOMPILING) + set(not_crosscompiling OFF) +else() + set(not_crosscompiling ON) +endif() +option(BUILD_SCHEMA "Build generated schema for configuration options." ${not_crosscompiling}) # Build all executables and libraries into the top-level /bin and /lib folders. set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index ea35b37..a052394 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -18,18 +18,14 @@ set(options_md "${CMAKE_CURRENT_BINARY_DIR}/manual/options.md") if(BUILD_SCHEMA OR BUILD_DOCS) add_custom_command( - OUTPUT "${cyclonedds_rnc}" "${cyclonedds_xsd}" + OUTPUT "${cyclonedds_rnc}" "${cyclonedds_xsd}" "${options_md}" COMMAND ddsconf ARGS -f rnc -o "${cyclonedds_rnc}" COMMAND ddsconf ARGS -f xsd -o "${cyclonedds_xsd}" - DEPENDS ddsconf) - add_custom_target(schema DEPENDS "${cyclonedds_rnc}" "${cyclonedds_xsd}") - - add_custom_command( - OUTPUT "${options_md}" COMMAND ${CMAKE_COMMAND} -E make_directory manual COMMAND ddsconf ARGS -f md -o "${options_md}" DEPENDS ddsconf) - add_custom_target(options_doc DEPENDS "${options_md}") + add_custom_target( + schema ALL DEPENDS "${cyclonedds_rnc}" "${cyclonedds_xsd}" "${options_md}") endif() if(BUILD_DOCS) @@ -39,7 +35,7 @@ if(BUILD_DOCS) BREATHE_PROJECTS ddsc_api_docs BUILDER html SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/manual") - add_dependencies(docs options_doc) + add_dependencies(docs schema) install( DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/docs" diff --git a/docs/manual/options.md b/docs/manual/options.md index 339c685..fc440b8 100644 --- a/docs/manual/options.md +++ b/docs/manual/options.md @@ -1695,4 +1695,4 @@ While none prevents any message from being written to a DDSI2 log file. The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are config, fine and finest. -The default value is: "none". \ No newline at end of file +The default value is: "none". diff --git a/etc/cyclonedds.rnc b/etc/cyclonedds.rnc index e1ac089..cdc59d7 100644 --- a/etc/cyclonedds.rnc +++ b/etc/cyclonedds.rnc @@ -101,7 +101,7 @@ CycloneDDS configuration""" ] ] text } }* - }? + }* & [ a:documentation [ xml:lang="en" """
This element statically configures an addresses for discovery.
""" ] ] element Peer { @@ -983,7 +983,7 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==The Sizing element specifies a variety of configuration settings dealing with expected system sizes, buffer sizes, &c.
""" ] ] element Sizing { @@ -1180,4 +1180,4 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==This element statically configures a fault tolerant group of " "addresses for discovery. Each member of the group is tried in " "sequence until one succeeds.
" - )), + ), + MAXIMUM(0)), /* Group element can occur more than once, but 1 is required + because of the way its processed (for now) */ END_MARKER }; @@ -1849,7 +1851,9 @@ static struct cfgelem domain_cfgelems[] = { DESCRIPTION( "This element is used to configure Cyclone DDS with the DDS Security " "specification plugins and settings.
" - )), + ), + MAXIMUM(1)), /* Security must occur at most once, but INT_MAX is required + because of the way its processed (for now) */ #endif #ifdef DDSI_INCLUDE_NETWORK_PARTITIONS GROUP("Partitioning", partitioning_cfgelems, NULL, 1, diff --git a/src/core/ddsi/src/q_config.c b/src/core/ddsi/src/q_config.c index a8e238a..a131d3c 100644 --- a/src/core/ddsi/src/q_config.c +++ b/src/core/ddsi/src/q_config.c @@ -230,6 +230,8 @@ DI(if_omg_security); #define RANGE(...) /* drop */ #define UNIT(...) /* drop */ #define VALUES(...) /* drop */ +#define MAXIMUM(...) /* drop */ +#define MINIMUM(...) /* drop */ #define NOMEMBER 0, 0 #define NOFUNCTIONS 0, 0, 0, 0 @@ -269,6 +271,12 @@ static const struct cfgelem root_cfgelem = { #undef MEMBER #undef MEMBEROF #undef FUNCTIONS +#undef DESCRIPTION +#undef RANGE +#undef UNIT +#undef VALUES +#undef MAXIMUM +#undef MINIMUM #undef NOMEMBER #undef NOFUNCTIONS #undef NODATA diff --git a/src/tools/ddsconf/ddsconf.c b/src/tools/ddsconf/ddsconf.c index f03cb6f..d0d900e 100644 --- a/src/tools/ddsconf/ddsconf.c +++ b/src/tools/ddsconf/ddsconf.c @@ -50,10 +50,12 @@ #define RANGE(str) .range = str #define UNIT(str) .unit = str #define VALUES(...) .values = (const char *[]){ __VA_ARGS__, NULL } +#define MAXIMUM(num) .force_maximum = 1, .maximum = num +#define MINIMUM(num) .force_minimum = 1, .minimum = num #define NOMEMBER /* drop */ #define NOFUNCTIONS /* drop */ -#define NOMETADATA { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } +#define NOMETADATA { NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL } #define END_MARKER { NULL, NULL, NULL, 0, NULL, NULL, NOMETADATA } #define ELEMENT(name, elems, attrs, multip, dflt, desc, ...) \ @@ -76,8 +78,8 @@ EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "string", __VA_ARGS__)) #define LIST(name, attrs, multip, dflt, ofst, funcs, desc, ...) \ EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "list", __VA_ARGS__)) -#define GROUP(name, elems, attrs, multip, ofst, funcs, desc) \ - EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, desc, .type = "group")) +#define GROUP(name, elems, attrs, multip, ofst, funcs, desc, ...) \ + EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, desc, .type = "group", __VA_ARGS__)) #include "dds/ddsi/ddsi_cfgelems.h" /* undefine element macros */ @@ -89,6 +91,8 @@ #undef RANGE #undef UNIT #undef VALUES +#undef MAXIMUM +#undef MINIMUM #undef NOMEMBER #undef NOFUNCTIONS #undef NOMETADATA @@ -228,27 +232,35 @@ int islist(const struct cfgelem *elem) int minimum(const struct cfgelem *elem) { - switch (elem->multiplicity) { - case 0: /* special case, treat as-if 1 */ - case 1: /* required if there is no default */ - if (isgroup(elem)) + if (elem->meta.force_minimum) { + return elem->meta.minimum; + } else { + switch (elem->multiplicity) { + case 0: /* special case, treat as-if 1 */ + case 1: /* required if there is no default */ + if (isgroup(elem)) + return 0; + return (!elem->value); + default: return 0; - return (!elem->value); - default: - return 0; + } } } int maximum(const struct cfgelem *elem) { - switch (elem->multiplicity) { - case INT_MAX: - return 0; - case 0: - case 1: - return 1; - default: - return elem->multiplicity; + if (elem->meta.force_maximum) { + return elem->meta.maximum; + } else { + switch (elem->multiplicity) { + case INT_MAX: + return 0; + case 0: + case 1: + return 1; + default: + return elem->multiplicity; + } } } diff --git a/src/tools/ddsconf/ddsconf.h b/src/tools/ddsconf/ddsconf.h index c3afb55..6a88f47 100644 --- a/src/tools/ddsconf/ddsconf.h +++ b/src/tools/ddsconf/ddsconf.h @@ -31,6 +31,8 @@ struct cfgmeta { char *pattern; char *description; unsigned int flags; + const int force_maximum, maximum; + const int force_minimum, minimum; const char *type; const char *unit; const char *range; diff --git a/src/tools/ddsconf/md.c b/src/tools/ddsconf/md.c index f768bf8..3cb0589 100644 --- a/src/tools/ddsconf/md.c +++ b/src/tools/ddsconf/md.c @@ -103,32 +103,32 @@ static void printtype( (void)units; assert(!isgroup(elem)); if (isbool(elem)) { - fprintf(out, "Boolean\n"); + fputs("Boolean\n", out); } else if (islist(elem)) { assert(elem->meta.values); - fprintf(out, "One of:\n"); + fputs("One of:\n", out); if (elem->value && strlen(elem->value)) fprintf(out, "* Keyword: %s\n", elem->value); - fprintf(out, "* Comma-separated list of: "); + fputs("* Comma-separated list of: ", out); for (const char **v = elem->meta.values; *v; v++) { fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v); } - fprintf(out, "\n"); + fputs("\n", out); if (!elem->value || !strlen(elem->value)) - fprintf(out, "* Or empty\n"); + fputs("* Or empty\n", out); } else if (isenum(elem)) { assert(elem->meta.values); - fprintf(out, "One of: "); + fputs("One of: ", out); for (const char **v = elem->meta.values; *v; v++) { fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v); } - fprintf(out, "\n"); + fputs("\n", out); } else if (isint(elem)) { - fprintf(out, "Integer\n"); + fputs("Integer\n", out); } else if (elem->meta.unit) { - fprintf(out, "Number-with-unit\n"); + fputs("Number-with-unit\n", out); } else if (isstring(elem)) { - fprintf(out, "Text\n"); + fputs("Text\n", out); } } @@ -142,12 +142,14 @@ static void printattr( const struct cfgunit *units) { if (flags & FLAG_LF) - fputs("\n\n\n", out); + fputs("\n\n", out); printhead(out, level, flags, elem, units); printtype(out, level, flags, elem, units); fputs("\n", out); - if (elem->description) + if (elem->description) { fputs(elem->meta.description, out); + fputs("\n", out); + } } static void printelem( @@ -158,7 +160,7 @@ static void printelem( const struct cfgunit *units) { if (flags & FLAG_LF) - fprintf(out, "\n\n\n"); + fputs("\n\n", out); printhead(out, level, flags, elem, units); flags &= ~FLAG_LF; if (hasattributes(elem)) { @@ -176,7 +178,7 @@ static void printelem( ce = nextelem(elem->attributes, ce); } if (cnt != 0) { - fprintf(out, "\n"); + fputs("\n", out); flags |= FLAG_LF; } } @@ -190,18 +192,19 @@ static void printelem( sep = ", "; ce = nextelem(elem->children, ce); } - fprintf(out, "\n"); + fputs("\n", out); flags |= FLAG_LF; } else if (!isgroup(elem)) { if (flags & FLAG_LF) - fprintf(out, "\n"); + fputs("\n", out); printtype(out, level+1, flags, elem, units); flags |= FLAG_LF; } if (elem->description) { if (flags & FLAG_LF) - fprintf(out, "\n"); + fputs("\n", out); fputs(elem->meta.description, out); + fputs("\n", out); } if (hasattributes(elem)) { struct cfgelem *ce = firstelem(elem->attributes); diff --git a/src/tools/ddsconf/rnc.c b/src/tools/ddsconf/rnc.c index 2b36b0e..7b0b825 100644 --- a/src/tools/ddsconf/rnc.c +++ b/src/tools/ddsconf/rnc.c @@ -158,6 +158,6 @@ int printrnc(FILE *out, struct cfgelem *elem, const struct cfgunit *units) static const char *fmt = " %s = xsd:token { pattern = \"%s\" }\n"; print(out, 0, fmt, cu->name, cu->pattern); } - print(out, 0, "}"); + print(out, 0, "}\n"); return 0; } diff --git a/src/tools/ddsconf/xsd.c b/src/tools/ddsconf/xsd.c index 95c1310..3f57f8f 100644 --- a/src/tools/ddsconf/xsd.c +++ b/src/tools/ddsconf/xsd.c @@ -165,7 +165,7 @@ printref( snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem)); if (!(flags & FLAG_NOMAX) && maximum(elem) == 0) snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" "); - else if (!(FLAG_NOMAX) && maximum(elem) != 1) + else if (!(flags & FLAG_NOMAX) && maximum(elem) != 1) snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem)); print(out, cols, fmt, minattr, maxattr, schema(), name(elem)); } @@ -185,11 +185,11 @@ printcomplextype( assert(!ismoved(elem) && !isdeprecated(elem)); if (flags & FLAG_REFERENCE) { - if (minimum(elem) != 1) + if (!(flags & FLAG_NOMIN) && minimum(elem) != 1) snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem)); - if (maximum(elem) == 0) + if (!(flags & FLAG_NOMAX) && maximum(elem) == 0) snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" "); - else if (maximum(elem) != 1) + else if (!(flags & FLAG_NOMAX) && maximum(elem) != 1) snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem)); } @@ -208,58 +208,63 @@ printcomplextype( if ((cnt = haschildren(elem))) { const char *cont = NULL; struct cfgelem *ce; - int min[2], max[2], eq[2] = { 1, 1 }; + int min[3], max[3]; + int mineq, maxeq; assert(isgroup(elem)); minattr[0] = '\0'; maxattr[0] = '\0'; if (cnt == 1) { - /* minOccurs and maxOccurs placed in element */ cont = "sequence"; } else { assert(cnt > 1); ce = firstelem(elem->children); - assert(ce); - min[0] = min[1] = minimum(ce); - max[0] = max[1] = maximum(ce); + min[0] = min[1] = min[2] = minimum(ce); + max[0] = max[1] = max[2] = maximum(ce); assert(min[1] <= max[1] || max[1] == 0); + mineq = maxeq = 1; ce = nextelem(elem->children, ce); assert(ce); while (ce) { min[1] = minimum(ce); max[1] = maximum(ce); assert(min[1] <= max[1] || max[1] == 0); + min[2] += minimum(ce); + max[2] += maximum(ce); if (min[1] != min[0]) { - eq[0] = 0; - if (min[1] < min[0]) + mineq = 0; + if (min[1] > min[0]) min[0] = min[1]; } if (max[1] != max[0]) { - eq[1] = 0; + maxeq = 0; if ((max[0] != 0 && max[1] > max[0]) || max[1] == 0) max[0] = max[1]; } ce = nextelem(elem->children, ce); } - if (min[0] > 1 || max[0] != 1 /* unbounded or >1 */) { - cont = "choice"; - if (eq[0]) { - if (min[0] != 1) - snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[0]); - flags |= FLAG_NOMIN; - } - if (eq[1]) { - if (max[0] == 0) - snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\""); - else if (max[0] != 1) - snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[0]); - flags |= FLAG_NOMAX; - } - } else { + /* xsd generation becomes significantly more difficult if the minimum + number of occurences for an element is more non-zero and the + maximum number of occurences of it (or one of its siblings) is more + than one, but that is not likely to occur */ + if (min[0] <= 1 && max[0] == 1) { /* any order, each zero or one time */ - /* minOccurs placed in element maxOccurs always one */ cont = "all"; + } else { + cont = "choice"; + if (min[0] == 0) + snprintf(minattr, sizeof(minattr), " minOccurs=\"0\""); + else if (min[0] != 1) /* incorrect, but make the most of it */ + snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[2]); + if (max[0] == 0) + snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\""); + else if (max[0] != 1) + snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[2]); + if (mineq) + flags |= FLAG_NOMIN; + if (maxeq) + flags |= FLAG_NOMAX; } } @@ -270,7 +275,6 @@ printcomplextype( ce = nextelem(elem->children, ce); } print(out, cols+4, "\n", cont); - flags &= ~(FLAG_NOMIN | FLAG_NOMAX); } else if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) { ofst = 4; print(out, cols+4, "