Merge "Identify modules ready to be converted to Soong"
diff --git a/core/Makefile b/core/Makefile
index fa787b5..f9de658 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -395,6 +395,22 @@
 $(call dist-for-goals,droidcore,$(BUILD_SYSTEM_STATS))
 
 # -----------------------------------------------------------------
+# Modules ready to be converted to Soong, ordered by how many
+# modules depend on them.
+SOONG_CONV := $(sort $(SOONG_CONV))
+SOONG_CONV_DATA := $(call intermediates-dir-for,PACKAGING,soong_conversion)/soong_conv_data
+$(SOONG_CONV_DATA):
+	@rm -f $@
+	@$(foreach s,$(SOONG_CONV),echo "$(s),$(sort $(SOONG_CONV.$(s).PROBLEMS)),$(sort $(filter-out $(SOONG_ALREADY_CONV),$(SOONG_CONV.$(s).DEPS)))" >>$@;)
+
+SOONG_TO_CONVERT_SCRIPT := build/tools/soong_to_convert.py
+SOONG_TO_CONVERT := $(PRODUCT_OUT)/soong_to_convert.txt
+$(SOONG_TO_CONVERT): $(SOONG_CONV_DATA) $(SOONG_TO_CONVERT_SCRIPT)
+	@rm -f $@
+	$(hide) $(SOONG_TO_CONVERT_SCRIPT) $< >$@
+$(call dist-for-goals,droidcore,$(SOONG_TO_CONVERT))
+
+# -----------------------------------------------------------------
 # The dev key is used to sign this package, and as the key required
 # for future OTA packages installed by this system.  Actual product
 # deliverables will be re-signed by hand.  We expect this file to
diff --git a/core/binary.mk b/core/binary.mk
index dc9fa4e..fb31890 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -30,6 +30,8 @@
   endif
 endif
 
+my_soong_problems :=
+
 # The following LOCAL_ variables will be modified in this file.
 # Because the same LOCAL_ variables may be used to define modules for both 1st arch and 2nd arch,
 # we can't modify them in place.
@@ -416,6 +418,16 @@
 endif
 endif
 
+ifneq ($(filter ../%,$(my_src_files)),)
+my_soong_problems += dotdot_srcs
+endif
+ifneq ($(foreach i,$(my_c_includes),$(filter %/..,$(i))$(findstring /../,$(i))),)
+my_soong_problems += dotdot_incs
+endif
+ifneq ($(filter %.arm,$(my_src_files)),)
+my_soong_problems += srcs_dotarm
+endif
+
 ####################################################
 ## Add FDO flags if FDO is turned on and supported
 ## Please note that we will do option filtering during FDO build.
@@ -698,6 +710,7 @@
 renderscript_sources := $(filter %.rs %.fs,$(my_src_files))
 
 ifneq (,$(renderscript_sources))
+my_soong_problems += rs
 
 renderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources))
 RenderScript_file_stamp := $(intermediates)/RenderScriptCPP.stamp
@@ -777,6 +790,7 @@
 ###########################################################
 proto_sources := $(filter %.proto,$(my_src_files))
 ifneq ($(proto_sources),)
+my_soong_problems += proto
 proto_gen_dir := $(generated_sources_dir)/proto
 proto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources))
 
@@ -859,6 +873,7 @@
 dbus_definitions := $(filter %.dbus-xml,$(my_src_files))
 dbus_generated_headers :=
 ifneq ($(dbus_definitions),)
+my_soong_problems += dbus
 
 dbus_definition_paths := $(addprefix $(LOCAL_PATH)/,$(dbus_definitions))
 dbus_service_config := $(filter %dbus-service-config.json,$(my_src_files))
@@ -914,6 +929,7 @@
 aidl_src := $(strip $(filter %.aidl,$(my_src_files)))
 aidl_gen_cpp :=
 ifneq ($(aidl_src),)
+my_soong_problems += aidl
 
 # Use the intermediates directory to avoid writing our own .cpp -> .o rules.
 aidl_gen_cpp_root := $(intermediates)/aidl-generated/src
@@ -946,6 +962,7 @@
 vts_src := $(strip $(filter %.vts,$(my_src_files)))
 vts_gen_cpp :=
 ifneq ($(vts_src),)
+my_soong_problems += vts
 
 # Use the intermediates directory to avoid writing our own .cpp -> .o rules.
 vts_gen_cpp_root := $(intermediates)/vts-generated/src
@@ -1208,6 +1225,7 @@
 $(call track-src-file-obj,$(objc_sources),$(objc_objects))
 
 ifneq ($(strip $(objc_objects)),)
