Merge "Add a --copy-out flag for build.rs output files."
diff --git a/scripts/cargo2android.py b/scripts/cargo2android.py
index 8004d2b..99f8976 100755
--- a/scripts/cargo2android.py
+++ b/scripts/cargo2android.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2019 The Android Open Source Project
 #
@@ -49,10 +49,12 @@
 from __future__ import print_function
 
 import argparse
+import glob
 import os
 import os.path
 import platform
 import re
+import shutil
 import sys
 
 # Some Rust packages include extra unwanted crates.
@@ -593,6 +595,17 @@
       return name3
     return self.runner.claim_module_name(name1, self, 0)
 
+  def dump_srcs_list(self):
+    """Dump the srcs list, for defaults or regular modules."""
+    if len(self.srcs) > 1:
+      srcs = sorted(set(self.srcs))  # make a copy and dedup
+    else:
+      srcs = [self.main_src]
+    copy_out = self.runner.copy_out_module_name()
+    if copy_out:
+      srcs.append(':' + copy_out)
+    self.dump_android_property_list('srcs', '"%s"', srcs)
+
   def dump_defaults_module(self):
     """Dump a rust_defaults module to be shared by other modules."""
     name = self.build_default_name()
@@ -606,7 +619,7 @@
       self.default_srcs = True
       if self.has_warning and not self.cap_lints:
         self.write('    // has rustc warnings')
-      self.write('    srcs: ["' + self.main_src + '"],')
+      self.dump_srcs_list()
     if 'test' in self.crate_types:
       self.write('    test_suites: ["general-tests"],')
       self.write('    auto_gen_config: true,')
@@ -769,10 +782,15 @@
       self.write('        ' + (fmt % escape_quotes(v)) + ',')
 
   def dump_android_property_list(self, name, fmt, values):
-    if values:
+    if not values:
+      return
+    if len(values) > 1:
       self.write('    ' + name + ': [')
       self.dump_android_property_list_items(fmt, values)
       self.write('    ],')
+    else:
+      self.write('    ' + name + ': [' +
+                 (fmt % escape_quotes(values[0])) + '],')
 
   def dump_android_core_properties(self):
     """Dump the module header, name, stem, etc."""
@@ -790,11 +808,8 @@
       self.write('    host_supported: true,')
     if not self.defaults:
       self.write('    crate_name: "' + self.crate_name + '",')
-    if len(self.srcs) > 1:
-      self.srcs = sorted(set(self.srcs))
-      self.dump_android_property_list('srcs', '"%s"', self.srcs)
-    elif not self.default_srcs:
-      self.write('    srcs: ["' + self.main_src + '"],')
+    if not self.default_srcs:
+      self.dump_srcs_list()
     if 'test' in self.crate_types and not self.defaults:
       # self.root_pkg can have multiple test modules, with different *_tests[n]
       # names, but their executables can all be installed under the same _tests
@@ -1021,6 +1036,8 @@
     self.dry_run = not args.run
     self.skip_cargo = args.skipcargo
     self.cargo_path = './cargo'  # path to cargo, will be set later
+    self.checked_out_files = False  # to check only once
+    self.build_out_files = []  # output files generated by build.rs
     # All cc/ar objects, crates, dependencies, and warning files
     self.cc_objects = list()
     self.pkg_obj2cc = {}
@@ -1099,11 +1116,56 @@
         rust_version = version
     return '.'.join(rust_version)
 
+  def copy_out_files(self):
+    """Copy build.rs output files to ./out and set up build_out_files."""
+    if not self.args.copy_out or self.checked_out_files:
+      return
+    self.checked_out_files = True
+    # list1 has build.rs output for normal crates
+    list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
+    # list2 has build.rs output for proc-macro crates
+    list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
+    out_files = set()
+    if list1 or list2:
+      os.makedirs('out', exist_ok=True)
+    for path in (list1 + list2):
+      file_name = path.split('/')[-1]
+      out_files.add(file_name)
+      shutil.copy(path, 'out/' + file_name)
+    self.build_out_files = sorted(out_files)
+
+  def copy_out_module_name(self):
+    if self.args.copy_out and self.build_out_files:
+      return 'copy_' + self.root_pkg + '_build_out'
+    else:
+      return ''
+
+  def dump_copy_out_module(self, outf):
+    """Output the genrule module to copy out/* to $(genDir)."""
+    copy_out = self.copy_out_module_name()
+    if not copy_out:
+      return
+    outf.write('\ngenrule {\n')
+    outf.write('    name: "' + copy_out + '",\n')
+    outf.write('    srcs: ["out/*"],\n')
+    outf.write('    cmd: "cp $(in) $(genDir)",\n')
+    if len(self.build_out_files) > 1:
+      outf.write('    out: [\n')
+      for f in self.build_out_files:
+        outf.write('        "' + f + '",\n')
+      outf.write('    ],\n')
+    else:
+      outf.write('    out: ["' + self.build_out_files[0] + '"],\n')
+    outf.write('}\n')
+
   def init_bp_file(self, name):
+    # name could be Android.bp or sub_dir_path/Android.bp
     if name not in self.bp_files:
       self.bp_files.add(name)
       with open(name, 'w') as outf:
         outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
+        # at most one copy_out module per .bp file
+        self.dump_copy_out_module(outf)
 
   def dump_test_mapping_files(self):
     """Dump all TEST_MAPPING files."""
@@ -1248,6 +1310,7 @@
       print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
     elif os.path.exists(CARGO_OUT):
       self.find_root_pkg()
+      self.copy_out_files()
       with open(CARGO_OUT, 'r') as cargo_out:
         self.parse(cargo_out, 'Android.bp')
         self.crates.sort(key=get_module_name)
@@ -1402,6 +1465,14 @@
       type=str,
       help='use cargo in the cargo_bin directory instead of the prebuilt one')
   parser.add_argument(
+      '--copy-out',
+      action='store_true',
+      default=False,
+      help=('only for root directory, ' +
+            'copy build.rs output to ./out/* and add a genrule to copy ' +
+            './out/* to genrule output; for crates with code pattern: ' +
+            'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
+  parser.add_argument(
       '--debug',
       action='store_true',
       default=False,