Improve BUILDDIR handling with symlinks

If BUILDDIR is a local symlink to another directory in the same parent
directory (out -> out.angler), then using out and .. as relative paths
to get back and forth work.

But if BUILDDIR is a symlink to another directory altogether (out ->
/mnt/sdd/out.master), then we shouldn't be relying on relative paths (so
that the source directory can still be moved).

Change-Id: I946c8116090410ab2b935eafba9b6e96f5f2f1dd
diff --git a/bootstrap.bash b/bootstrap.bash
index c4c7e35..62c429c 100755
--- a/bootstrap.bash
+++ b/bootstrap.bash
@@ -8,11 +8,7 @@
     echo "error: To use BUILDDIR, run from the source directory"
     exit 1
   fi
-  if [[ ${ORIG_SRCDIR:0:1} == '/' ]]; then
-    export BUILDDIR=$PWD
-  else
-    export BUILDDIR=$(python -c "import os; print os.path.relpath('.', '$ORIG_SRCDIR')")
-  fi
+  export BUILDDIR=$("${ORIG_SRCDIR}/build/soong/reverse_path.py" "$ORIG_SRCDIR")
   cd $ORIG_SRCDIR
 fi
 if [[ -z "$BUILDDIR" ]]; then
@@ -49,11 +45,7 @@
       exit 1
     fi
 
-    if [[ ${BUILDDIR:0:1} == '/' ]]; then
-      export SRCDIR_FROM_BUILDDIR=$PWD
-    else
-      export SRCDIR_FROM_BUILDDIR=$(python -c "import os; print os.path.relpath('.', '$BUILDDIR')")
-    fi
+    export SRCDIR_FROM_BUILDDIR=$(build/soong/reverse_path.py "$BUILDDIR")
 
     sed -e "s|@@BuildDir@@|${BUILDDIR}|" \
         -e "s|@@SrcDirFromBuildDir@@|${SRCDIR_FROM_BUILDDIR}|" \
diff --git a/reverse_path.py b/reverse_path.py
new file mode 100755
index 0000000..7b7d621
--- /dev/null
+++ b/reverse_path.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+
+import os
+import sys
+
+# Find the best reverse path to reference the current directory from another
+# directory. We use this to find relative paths to and from the source and build
+# directories.
+#
+# If the directory is given as an absolute path, return an absolute path to the
+# current directory.
+#
+# If there's a symlink involved, and the same relative path would not work if
+# the symlink was replace with a regular directory, then return an absolute
+# path. This handles paths like out -> /mnt/ssd/out
+#
+# For symlinks that can use the same relative path (out -> out.1), just return
+# the relative path. That way out.1 can be renamed as long as the symlink is
+# updated.
+#
+# For everything else, just return the relative path. That allows the source and
+# output directories to be moved as long as they stay in the same position
+# relative to each other.
+def reverse_path(path):
+    if path.startswith("/"):
+        return os.path.abspath('.')
+
+    realpath = os.path.relpath(os.path.realpath('.'), os.path.realpath(path))
+    relpath = os.path.relpath('.', path)
+
+    if realpath != relpath:
+        return os.path.abspath('.')
+
+    return relpath
+
+
+if __name__ == '__main__':
+    print(reverse_path(sys.argv[1]))
diff --git a/reverse_path_test.py b/reverse_path_test.py
new file mode 100644
index 0000000..c5bb8e6
--- /dev/null
+++ b/reverse_path_test.py
@@ -0,0 +1,45 @@
+from __future__ import print_function
+
+import os
+import shutil
+import tempfile
+import unittest
+
+from reverse_path import reverse_path
+
+class TestReversePath(unittest.TestCase):
+    def setUp(self):
+        self.tmpdir = tempfile.mkdtemp()
+        os.chdir(self.tmpdir)
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir)
+
+    def test_absolute(self):
+        self.assertEqual(self.tmpdir, reverse_path('/out'))
+
+    def test_relative(self):
+        os.mkdir('a')
+        os.mkdir('b')
+
+        self.assertEqual('..', reverse_path('a'))
+
+        os.chdir('a')
+        self.assertEqual('a', reverse_path('..'))
+        self.assertEqual('.', reverse_path('../a'))
+        self.assertEqual('../a', reverse_path('../b'))
+
+    def test_symlink(self):
+        os.mkdir('b')
+        os.symlink('b', 'a')
+        os.mkdir('b/d')
+        os.symlink('b/d', 'c')
+
+        self.assertEqual('..', reverse_path('a'))
+        self.assertEqual('..', reverse_path('b'))
+        self.assertEqual(self.tmpdir, reverse_path('c'))
+        self.assertEqual('../..', reverse_path('b/d'))
+
+
+if __name__ == '__main__':
+    unittest.main()