+my_soong_problems += objc
 $(objc_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.m \
     $(my_additional_dependencies)
 	$(transform-$(PRIVATE_HOST)m-to-o)
@@ -1223,6 +1241,7 @@
 $(call track-src-file-obj,$(objcpp_sources),$(objcpp_objects))
 
 ifneq ($(strip $(objcpp_objects)),)
+my_soong_problems += objc
 $(objcpp_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.mm \
     $(my_additional_dependencies)
 	$(transform-$(PRIVATE_HOST)mm-to-o)
@@ -1737,3 +1756,15 @@
 
 # Make sure export_includes gets generated when you are running mm/mmm
 $(LOCAL_BUILT_MODULE) : | $(export_includes) $(my_link_type)
+
+ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
+SOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \
+    $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems)
+SOONG_CONV.$(LOCAL_MODULE).DEPS := \
+    $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \
+    $(my_static_libraries) \
+    $(my_whole_static_libraries) \
+    $(my_shared_libraries) \
+    $(my_system_shared_libraries)
+SOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE)
+endif
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index ea4e66b..997b9be 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -79,6 +79,12 @@
   prebuilt_module_is_a_library :=
 endif
 
+ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
+ifeq ($(prebuilt_module_is_a_library),true)
+SOONG_ALREADY_CONV := $(SOONG_ALREADY_CONV) $(LOCAL_MODULE)
+endif
+endif
+
 # Don't install static libraries by default.
 ifndef LOCAL_UNINSTALLABLE_MODULE
 ifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS))
diff --git a/tools/soong_to_convert.py b/tools/soong_to_convert.py
new file mode 100755
index 0000000..379a1ad
--- /dev/null
+++ b/tools/soong_to_convert.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# 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.
+
+"""Tool to prioritize which modules to convert to Soong.
+
+Generally, you'd use this through the make integration, which automatically
+generates the CSV input file that this tool expects:
+
+  $ m $OUT/soong_to_convert.txt
+  $ less $OUT/soong_to_convert.txt
+
+The output is a list of modules that are probably ready to convert to Soong:
+
+  # Blocked on Module (potential problems)
+           283 libEGL (srcs_dotarm)
+           246 libicuuc (dotdot_incs dotdot_srcs)
+           221 libspeexresampler
+           215 libcamera_metadata
+               ...
+             0 zram-perf (dotdot_incs)
+
+The number at the beginning of the line shows how many native modules depend
+on that module.
+
+All of their dependencies have been satisfied, and any potential problems
+that Make can detect are listed in parenthesis after the module:
+
+  dotdot_srcs: LOCAL_SRC_FILES contains paths outside $(LOCAL_PATH)
+  dotdot_incs: LOCAL_C_INCLUDES contains paths include '..'
+  srcs_dotarm: LOCAL_SRC_FILES contains source files like <...>.c.arm
+  aidl: LOCAL_SRC_FILES contains .aidl sources
+  dbus: LOCAL_SRC_FILES contains .dbus-xml sources
+  objc: LOCAL_SRC_FILES contains Objective-C sources
+  proto: LOCAL_SRC_FILES contains .proto sources
+  rs: LOCAL_SRC_FILES contains renderscript sources
+  vts: LOCAL_SRC_FILES contains .vts sources
+
+Not all problems can be discovered, but this is a starting point.
+
+"""
+
+from __future__ import print_function
+
+import csv
+import sys
+
+def count_deps(depsdb, module, seen):
+    """Based on the depsdb, count the number of transitive dependencies.
+
+    You can pass in an reversed dependency graph to conut the number of
+    modules that depend on the module."""
+    count = 0
+    seen.append(module)
+    if module in depsdb:
+        for dep in depsdb[module]:
+            if dep in seen:
+                continue
+            count += 1 + count_deps(depsdb, dep, seen)
+    return count
+
+def process(reader):
+    """Read the input file and produce a list of modules ready to move to Soong
+    """
+    problems = dict()
+    deps = dict()
+    reverse_deps = dict()
+
+    for (module, problem, dependencies) in reader:
+        problems[module] = problem
+        deps[module] = [d for d in dependencies.strip().split(' ') if d != ""]
+        for dep in deps[module]:
+            if not dep in reverse_deps:
+                reverse_deps[dep] = []
+            reverse_deps[dep].append(module)
+
+    results = []
+    for module in problems:
+        # Only display actionable conversions, ones without missing dependencies
+        if len(deps[module]) != 0:
+            continue
+
+        extra = ""
+        if len(problems[module]) > 0:
+            extra = " ({})".format(problems[module])
+        results.append((count_deps(reverse_deps, module, []), module + extra))
+
+    return sorted(results, key=lambda result: (-result[0], result[1]))
+
+def display(results):
+    """Displays the results"""
+    count_header = "# Blocked on"
+    count_width = len(count_header)
+    print("{} Module (potential problems)".format(count_header))
+    for (count, module) in results:
+        print("{:>{}} {}".format(count, count_width, module))
+
+def main(filename):
+    """Read the CSV file, print the results"""
+    with open(filename, 'rb') as csvfile:
+        results = process(csv.reader(csvfile))
+
+    display(results)
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        print("usage: soong_conversion.py <file>", file=sys.stderr)
+        sys.exit(1)
+
+    main(sys.argv[1])