diff --git a/sdf/1.12/CMakeLists.txt b/sdf/1.12/CMakeLists.txt index 9d02c42bf..1f1ceb3dc 100644 --- a/sdf/1.12/CMakeLists.txt +++ b/sdf/1.12/CMakeLists.txt @@ -94,6 +94,11 @@ add_custom_command( list(APPEND SDF_SCHEMA "${CMAKE_CURRENT_BINARY_DIR}/types.xsd") +configure_file( + ${CMAKE_SOURCE_DIR}/sdf/catalog.xml.in + ${CMAKE_CURRENT_BINARY_DIR}/catalog.xml + @ONLY) + add_custom_target(schema1_12 ALL DEPENDS ${SDF_SCHEMA}) set_source_files_properties(${SDF_SCHEMA} PROPERTIES GENERATED TRUE) diff --git a/sdf/catalog.xml.in b/sdf/catalog.xml.in new file mode 100644 index 000000000..e099fb3b7 --- /dev/null +++ b/sdf/catalog.xml.in @@ -0,0 +1,5 @@ + + + + + diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index afaa83ad2..42c236d4c 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -71,31 +71,7 @@ endif() find_program(XMLLINT_EXE xmllint) if (EXISTS ${XMLLINT_EXE}) - # get xmllint version - execute_process( - COMMAND ${XMLLINT_EXE} --version - ERROR_VARIABLE XMLLINT_VERSION_OUTPUT - ) - # typical output: "/usr/bin/xmllint: using libxml version 20913\ncompiled with: ..." - # version is formatted as a single integer, with 20913 representing 2.9.13 - string(REGEX MATCH "using libxml version [0-9]+" XMLLINT_VERSION_STRING "${XMLLINT_VERSION_OUTPUT}") - string(REPLACE "using libxml version " "" XMLLINT_VERSION "${XMLLINT_VERSION_STRING}") - if("${XMLLINT_VERSION}" STREQUAL "") - message(WARNING "Unable to identify xmllint version. schema_test won't be run") - elseif("${XMLLINT_VERSION}" LESS 10000) - message(WARNING "Old version of xmllint (${XMLLINT_VERSION_STRING}) detected. schema_test won't be run") - elseif("${XMLLINT_VERSION}" LESS 21500) - # schema test is broken with very new versions of xmllint - # https://github.com/gazebosim/sdformat/issues/1656 - # enable schema test if xmllint is older than 2.15.0 - set (tests ${tests} schema_test.cc) - else() - # TODO: convert to WARNING when https://github.com/gazebosim/sdformat/issues/1656 - # is resolved on supported platforms. It currently fails on Ubuntu 26.04 - message(STATUS "WARNING: xmllint version (${XMLLINT_VERSION}) is too new; " - "schema_test won't be run. " - "See https://github.com/gazebosim/sdformat/issues/1656") - endif() + set (tests ${tests} schema_test.cc) else() message(WARNING "xmllint not found. schema_test won't be run") endif() @@ -111,6 +87,7 @@ if (TARGET ${TEST_TYPE}_schema_test) target_compile_definitions(${TEST_TYPE}_schema_test PRIVATE -DSDF_ROOT_SCHEMA="${PROJECT_BINARY_DIR}/sdf/${SDF_PROTOCOL_VERSION}/root.xsd" + -DSDF_XML_CATALOG="${PROJECT_BINARY_DIR}/sdf/${SDF_PROTOCOL_VERSION}/catalog.xml" ) if (EXISTS ${XMLLINT_EXE}) diff --git a/test/integration/schema_test.cc b/test/integration/schema_test.cc index 7a8392d53..389d12b13 100644 --- a/test/integration/schema_test.cc +++ b/test/integration/schema_test.cc @@ -21,6 +21,8 @@ #include +#include + #include "sdf/parser.hh" #include "test_config.hh" @@ -31,8 +33,10 @@ class SDFSchemaGenerator : public testing::Test public: void runXMLlint(const std::string & model) { - const auto sdfRootSchema = sdf::filesystem::append(SDF_ROOT_SCHEMA); - std::string xmllintCmd = "xmllint --noout --schema " + + const std::string sdfCatalog = SDF_XML_CATALOG; + gz::utils::setenv("XML_CATALOG_FILES", sdfCatalog.c_str()); + const std::string sdfRootSchema = SDF_ROOT_SCHEMA; + std::string xmllintCmd = "xmllint --catalogs --noout --schema " + sdfRootSchema + " " + model; std::cout << "CMD[" << xmllintCmd << "]\n"; if (system(xmllintCmd.c_str()) != 0) diff --git a/tools/xmlschema.py b/tools/xmlschema.py index b3518f9ec..c638854a1 100755 --- a/tools/xmlschema.py +++ b/tools/xmlschema.py @@ -128,7 +128,15 @@ def print_include_ref(element: ElementTree.Element, sdf_root_dir: str) -> List[s include_tree = ElementTree.parse(sdf_path) root = include_tree.getroot() include_element_name = root.attrib["name"] - lines.append(f"") + + elem_reqd = get_attribute(element, "required") + if elem_reqd: + min_occurs, max_occurs = SDF_REQUIRED_TO_MIN_MAX_OCCURS[elem_reqd] + lines.append(f"") + lines.append(f" ") + lines.append("") + else: + lines.append(f"") return lines @@ -147,7 +155,7 @@ def print_plugin_element(element: ElementTree.Element) -> List[str]: return lines -def print_element(element: ElementTree.Element) -> List[str]: +def print_element(element: ElementTree.Element, sdf_root_dir: str) -> List[str]: """ Print a child element of the sdf definition """ @@ -160,30 +168,58 @@ def print_element(element: ElementTree.Element) -> List[str]: if elem_type and is_std_type(elem_type): elem_type = xsd_type_string(elem_type) + elements = element.findall("element") + attributes = element.findall("attribute") + includes = element.findall("include") + if not elem_reqd: raise RuntimeError("Cannot process element missing 'required' attribute") min_occurs, max_occurs = SDF_REQUIRED_TO_MIN_MAX_OCCURS[elem_reqd] lines.append(f"") - if elem_type is None: + complex_type = len(elements) > 0 or len(attributes) > 0 or len(includes) > 0 + + if not complex_type and elem_type: + lines.append(f"") + else: lines.append(f"") - lines.extend(indent_lines(print_documentation(element), 2)) + + lines.extend(indent_lines(print_documentation(element), 2)) + + if complex_type: lines.append(" ") - lines.append(" ") - for child_element in element.findall("element"): - lines.extend(indent_lines(print_element(child_element), 6)) + if elem_type: + lines.append(" ") + lines.append(f" ") + for attribute in attributes: + lines.extend(indent_lines(print_attribute(attribute), 8)) + lines.append(" ") + lines.append(" ") + else: + if len(elements) or len(includes): + lines.append(" ") + + for child_element in elements: + if "copy_data" in child_element.attrib: + element_lines = print_plugin_element(child_element) + lines.extend(indent_lines(element_lines, 4)) + else: + element_lines = print_element(child_element, sdf_root_dir) + lines.extend(indent_lines(element_lines, 6)) + + for include_element in includes: + element_lines = print_include_ref(include_element, sdf_root_dir) + lines.extend(indent_lines(element_lines, 6)) - lines.append(" ") + if len(elements) or len(includes): + lines.append(" ") - for attribute in element.findall("attribute"): - lines.extend(indent_lines(print_attribute(attribute), 4)) + for attribute in attributes: + lines.extend(indent_lines(print_attribute(attribute), 4)) lines.append(" ") - else: - lines.append(f"") - lines.extend(indent_lines(print_documentation(element), 2)) lines.append("") lines.append("") @@ -231,6 +267,9 @@ def print_xsd(element: ElementTree.Element, sdf_root_dir: str) -> List[str]: elem_name = get_attribute(element, "name") elem_type = get_attribute(element, "type") + if elem_type and is_std_type(elem_type): + elem_type = xsd_type_string(elem_type) + elements = element.findall("element") attributes = element.findall("attribute") includes = element.findall("include") @@ -240,44 +279,57 @@ def print_xsd(element: ElementTree.Element, sdf_root_dir: str) -> List[str]: "" ) - # Reference any includes in the SDF file - for include in includes: - lines.extend(print_include(include)) + # Reference all includes in the SDF file (including nested ones) + all_includes = element.findall(".//include") + included_files = set() + for include in all_includes: + filename = get_attribute(include, "filename") + if filename and filename not in included_files: + included_files.add(filename) + lines.extend(print_include(include)) - if len(elements) or len(attributes) or len(includes): + complex_type = len(elements) > 0 or len(attributes) > 0 or len(includes) > 0 + + if not complex_type and elem_type: + lines.append(f"") + else: lines.append(f"") - lines.append(" ") - if elem_name != "plugin" and (len(elements) or len(includes)): - lines.append(" ") + if complex_type: + lines.append(" ") - for child_element in elements: - if "copy_data" in child_element.attrib: - element_lines = print_plugin_element(child_element) - lines.extend(indent_lines(element_lines, 4)) - else: - element_lines = print_element(child_element) + if elem_type: + lines.append(" ") + lines.append(f" ") + for attribute in attributes: + lines.extend(indent_lines(print_attribute(attribute), 8)) + lines.append(" ") + lines.append(" ") + else: + if elem_name != "plugin" and (len(elements) or len(includes)): + lines.append(" ") + + for child_element in elements: + if "copy_data" in child_element.attrib: + element_lines = print_plugin_element(child_element) + lines.extend(indent_lines(element_lines, 4)) + else: + element_lines = print_element(child_element, sdf_root_dir) + lines.extend(indent_lines(element_lines, 6)) + + for include_element in includes: + element_lines = print_include_ref(include_element, sdf_root_dir) lines.extend(indent_lines(element_lines, 6)) - for include_element in includes: - element_lines = print_include_ref(include_element, sdf_root_dir) - lines.extend(indent_lines(element_lines, 6)) - - if elem_name != "plugin" and (len(elements) or len(includes)): - lines.append(" ") + if elem_name != "plugin" and (len(elements) or len(includes)): + lines.append(" ") - for attribute_element in attributes: - lines.extend(indent_lines(print_attribute(attribute_element), 4)) + for attribute in attributes: + lines.extend(indent_lines(print_attribute(attribute), 4)) lines.append(" ") - lines.append("") - else: - if elem_type and is_std_type(elem_type): - elem_type = f' type={xsd_type_string(elem_type)}' - else: - elem_type = "" + lines.append("") - lines.append(f"") return lines