cr-os: normalize out/ directories

The descriptions we're emitting here include the name of the out/
directory that they reside in. Exposing this implementation detail is
pretty awkward in general, so let's not do that. :)

BUG=chromium:908661
TEST=unittests; ran locally

Change-Id: Ib5a1cc66936fb6ab8677f64cd3e07a5e40141154
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2165511
Reviewed-by: Tiancong Wang <tcwang@google.com>
Tested-by: George Burgess <gbiv@chromium.org>
diff --git a/cwp/cr-os/fetch_gn_descs.py b/cwp/cr-os/fetch_gn_descs.py
index 5cfda2d..60d331c 100755
--- a/cwp/cr-os/fetch_gn_descs.py
+++ b/cwp/cr-os/fetch_gn_descs.py
@@ -31,6 +31,17 @@
 import tempfile
 
 
+def _find_chromium_root(search_from):
+  """Finds the chromium root directory from `search_from`."""
+  current = search_from
+  while current != '/':
+    if os.path.isfile(os.path.join(current, '.gclient')):
+      return current
+    current = os.path.dirname(current)
+  raise ValueError(
+      "%s doesn't appear to be a Chromium subdirectory" % search_from)
+
+
 def _create_gn_args_for(arch):
   """Creates a `gn args` listing for the given architecture."""
   # FIXME(gbiv): is_chromeos_device = True would be nice to support, as well.
@@ -90,18 +101,28 @@
   return result
 
 
-def _fix_result(gn_desc):
+def _fix_result(rename_out, out_dir, chromium_root, gn_desc):
   """Performs postprocessing on `gn desc` JSON."""
   result = {}
 
+  rel_out = '//' + os.path.relpath(out_dir, os.path.join(chromium_root, 'src'))
+  rename_out = rename_out if rename_out.endswith('/') else rename_out + '/'
+
+  def fix_source_file(f):
+    if not f.startswith(rel_out):
+      return f
+    return rename_out + f[len(rel_out) + 1:]
+
   for target, info in gn_desc.items():
     sources = info.get('sources')
     configs = info.get('configs')
-    if sources and configs:
-      result[target] = {
-          'configs': configs,
-          'sources': sources,
-      }
+    if not sources or not configs:
+      continue
+
+    result[target] = {
+        'configs': configs,
+        'sources': [fix_source_file(f) for f in sources],
+    }
 
   return result
 
@@ -128,6 +149,11 @@
       required=True,
       help='Chromium out/ directory for us to use. This directory will '
       'be clobbered by this script.')
+  parser.add_argument(
+      '--rename_out',
+      default='//out',
+      help='Directory to rename files in --chromium_out_dir to. '
+      'Default: %(default)s')
   opts = parser.parse_args(args)
 
   logging.basicConfig(
@@ -136,6 +162,7 @@
   )
 
   arches = opts.arch
+  rename_out = opts.rename_out
   for arch in arches:
     if arch not in known_arches:
       parser.error(
@@ -143,6 +170,7 @@
 
   results_file = os.path.realpath(opts.output)
   out_dir = os.path.realpath(opts.chromium_out_dir)
+  chromium_root = _find_chromium_root(out_dir)
 
   os.makedirs(out_dir, exist_ok=True)
   results = {}
@@ -150,6 +178,7 @@
     logging.info('Getting `gn` desc for %s...', arch)
 
     results[arch] = _fix_result(
+        rename_out, out_dir, chromium_root,
         _run_gn_desc(
             in_dir=out_dir,
             gn_args=_create_gn_args_for(arch),
diff --git a/cwp/cr-os/fetch_gn_descs_test.py b/cwp/cr-os/fetch_gn_descs_test.py
index dafa059..b6fc0ee 100755
--- a/cwp/cr-os/fetch_gn_descs_test.py
+++ b/cwp/cr-os/fetch_gn_descs_test.py
@@ -47,7 +47,33 @@
         '//interesting:b': items['//interesting:b'],
     }
 
-    self.assertDictEqual(fetch_gn_descs._fix_result(items), expected_items)
+    self.assertDictEqual(
+        fetch_gn_descs._fix_result('/', '/', '/', items), expected_items)
+
+  def test_fix_result_translates_paths_in_out_dir(self):
+    items = {
+        '//interesting:a': {
+            'sources': ['//out_dir/foo', '//out_dir'],
+            'configs': ['b'],
+        },
+    }
+
+    expected_items = {
+        '//interesting:a': {
+            'sources': ['//out_translated/foo', '//out_translated/'],
+            'configs': ['b'],
+        },
+    }
+
+    self.assertDictEqual(
+        fetch_gn_descs._fix_result(
+            rename_out='//out_translated',
+            out_dir='/chromium/src/out_dir',
+            chromium_root='/chromium',
+            gn_desc=items,
+        ),
+        expected_items,
+    )
 
   def test_gn_desc_output_parsing_skips_pre_json_warnings(self):
     gn_desc = io.StringIO('\n'.join((