Collect modules' info to create IDE project file.

- Register a singleton and implement GenerateBuildActions func in java/jdeps.go.
- Declare a interface and a struct to collect info in android/module.go.
- Implement IDEInfo for Library & Import module in java/jdeps.go.
- Implement IDEInfo for Genrule module in genrule/genrule.go.
- Implement IDEInfo for fileGroup module in android/filegroup.go.
- Test codes for jdeps.go in java/jdeps_test.go.

Bug: 111044346

Test: export SOONG_COLLECT_JAVA_DEPS=1;mmm packages/apps/Settings
      out/soong/module_bp_java_deps.json will be generated

Change-Id: If61da77b4d7614c2c5da438b6af4c725ceccc5c3
diff --git a/Android.bp b/Android.bp
index dcbc030..ac50166 100644
--- a/Android.bp
+++ b/Android.bp
@@ -235,6 +235,7 @@
         "java/genrule.go",
         "java/jacoco.go",
         "java/java.go",
+        "java/jdeps.go",
         "java/java_resources.go",
         "java/prebuilt_apis.go",
         "java/proto.go",
@@ -245,6 +246,7 @@
     testSrcs: [
         "java/app_test.go",
         "java/java_test.go",
+        "java/jdeps_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/android/module.go b/android/module.go
index ae12274..4dc4e9c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1547,3 +1547,27 @@
 	}
 }
 func (s AndroidModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
+
+// Collect information for opening IDE project files in java/jdeps.go.
+type IDEInfo interface {
+	IDEInfo(ideInfo *IdeInfo)
+	BaseModuleName() string
+}
+
+// Extract the base module name from the Import name.
+// Often the Import name has a prefix "prebuilt_".
+// Remove the prefix explicitly if needed
+// until we find a better solution to get the Import name.
+type IDECustomizedModuleName interface {
+	IDECustomizedModuleName() string
+}
+
+type IdeInfo struct {
+	Deps              []string `json:"dependencies,omitempty"`
+	Srcs              []string `json:"srcs,omitempty"`
+	Aidl_include_dirs []string `json:"aidl_include_dirs,omitempty"`
+	Jarjar_rules      []string `json:"jarjar_rules,omitempty"`
+	Jars              []string `json:"jars,omitempty"`
+	Classes           []string `json:"class,omitempty"`
+	Installed_paths   []string `json:"installed,omitempty"`
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8fedc60..e3823c5 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -344,6 +344,17 @@
 	g.outputDeps = append(g.outputDeps, task.out[0])
 }
 
+// Collect information for opening IDE project files in java/jdeps.go.
+func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
+	for _, src := range g.properties.Srcs {
+		if strings.HasPrefix(src, ":") {
+			src = strings.Trim(src, ":")
+			dpInfo.Deps = append(dpInfo.Deps, src)
+		}
+	}
+}
+
 func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
 	module := &Module{
 		taskGenerator: taskGenerator,
diff --git a/java/java.go b/java/java.go
index 4bf5880..ae7a6e0 100644
--- a/java/java.go
+++ b/java/java.go
@@ -311,6 +311,10 @@
 
 	// list of SDK lib names that this java moudule is exporting
 	exportedSdkLibs []string
+
+	// list of source files, collected from compiledJavaSrcs and compiledSrcJars
+	// filter out Exclude_srcs, will be used by android.IDEInfo struct
+	expandIDEInfoCompiledSrcs []string
 }
 
 func (j *Module) Srcs() android.Paths {
@@ -1010,6 +1014,10 @@
 	srcJars = append(srcJars, deps.srcJars...)
 	srcJars = append(srcJars, extraSrcJars...)
 
+	// Collect source files from compiledJavaSrcs, compiledSrcJars and filter out Exclude_srcs
+	// that IDEInfo struct will use
+	j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.Strings()...)
+
 	jarName := ctx.ModuleName() + ".jar"
 
 	javaSrcFiles := srcFiles.FilterByExt(".java")
@@ -1362,6 +1370,23 @@
 	return j.logtagsSrcs
 }
 
+// Collect information for opening IDE project files in java/jdeps.go.
+func (j *Module) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
+	dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
+	dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
+	if j.properties.Jarjar_rules != nil {
+		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, *j.properties.Jarjar_rules)
+	}
+}
+
+func (j *Module) CompilerDeps() []string {
+	jdeps := []string{}
+	jdeps = append(jdeps, j.properties.Libs...)
+	jdeps = append(jdeps, j.properties.Static_libs...)
+	return jdeps
+}
+
 //
 // Java libraries (.jar file)
 //
@@ -1691,6 +1716,26 @@
 	return j.exportedSdkLibs
 }
 
