From: Jean Privat Date: Mon, 10 Nov 2014 23:35:07 +0000 (-0500) Subject: Merge: MModule has a c_name X-Git-Tag: v0.6.11~39 X-Git-Url: http://nitlanguage.org?hp=89cd67027c083b970d64dc9c97d627f7a3f5870d Merge: MModule has a c_name In compiler/, all entities have a `c_name` method to get a fully qualified mangled C identifier also usable to name files. All, except for one small class of indomitable entities that still holds out against the namespaces. `c_name` where developed before the proposition of projects. This PR add a `c_name` for mmodules and update the places where its simple name was used. Pull-Request: #898 Reviewed-by: Alexis Laferrière --- diff --git a/contrib/nitester/src/nitester.nit b/contrib/nitester/src/nitester.nit index 5edfe22..37b5594 100644 --- a/contrib/nitester/src/nitester.nit +++ b/contrib/nitester/src/nitester.nit @@ -62,7 +62,7 @@ abstract class Processor var done_tag: Tag = 5.tag # Number of tasks within each task assignation with `task_tag` - var tasks_per_packet = 4 + var tasks_per_packet = 1 # Run the main logic of this node fun run is abstract @@ -163,10 +163,8 @@ abstract class Processor # Gather and registar all tasks fun create_tasks do - var c = 0 - for engine in engines do for prog in test_programs do + for prog in test_programs do for engine in engines do tasks.add new Task(engine, prog) - c += 1 end end end @@ -250,6 +248,8 @@ class Controller if res == 5 then result.fail = true if res == 6 then result.soso = true if res == 7 then result.skip = true + if res == 8 then result.todo = true + if res == 9 then result.skip_exec = true if res == 0 then result.unknown = true results.add result @@ -305,6 +305,8 @@ class Controller print "* {results.fixmes.length} fixmes" print "* {results.sosos.length} sosos" print "* {results.skips.length} skips" + print "* {results.todos.length} todos" + print "* {results.skip_execs.length} skip execs" print "* {results.unknowns.length} unknowns (bug in tests.sh or nitester)" end @@ -332,12 +334,12 @@ class Worker # Output file directory var out_dir = "/dev/shm/nit_out{rank}" is lazy + # Directory to store the xml files produced for Jenkins + var xml_dir = "~/jenkins_xml/" + # Output file of the `tests.sh` script var tests_sh_out = "/dev/shm/nit_local_out{rank}" is lazy - # Path to the local copy of the Nit repository - var nit_copy_dir = "/dev/shm/nit{rank}/" is lazy - # Source Nit repository, must be already updated and `make` before execution var nit_source_dir = "~/nit" @@ -362,7 +364,6 @@ class Worker fun setup do if verbose > 0 then sys.system "hostname" - sys.system "git clone {nit_source_dir} {nit_copy_dir}" end # Clean up the testing environment @@ -372,7 +373,6 @@ class Worker do if comp_dir.file_exists then comp_dir.rmdir if out_dir.file_exists then out_dir.rmdir - if nit_copy_dir.file_exists then nit_copy_dir.rmdir if tests_sh_out.file_exists then tests_sh_out.file_delete end @@ -395,17 +395,17 @@ class Worker # Receive tasks to execute mpi.recv_into(task_buffer, 0, 1, status.source, status.tag, comm_world) var first_id = task_buffer[0] - for task_id in [first_id .. first_id + tasks_per_packet] do + for task_id in [first_id .. first_id + tasks_per_packet[ do # If id is over all known tasks, stop right here if task_id >= tasks.length then break var task = tasks[task_id] # Command line to execute test - var cmd = "XMLDIR={out_dir} ERRLIST={out_dir}/errlist TMPDIR={out_dir} " + + var cmd = "XMLDIR={xml_dir} ERRLIST={out_dir}/errlist TMPDIR={out_dir} " + "CCACHE_DIR={ccache_dir} CCACHE_TEMPDIR={ccache_dir} CCACHE_BASEDIR={comp_dir} " + - "./tests.sh --compdir {comp_dir} --outdir {out_dir} -o \"--make-flags '-j1'\"" + - " --node --engine {task.engine} {nit_copy_dir / "tests" / task.test_program} > {tests_sh_out}" + "./tests.sh --compdir {comp_dir} --outdir {out_dir} " + + " --node --engine {task.engine} {task.test_program} > {tests_sh_out}" # Execute test sys.system cmd @@ -446,6 +446,8 @@ class Worker if line.has("[======= fail") then res = 5 if line.has("[======= soso") then res = 6 if line.has("[skip]") then res = 7 + if line.has("[todo]") then res = 8 + if line.has("[skip exec]") then res = 9 if res == null then res = 0 @@ -552,9 +554,15 @@ class Result # Is `self` result a _soso_? var soso = false - # Is `self` skipped test? + # Has `self` been skipped? var skip = false + # Is `self` TODO? + var todo = false + + # Has the execution of `self` been skipped? + var skip_exec = false + # Is `self` an unknown result, probably an error var unknown = false @@ -566,6 +574,10 @@ class Result if ok_empty then err = "0k" if fixme then err = "fixme" if fail then err = "fail" + if soso then err = "soso" + if skip then err = "skip" + if todo then err = "todo" + if skip_exec then err = "skip_exec" return "{task} arg{arg} alt{alt} => {err}" end @@ -582,6 +594,8 @@ class ResultSet var fails = new HashSet[Result] var sosos = new HashSet[Result] var skips = new HashSet[Result] + var todos = new HashSet[Result] + var skip_execs = new HashSet[Result] var unknowns = new HashSet[Result] # TODO remove @@ -596,6 +610,8 @@ class ResultSet if result.fail then fails.add result if result.soso then sosos.add result if result.skip then skips.add result + if result.todo then todos.add result + if result.skip_exec then skip_execs.add result if result.unknown then unknowns.add result super diff --git a/lib/ai/examples/puzzle.nit b/lib/ai/examples/puzzle.nit index 782fee6..d07c1be 100644 --- a/lib/ai/examples/puzzle.nit +++ b/lib/ai/examples/puzzle.nit @@ -285,6 +285,6 @@ for arg in args do break end - print "Solved, after looking at {r.steps} positions during {c.lapse}" + print "Solved, after looking at {r.steps} positions" pb.print_plan(r.plan) end diff --git a/lib/neo4j/json_store.nit b/lib/neo4j/json_store.nit new file mode 100644 index 0000000..49e055c --- /dev/null +++ b/lib/neo4j/json_store.nit @@ -0,0 +1,199 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Uses JSON as a storage medium for a Neo4j subgraph. +module neo4j::json_store + +import neo4j +private import template + +# A Neo4j graph that uses as a storage medium. +# +# The graph is stored as a JSON object with the following properties: +# +# * `"nodes"`: An array with all nodes. Each node is an object with the +# following properties: +# * `"id"`: The ID (`Int`) that uniquely identifies the node in the current +# graph. +# * `"labels"`: An array of all applied labels. +# * `"properties"`: An object mapping each defined property to its value. +# * `"links"`: An array with all relationships. Each relationship is an object +# with the following properties: +# * `"type"`: The type (`String`) of the relationship. +# * `"properties"`: An object mapping each defined property to its value. +# * `"from"`: The ID (`Int`) of the source node. +# * `"to"`: The ID (`Int`) of the destination node. +# +# TODO Refine the graph API instead when it will be available. +class JsonGraph + super Jsonable + + # All nodes in the graph. + var nodes: SimpleCollection[NeoNode] = new Array[NeoNode] + + # All relationships in the graph. + var links: SimpleCollection[NeoEdge] = new Array[NeoEdge] + + # Create an empty graph. + init do end + + # Retrieve the graph from the specified JSON value. + # + # var graph = new JsonGraph + # var a = new NeoNode + # a.labels.add "Foo" + # a["answer"] = 42 + # a["Ultimate question of"] = new JsonArray.from(["life", + # "the Universe", "and Everything."]) + # graph.nodes.add a + # var b = new NeoNode + # b.labels.add "Foo" + # b.labels.add "Bar" + # graph.nodes.add b + # graph.links.add new NeoEdge(a, "BAZ", b) + # # + # graph = new JsonGraph.from_json(graph.to_json) + # assert 1 == graph.links.length + # for link in graph.links do + # assert "BAZ" == link.rel_type + # assert a.labels == link.from.labels + # for k, v in a.properties do assert v == link.from.properties[k] + # assert b.labels == link.to.labels + # for k, v in b.properties do assert v == link.to.properties[k] + # end + # assert 2 == graph.nodes.length + init from_json(t: Text) do + from_json_object(t.to_jsonable.as(JsonObject)) + end + + # Retrieve the graph from the specified JSON object. + init from_json_object(o: JsonObject) do + var node_by_id = new HashMap[Int, NeoNode] + var nodes = o["nodes"].as(JsonArray) + for json_node in nodes do + assert json_node isa JsonObject + var node = new NeoNode.from_json_object(json_node) + node_by_id[json_node["id"].as(Int)] = node + self.nodes.add node + end + var links = o["links"].as(JsonArray) + for json_link in links do + assert json_link isa JsonObject + var from = node_by_id[json_link["from"].as(Int)] + var to = node_by_id[json_link["to"].as(Int)] + var rel_type = json_link["type"].as(String) + var json_properties = json_link["properties"].as(JsonObject) + var link = new NeoEdge(from, rel_type, to) + link.properties.recover_with(json_properties) + self.links.add link + end + end + + redef fun to_json do + var t = new Template + t.add "\{\"nodes\":[" + var i = 0 + for n in nodes do + if i > 0 then t.add "," + t.add n.to_json + i += 1 + end + t.add "],\"links\":[" + i = 0 + for link in links do + if i > 0 then t.add "," + t.add link.to_json + i += 1 + end + t.add "]\}" + return t.write_to_string + end +end + +# Make `NeoNode` `Jsonable`. +redef class NeoNode + super Jsonable + + # Retrieve the node from the specified JSON value. + # + # Note: Here, the `"id"` is optional and ignored. + # + # SEE: `JsonGraph` + # + # var node = new NeoNode.from_json(""" + # { + # "labels": ["foo", "Bar"], + # "properties": { + # "baz": 42 + # } + # } + # """) + # assert ["foo", "Bar"] == node.labels + # assert 42 == node["baz"] + init from_json(t: Text) do + from_json_object(t.to_jsonable.as(JsonObject)) + end + + # Retrieve the node from the specified JSON value. + # + # Note: Here, the `"id"` is optional and ignored. + # + # SEE: `JsonGraph` + init from_json_object(o: JsonObject) do + init + var labels = o["labels"].as(JsonArray) + for lab in labels do self.labels.add(lab.as(String)) + var json_properties = o["properties"].as(JsonObject) + properties.recover_with(json_properties) + end + + # Get the JSON representation of `self`. + # + # SEE: `JsonGraph` + redef fun to_json do + var t = new Template + t.add "\{\"id\":" + t.add object_id.to_json + t.add ",\"labels\":[" + var i = 0 + for lab in labels do + if i > 0 then t.add "," + t.add lab.to_json + i += 1 + end + t.add "],\"properties\":" + t.add properties.to_json + t.add "}" + return t.write_to_string + end + + redef fun to_s do return to_json +end + +# Make `NeoEdge` `Jsonable`. +redef class NeoEdge + super Jsonable + + redef fun to_json do + var t = new Template + t.add "\{\"type\":" + t.add rel_type.to_json + t.add ",\"properties\":" + t.add properties.to_json + t.add ",\"from\":" + t.add from.object_id.to_json + t.add ",\"to\":" + t.add to.object_id.to_json + t.add "}" + return t.write_to_string + end + + redef fun to_s do return to_json +end diff --git a/lib/neo4j/neo4j.nit b/lib/neo4j/neo4j.nit index 6629b80..b84a60b 100644 --- a/lib/neo4j/neo4j.nit +++ b/lib/neo4j/neo4j.nit @@ -300,7 +300,7 @@ class Neo4jClient # Perform a `CypherQuery` # see: CypherQuery fun cypher(query: CypherQuery): Jsonable do - return post("{cypher_url}", query.to_json) + return post("{cypher_url}", query.to_rest) end # GET JSON data from `url` @@ -436,8 +436,8 @@ class CypherQuery return self end - # Translate the query to JSON - fun to_json: JsonObject do + # Translate the query to the body of a corresponding Neo4j REST request. + fun to_rest: JsonObject do var obj = new JsonObject obj["query"] = query if not params.is_empty then @@ -446,7 +446,7 @@ class CypherQuery return obj end - redef fun to_s do return to_json.to_s + redef fun to_s do return to_rest.to_s end # The fundamental units that form a graph are nodes and relationships. @@ -546,9 +546,6 @@ abstract class NeoEntity # Is the property `key` set? fun has_key(key: String): Bool do return properties.has_key(key) - - # Translate `self` to JSON - fun to_json: JsonObject do return properties end # Nodes are used to represent entities stored in base. @@ -595,7 +592,7 @@ class NeoNode var tpl = new FlatBuffer tpl.append "\{" tpl.append "labels: [{labels.join(", ")}]," - tpl.append "data: {to_json}" + tpl.append "data: {properties.to_json}" tpl.append "\}" return tpl.write_to_string end @@ -743,7 +740,8 @@ class NeoEdge # Get edge type fun rel_type: nullable String do return internal_type - redef fun to_json do + # Get the JSON body of a REST request that create the relationship. + private fun to_rest: JsonObject do var obj = new JsonObject if to.is_linked then obj["to"] = to.url @@ -891,7 +889,7 @@ class NeoBatch else job.to = "\{{edge.from.batch_id.to_s}\}/relationships" end - job.body = edge.to_json + job.body = edge.to_rest end # Create multiple edges @@ -902,7 +900,7 @@ class NeoBatch var request = new JsonPOST(client.batch_url, client.curl) # request.headers["X-Stream"] = "true" var json_jobs = new JsonArray - for job in jobs.values do json_jobs.add job.to_json + for job in jobs.values do json_jobs.add job.to_rest request.data = json_jobs var response = request.execute var res = client.parse_response(response) @@ -1003,7 +1001,7 @@ class NeoJob var body: nullable Jsonable = null # JSON formated job - fun to_json: JsonObject do + fun to_rest: JsonObject do var job = new JsonObject job["id"] = id job["method"] = method diff --git a/misc/jenkins/nitester-wrapper.sh b/misc/jenkins/nitester-wrapper.sh new file mode 100755 index 0000000..2f9e20f --- /dev/null +++ b/misc/jenkins/nitester-wrapper.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script is a wrapper for `nitester` which also manages a local repo +# +# The first argument _must_ be the hash of the commit at the head of the +# branch to test. The other arguments are passed on to `nitester`. + +hash=$1 +shift + +set +x + +local_repo=nit/ +remote_repo=privat + +tools_dir=misc/jenkins/ + +cd $local_repo +git clean -fdxq . + +git fetch $remote_repo +git checkout $hash + +# Make nitg and tools +$tools_dir/unitrun.sh "run-make-0initial_make" make + +# Make nitester +$tools_dir/unitrun.sh "run-make-nitester" make -C contrib/nitester/ + +# Run tests +cd tests +mkdir -p out +rm ~/jenkins_xml/*.xml +mpirun -np 30 ../contrib/nitester/bin/nitester $@ diff --git a/misc/jenkins/unitrun.sh b/misc/jenkins/unitrun.sh index 54d875c..10dfd38 100755 --- a/misc/jenkins/unitrun.sh +++ b/misc/jenkins/unitrun.sh @@ -24,9 +24,18 @@ fi name=$1 shift +# Detect a working time command +if env time --quiet -f%U true 2>/dev/null; then + TIME="env time --quiet -f%U -o '${name}.t.out'" +elif env time -f%U true 2>/dev/null; then + TIME="env time -f%U -o '${name}.t.out'" +else + TIME= +fi + # Magic here! This tee and save both stdout and stderr in distinct files without messing with them # Time just get the user time -/usr/bin/time -f%U --quiet -o "${name}.t.out" "$@" > >(tee "${name}.out") 2> >(tee "${name}.2.out" >&2) +$TIME "$@" > >(tee "${name}.out") 2> >(tee "${name}.2.out" >&2) res=$? c=`echo "${name%-*}" | tr "-" "."` diff --git a/src/doc/doc_pages.nit b/src/doc/doc_pages.nit index db5ba9b..5d71b02 100644 --- a/src/doc/doc_pages.nit +++ b/src/doc/doc_pages.nit @@ -997,15 +997,15 @@ class NitdocModule # build graph var op = new RopeBuffer var name = "dep_module_{mmodule.nitdoc_id}" - op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") + op.append("digraph \"{name.escape_to_dot}\" \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") for mmodule in poset do if mmodule == self.mmodule then - op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n") + op.append("\"{mmodule.name.escape_to_dot}\"[shape=box,margin=0.03];\n") else - op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n") + op.append("\"{mmodule.name.escape_to_dot}\"[URL=\"{mmodule.nitdoc_url.escape_to_dot}\"];\n") end for omodule in poset[mmodule].direct_greaters do - op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n") + op.append("\"{mmodule.name.escape_to_dot}\"->\"{omodule.name.escape_to_dot}\";\n") end end op.append("\}\n") @@ -1378,7 +1378,7 @@ class NitdocClass var op = new RopeBuffer var name = "dep_class_{mclass.nitdoc_id}" - op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") + op.append("digraph \"{name.escape_to_dot}\" \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") var classes = poset.to_a var todo = new Array[MClass] var done = new HashSet[MClass] @@ -1389,18 +1389,18 @@ class NitdocClass if done.has(c) then continue done.add c if c == mclass then - op.append("\"{c.name}\"[shape=box,margin=0.03];\n") + op.append("\"{c.name.escape_to_dot}\"[shape=box,margin=0.03];\n") else - op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n") + op.append("\"{c.name.escape_to_dot}\"[URL=\"{c.nitdoc_url.escape_to_dot}\"];\n") end var smallers = poset[c].direct_smallers if smallers.length < 10 then for c2 in smallers do - op.append("\"{c2.name}\"->\"{c.name}\";\n") + op.append("\"{c2.name.escape_to_dot}\"->\"{c.name.escape_to_dot}\";\n") end todo.add_all smallers else - op.append("\"...\"->\"{c.name}\";\n") + op.append("\"...\"->\"{c.name.escape_to_dot}\";\n") end end op.append("\}\n") diff --git a/src/metrics/generate_hierarchies.nit b/src/metrics/generate_hierarchies.nit index e902b05..3522245 100644 --- a/src/metrics/generate_hierarchies.nit +++ b/src/metrics/generate_hierarchies.nit @@ -53,12 +53,12 @@ do dot.mprojects.add(g.mproject) end var projectpath = toolcontext.output_dir.join_path("project_hierarchy.dot") - print "generating {projectpath}" + print "generating project_hierarchy.dot" dot.write_to_file(projectpath) var modulepath = toolcontext.output_dir.join_path("module_hierarchy.dot") dot.mprojects.add_all(model.mprojects) - print "generating {modulepath}" + print "generating module_hierarchy.dot" dot.write_to_file(modulepath) end diff --git a/src/nitunit.nit b/src/nitunit.nit index 0a8973d..c057beb 100644 --- a/src/nitunit.nit +++ b/src/nitunit.nit @@ -73,9 +73,8 @@ end var file = toolcontext.opt_output.value if file == null then file = "nitunit.xml" page.write_to_file(file) -print "Results saved in {file}" # print docunits results -print "\nDocUnits:" +print "DocUnits:" if modelbuilder.unit_entities == 0 then print "No doc units found" else if modelbuilder.failed_entities == 0 and not toolcontext.opt_noact.value then diff --git a/tests/listfull.sh b/tests/listfull.sh index 087e573..778d0d3 100755 --- a/tests/listfull.sh +++ b/tests/listfull.sh @@ -1,5 +1,7 @@ #!/bin/sh -printf "%s\n" "$@" *.nit \ +printf "%s\n" "$@" \ + ../src/nit*.nit \ + ../src/test_*.nit \ ../examples/*.nit \ ../examples/*/*.nit \ ../examples/shoot/src/shoot_logic.nit \ @@ -12,5 +14,4 @@ printf "%s\n" "$@" *.nit \ ../lib/*/examples/*.nit \ ../contrib/friendz/src/solver_cmd.nit \ ../contrib/pep8analysis/src/pep8analysis.nit \ - ../src/nit*.nit \ - ../src/test_*.nit + *.nit diff --git a/tests/sav/nitmetrics_args1.res b/tests/sav/nitmetrics_args1.res index 481e468..e5ac8bd 100644 --- a/tests/sav/nitmetrics_args1.res +++ b/tests/sav/nitmetrics_args1.res @@ -470,8 +470,8 @@ Average number of composing class definition by runtime class: 2.00 Total size of tables (classes and instances): 38 (not including stuff like info for subtyping or call-next-method) Average size of table by runtime class: 6.33 Values never redefined: 32 (84.21%) -generating out/nitmetrics_args1.write/project_hierarchy.dot -generating out/nitmetrics_args1.write/module_hierarchy.dot +generating project_hierarchy.dot +generating module_hierarchy.dot # Inheritance metrics diff --git a/tests/sav/nitunit_args1.res b/tests/sav/nitunit_args1.res index 3b5b590..3a07d20 100644 --- a/tests/sav/nitunit_args1.res +++ b/tests/sav/nitunit_args1.res @@ -4,8 +4,6 @@ test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_ test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/test_test_nitunit_TestX_test_foo1.nit): Runtime error: Assert failed (test_test_nitunit.nit:39) -Results saved in out/nitunit_args1.write - DocUnits: Entities: 27; Documented ones: 3; With nitunits: 3; Failures: 2 diff --git a/tests/sav/puzzle_args1.res b/tests/sav/puzzle_args1.res index 17ccd5a..6c361fe 100644 --- a/tests/sav/puzzle_args1.res +++ b/tests/sav/puzzle_args1.res @@ -2,5 +2,5 @@ Initial: eabf.cdgh eab f.c dgh -Solved, after looking at 14 positions during 0.0s +Solved, after looking at 14 positions Solution in 10 moves: right(>) down(v) left(<) left(<) up(^) right(>) right(>) up(^) left(<) left(<) diff --git a/tests/tests.sh b/tests/tests.sh index 8179c9c..76ecf2c 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -457,7 +457,9 @@ todos="" if [ "x$XMLDIR" = "x" ]; then xml="tests-$engine.xml" else - xml="$XMLDIR/tests-$engine.xml" + sum=`echo $@ | md5sum | cut -f1 -d " "` + xml="$XMLDIR/tests-$engine-$sum.xml" + mkdir -p "$XMLDIR" fi echo >$xml ""