From 5d45c6f6f83b6a9b360c392b9fdfdeb2b935de7d Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Wed, 15 Aug 2018 15:35:38 -0700 Subject: [PATCH] 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 --- Android.bp | 2 + android/module.go | 24 ++++++++++ genrule/genrule.go | 11 +++++ java/java.go | 45 +++++++++++++++++++ java/jdeps.go | 109 +++++++++++++++++++++++++++++++++++++++++++++ java/jdeps_test.go | 87 ++++++++++++++++++++++++++++++++++++ 6 files changed, 278 insertions(+) create mode 100644 java/jdeps.go create mode 100644 java/jdeps_test.go diff --git a/Android.bp b/Android.bp index dcbc03029..ac50166e1 100644 --- a/Android.bp +++ b/Android.bp @@ -235,6 +235,7 @@ bootstrap_go_package { "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 @@ bootstrap_go_package { 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 ae1227449..4dc4e9c32 100644 --- a/android/module.go +++ b/android/module.go @@ -1547,3 +1547,27 @@ func (s AndroidModulesByName) Less(i, j int) bool { } } 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 8fedc6073..e3823c5c1 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -344,6 +344,17 @@ func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask 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 4bf5880bc..ae7a6e02c 100644 --- a/java/java.go +++ b/java/java.go @@ -311,6 +311,10 @@ type Module struct { // 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 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars ...android.Path 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 @@ func (j *Module) logtags() android.Paths { 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 @@ func (j *Import) ExportedSdkLibs() []string { 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 000000000..c7fa42aec --- /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 000000000..ca8a3cdd5 --- /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) + } +}