+// Collect information for opening IDE project files in java/jdeps.go.
+const (
+	removedPrefix = "prebuilt_"
+)
+
+func (j *Import) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...)
+}
+
+func (j *Import) IDECustomizedModuleName() string {
+	// TODO(b/113562217): Extract the base module name from the Import name, often the Import name
+	// has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
+	// solution to get the Import name.
+	name := j.Name()
+	if strings.HasPrefix(name, removedPrefix) {
+		name = strings.Trim(name, removedPrefix)
+	}
+	return name
+}
+
 var _ android.PrebuiltInterface = (*Import)(nil)
 
 func ImportFactory() android.Module {
diff --git a/java/jdeps.go b/java/jdeps.go
new file mode 100644
index 0000000..c7fa42a
--- /dev/null
+++ b/java/jdeps.go
@@ -0,0 +1,109 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// 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.
+
+package java
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+
+	"android/soong/android"
+)
+
+// This singleton generates android java dependency into to a json file. It does so for each
+// blueprint Android.bp resulting in a java.Module when either make, mm, mma, mmm or mmma is
+// called. Dependency info file is generated in $OUT/module_bp_java_depend.json.
+
+func init() {
+	android.RegisterSingletonType("jdeps_generator", jDepsGeneratorSingleton)
+}
+
+func jDepsGeneratorSingleton() android.Singleton {
+	return &jdepsGeneratorSingleton{}
+}
+
+type jdepsGeneratorSingleton struct {
+}
+
+const (
+	// Environment variables used to modify behavior of this singleton.
+	envVariableCollectJavaDeps = "SOONG_COLLECT_JAVA_DEPS"
+	jdepsJsonFileName          = "module_bp_java_deps.json"
+)
+
+func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !ctx.Config().IsEnvTrue(envVariableCollectJavaDeps) {
+		return
+	}
+
+	moduleInfos := make(map[string]android.IdeInfo)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		ideInfoProvider, ok := module.(android.IDEInfo)
+		if !ok {
+			return
+		}
+		name := ideInfoProvider.BaseModuleName()
+		ideModuleNameProvider, ok := module.(android.IDECustomizedModuleName)
+		if ok {
+			name = ideModuleNameProvider.IDECustomizedModuleName()
+		}
+
+		dpInfo := moduleInfos[name]
+		ideInfoProvider.IDEInfo(&dpInfo)
+		dpInfo.Deps = android.FirstUniqueStrings(dpInfo.Deps)
+		dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
+		dpInfo.Aidl_include_dirs = android.FirstUniqueStrings(dpInfo.Aidl_include_dirs)
+		dpInfo.Jarjar_rules = android.FirstUniqueStrings(dpInfo.Jarjar_rules)
+		dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
+		moduleInfos[name] = dpInfo
+
+		mkProvider, ok := module.(android.AndroidMkDataProvider)
+		if !ok {
+			return
+		}
+		data := mkProvider.AndroidMk()
+		if data.Class != "" {
+			dpInfo.Classes = append(dpInfo.Classes, data.Class)
+		}
+		out := data.OutputFile.String()
+		if out != "" {
+			dpInfo.Installed_paths = append(dpInfo.Installed_paths, out)
+		}
+		dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes)
+		dpInfo.Installed_paths = android.FirstUniqueStrings(dpInfo.Installed_paths)
+		moduleInfos[name] = dpInfo
+	})
+
+	jfpath := android.PathForOutput(ctx, jdepsJsonFileName).String()
+	err := createJsonFile(moduleInfos, jfpath)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+}
+
+func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath string) error {
+	file, err := os.Create(jfpath)
+	if err != nil {
+		return fmt.Errorf("Failed to create file: %s, relative: %v", jdepsJsonFileName, err)
+	}
+	defer file.Close()
+	buf, err := json.MarshalIndent(moduleInfos, "", "\t")
+	if err != nil {
+		return fmt.Errorf("Write file failed: %s, relative: %v", jdepsJsonFileName, err)
+	}
+	fmt.Fprintf(file, string(buf))
+	return nil
+}
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
new file mode 100644
index 0000000..ca8a3cd
--- /dev/null
+++ b/java/jdeps_test.go
@@ -0,0 +1,87 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// 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.
+
+package java
+
+import (
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestCollectJavaLibraryPropertiesAddLibsDeps(t *testing.T) {
+	expected := []string{"Foo", "Bar"}
+	module := LibraryFactory().(*Library)
+	module.properties.Libs = append(module.properties.Libs, expected...)
+	dpInfo := &android.IdeInfo{}
+
+	module.IDEInfo(dpInfo)
+
+	if !reflect.DeepEqual(dpInfo.Deps, expected) {
+		t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected)
+	}
+}
+
+func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) {
+	expected := []string{"Foo", "Bar"}
+	module := LibraryFactory().(*Library)
+	module.properties.Static_libs = append(module.properties.Static_libs, expected...)
+	dpInfo := &android.IdeInfo{}
+
+	module.IDEInfo(dpInfo)
+
+	if !reflect.DeepEqual(dpInfo.Deps, expected) {
+		t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected)
+	}
+}
+
+func TestCollectJavaLibraryPropertiesAddScrs(t *testing.T) {
+	expected := []string{"Foo", "Bar"}
+	module := LibraryFactory().(*Library)
+	module.expandIDEInfoCompiledSrcs = append(module.expandIDEInfoCompiledSrcs, expected...)
+	dpInfo := &android.IdeInfo{}
+
+	module.IDEInfo(dpInfo)
+
+	if !reflect.DeepEqual(dpInfo.Srcs, expected) {
+		t.Errorf("Library.IDEInfo() Srcs = %v, want %v", dpInfo.Srcs, expected)
+	}
+}
+
+func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) {
+	expected := []string{"Foo", "Bar"}
+	module := LibraryFactory().(*Library)
+	module.deviceProperties.Aidl.Include_dirs = append(module.deviceProperties.Aidl.Include_dirs, expected...)
+	dpInfo := &android.IdeInfo{}
+
+	module.IDEInfo(dpInfo)
+
+	if !reflect.DeepEqual(dpInfo.Aidl_include_dirs, expected) {
+		t.Errorf("Library.IDEInfo() Aidl_include_dirs = %v, want %v", dpInfo.Aidl_include_dirs, expected)
+	}
+}
+
+func TestCollectJavaLibraryPropertiesAddJarjarRules(t *testing.T) {
+	expected := "Jarjar_rules.txt"
+	module := LibraryFactory().(*Library)
+	module.properties.Jarjar_rules = &expected
+	dpInfo := &android.IdeInfo{}
+
+	module.IDEInfo(dpInfo)
+
+	if dpInfo.Jarjar_rules[0] != expected {
+		t.Errorf("Library.IDEInfo() Jarjar_rules = %v, want %v", dpInfo.Jarjar_rules[0], expected)
+	}
+}