Repository: ros/ros_comm

Issue:
Unexpected $(dirname) behaviour when value assigned to arg.
The existing test is here, added as part of the overall feature in #1103: 

https://github.com/ros/ros_comm/blob/e96c407c64e1c17b0dd2bb85b67f388380527097/tools/roslaunch/test/xml/test-dirname.xml

If the test is augmented to be:

```
<launch>
  <param name="foo" value="$(dirname)/bar" />
  <include file="$(dirname)/test-dirname/included.xml">
    <arg name="baz" value="$(dirname)/baz" />
  </include>
</launch>
```

And then the `included.xml` file is changed to:

```
<launch>
  <arg name="baz" />

  <param name="bar" value="$(dirname)/baz" />
  <param name="baz" value="$(arg baz)" />
</launch>
```

You'd expect the `baz` parameter to have `tools/roslaunch/test/xml/baz` as its path, but instead it's `tools/roslaunch/test/xml/test-dirname/baz`. That is, dirname is being lazily resolved at the point where the value is assigned to the parameter, rather than when the arg value is assigned. Historically this hasn't mattered, as no other substitutions have this kind of context sensitivity.

I'm not sure how much effort this will be to fix, but I had an instance recently where it would have been nice to be able to pass a "back path" into an included file, and wasn't able to do so due to this problem.


Discussion:
this indeed is a issue
This is entirely unintuitive behavior demoting the use of the (in ROS terms) relatively new `$(dirname)`.
Sad to see this unresolved three years after Mike went through the trouble to document it...
@v4hn In fairness to the maintainers of this repo, I am in fact the author of the original `$(dirname)` patch, so this ticket was as much about self-disclosure as it was anything else. :) We ended up reworking our launchfiles to not require this fixed, but I left the ticket up in case someone else was interested in tackling it.
> I am in fact the author of the original `$(dirname)` patch,

Thanks! Without this bug, it would be an even more useful command, though it already helps with relative includes. :sunglasses:

> We ended up reworking our launchfiles to not require this fixed, but I left the ticket up in case someone else was interested in tackling it.

Yeah, the usual lot of bugs that are relatively easy to work around...
They just never disappear unless some good soul has the interest and time to fix it.


Resolving patch:
diff --git a/tools/roslaunch/src/roslaunch/xmlloader.py b/tools/roslaunch/src/roslaunch/xmlloader.py
--- a/tools/roslaunch/src/roslaunch/xmlloader.py
+++ b/tools/roslaunch/src/roslaunch/xmlloader.py
@@ -613,6 +613,7 @@ def _include_tag(self, tag, context, ros_config, default_machine, is_core, verbo
             # error on) attempts to set the same arg twice.
             child_ns.pass_all_args = True
 
+        child_ns.filename = context.filename  # evaluate substitutions w.r.t. parent filename
         for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
             tag_name = t.tagName.lower()
             if tag_name == 'env':
@@ -621,6 +622,7 @@ def _include_tag(self, tag, context, ros_config, default_machine, is_core, verbo
                 self._arg_tag(t, child_ns, ros_config, verbose=verbose)
             else:
                 print("WARN: unrecognized '%s' tag in <%s> tag"%(t.tagName, tag.tagName), file=sys.stderr)
+        child_ns.filename = inc_filename  # restore filename
 
         # setup arg passing
         loader.process_include_args(child_ns)


Test patch:
diff --git a/tools/roslaunch/test/unit/test_xmlloader.py b/tools/roslaunch/test/unit/test_xmlloader.py
--- a/tools/roslaunch/test/unit/test_xmlloader.py
+++ b/tools/roslaunch/test/unit/test_xmlloader.py
@@ -1080,5 +1080,7 @@ def test_dirname(self):
         for p in mock.params:
             param_d[p.key] = p.value
 
-        self.assertEquals(param_d['/foo'], self.xml_dir + '/bar')
-        self.assertEquals(param_d['/bar'], self.xml_dir + '/test-dirname/baz')
+        self.assertEqual(param_d['/base'], self.xml_dir)
+        self.assertEqual(param_d['/foo'], self.xml_dir + '/bar')
+        self.assertEqual(param_d['/baz'], self.xml_dir + '/baz')
+        self.assertEqual(param_d['/bar'], self.xml_dir + '/test-dirname/baz')
diff --git a/tools/roslaunch/test/xml/test-dirname.xml b/tools/roslaunch/test/xml/test-dirname.xml
--- a/tools/roslaunch/test/xml/test-dirname.xml
+++ b/tools/roslaunch/test/xml/test-dirname.xml
@@ -1,4 +1,7 @@
 <launch>
+  <arg name="base" value="$(dirname)" />
   <param name="foo" value="$(dirname)/bar" />
-  <include file="$(dirname)/test-dirname/included.xml" />
+  <include file="$(dirname)/test-dirname/included.xml" pass_all_args="True">
+    <arg name="baz" value="$(dirname)/baz" />
+  </include>
 </launch>
diff --git a/tools/roslaunch/test/xml/test-dirname/included.xml b/tools/roslaunch/test/xml/test-dirname/included.xml
--- a/tools/roslaunch/test/xml/test-dirname/included.xml
+++ b/tools/roslaunch/test/xml/test-dirname/included.xml
@@ -1,3 +1,6 @@
 <launch>
+  <arg name="baz" />
+  <param name="base" value="$(arg base)" />
   <param name="bar" value="$(dirname)/baz" />
+  <param name="baz" value="$(arg baz)" />
 </launch>